public final class JsDescriptorUtils { // TODO: maybe we should use external annotations or something else. private static final Set<String> FAKE_CLASSES = ContainerUtil.immutableSet( getFqNameSafe(KotlinBuiltIns.getInstance().getAny()).asString(), getFqNameSafe(KotlinBuiltIns.getInstance().getIterable()).asString()); private JsDescriptorUtils() {} private static int valueParametersCount(@NotNull FunctionDescriptor functionDescriptor) { return functionDescriptor.getValueParameters().size(); } public static boolean hasParameters(@NotNull FunctionDescriptor functionDescriptor) { return (valueParametersCount(functionDescriptor) > 0); } public static boolean isCompareTo(@NotNull CallableDescriptor descriptor) { return descriptor.getName().equals(OperatorConventions.COMPARE_TO); } @Nullable public static ClassDescriptor findAncestorClass( @NotNull List<ClassDescriptor> superclassDescriptors) { for (ClassDescriptor descriptor : superclassDescriptors) { if (descriptor.getKind() == ClassKind.CLASS || descriptor.getKind() == ClassKind.ENUM_CLASS) { return descriptor; } } return null; } @Nullable public static ClassDescriptor getSuperclass(@NotNull ClassDescriptor classDescriptor) { return findAncestorClass(getSuperclassDescriptors(classDescriptor)); } @NotNull public static List<JetType> getSupertypesWithoutFakes(ClassDescriptor descriptor) { Collection<JetType> supertypes = descriptor.getTypeConstructor().getSupertypes(); return ContainerUtil.filter( supertypes, new Condition<JetType>() { @Override public boolean value(JetType type) { ClassDescriptor classDescriptor = getClassDescriptorForType(type); return !FAKE_CLASSES.contains(getFqNameSafe(classDescriptor).asString()); } }); } @NotNull public static DeclarationDescriptor getContainingDeclaration( @NotNull DeclarationDescriptor descriptor) { DeclarationDescriptor containing = descriptor.getContainingDeclaration(); assert containing != null : "Should be called on objects that have containing declaration."; return containing; } public static boolean isExtension(@NotNull CallableDescriptor functionDescriptor) { return (functionDescriptor.getReceiverParameter() != null); } public static boolean isOverride(@NotNull CallableMemberDescriptor descriptor) { return !descriptor.getOverriddenDescriptors().isEmpty(); } @NotNull public static ReceiverParameterDescriptor getReceiverParameterForReceiver( @NotNull ReceiverValue receiverParameter) { DeclarationDescriptor declarationDescriptor = getDeclarationDescriptorForReceiver(receiverParameter); return getReceiverParameterForDeclaration(declarationDescriptor); } @NotNull private static DeclarationDescriptor getDeclarationDescriptorForReceiver( @NotNull ReceiverValue receiverParameter) { if (receiverParameter instanceof ThisReceiver) { DeclarationDescriptor declarationDescriptor = ((ThisReceiver) receiverParameter).getDeclarationDescriptor(); return declarationDescriptor.getOriginal(); } throw new UnsupportedOperationException( "Unsupported receiver type: " + receiverParameter.getClass() + ", receiverParameter = " + receiverParameter); } public static ReceiverParameterDescriptor getReceiverParameterForDeclaration( DeclarationDescriptor declarationDescriptor) { if (declarationDescriptor instanceof ClassDescriptor) { return ((ClassDescriptor) declarationDescriptor).getThisAsReceiverParameter(); } else if (declarationDescriptor instanceof CallableMemberDescriptor) { ReceiverParameterDescriptor receiverDescriptor = ((CallableMemberDescriptor) declarationDescriptor).getReceiverParameter(); assert receiverDescriptor != null; return receiverDescriptor; } throw new UnsupportedOperationException( "Unsupported declaration type: " + declarationDescriptor.getClass() + ", declarationDescriptor = " + declarationDescriptor); } // TODO: maybe we have similar routine @Nullable public static ClassDescriptor getContainingClass(@NotNull DeclarationDescriptor descriptor) { DeclarationDescriptor containing = descriptor.getContainingDeclaration(); while (containing != null) { if (containing instanceof ClassDescriptor && !isClassObject(containing)) { return (ClassDescriptor) containing; } containing = containing.getContainingDeclaration(); } return null; } @Nullable public static FunctionDescriptor getOverriddenDescriptor( @NotNull FunctionDescriptor functionDescriptor) { Set<? extends FunctionDescriptor> overriddenDescriptors = functionDescriptor.getOverriddenDescriptors(); if (overriddenDescriptors.isEmpty()) { return null; } else { // TODO: for now translator can't deal with multiple inheritance good enough return overriddenDescriptors.iterator().next(); } } private static boolean isDefaultAccessor( @Nullable PropertyAccessorDescriptor accessorDescriptor) { return accessorDescriptor == null || accessorDescriptor.isDefault(); } public static boolean isSimpleFinalProperty(@NotNull PropertyDescriptor propertyDescriptor) { return !isExtension(propertyDescriptor) && isDefaultAccessor(propertyDescriptor.getGetter()) && isDefaultAccessor(propertyDescriptor.getSetter()) && !propertyDescriptor.getModality().isOverridable(); } public static boolean isBuiltin(@NotNull DeclarationDescriptor descriptor) { PackageFragmentDescriptor containingPackageFragment = DescriptorUtils.getParentOfType(descriptor, PackageFragmentDescriptor.class); return containingPackageFragment == KotlinBuiltIns.getInstance().getBuiltInsPackageFragment(); } @Nullable public static Name getNameIfStandardType( @NotNull JetExpression expression, @NotNull TranslationContext context) { JetType type = context.bindingContext().get(BindingContext.EXPRESSION_TYPE, expression); return type != null ? getNameIfStandardType(type) : null; } @Nullable public static Name getNameIfStandardType(@NotNull JetType type) { ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); if (descriptor != null && descriptor.getContainingDeclaration() == KotlinBuiltIns.getInstance().getBuiltInsPackageFragment()) { return descriptor.getName(); } return null; } }
/** @author peter */ public class AnnotationTargetUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.AnnotationUtil"); public static final Set<TargetType> DEFAULT_TARGETS = ContainerUtil.immutableSet( TargetType.PACKAGE, TargetType.TYPE, TargetType.ANNOTATION_TYPE, TargetType.FIELD, TargetType.METHOD, TargetType.CONSTRUCTOR, TargetType.PARAMETER, TargetType.LOCAL_VARIABLE); private static final TargetType[] PACKAGE_TARGETS = {TargetType.PACKAGE}; private static final TargetType[] TYPE_USE_TARGETS = {TargetType.TYPE_USE}; private static final TargetType[] ANNOTATION_TARGETS = { TargetType.ANNOTATION_TYPE, TargetType.TYPE, TargetType.TYPE_USE }; private static final TargetType[] TYPE_TARGETS = {TargetType.TYPE, TargetType.TYPE_USE}; private static final TargetType[] TYPE_PARAMETER_TARGETS = { TargetType.TYPE_PARAMETER, TargetType.TYPE_USE }; private static final TargetType[] CONSTRUCTOR_TARGETS = { TargetType.CONSTRUCTOR, TargetType.TYPE_USE }; private static final TargetType[] METHOD_TARGETS = {TargetType.METHOD, TargetType.TYPE_USE}; private static final TargetType[] FIELD_TARGETS = {TargetType.FIELD, TargetType.TYPE_USE}; private static final TargetType[] PARAMETER_TARGETS = {TargetType.PARAMETER, TargetType.TYPE_USE}; private static final TargetType[] LOCAL_VARIABLE_TARGETS = { TargetType.LOCAL_VARIABLE, TargetType.TYPE_USE }; @NotNull public static TargetType[] getTargetsForLocation(@Nullable PsiAnnotationOwner owner) { if (owner == null) { return TargetType.EMPTY_ARRAY; } if (owner instanceof PsiType || owner instanceof PsiTypeElement) { return TYPE_USE_TARGETS; } if (owner instanceof PsiTypeParameter) { return TYPE_PARAMETER_TARGETS; } if (owner instanceof PsiModifierList) { PsiElement element = ((PsiModifierList) owner).getParent(); if (element instanceof PsiPackageStatement) { return PACKAGE_TARGETS; } if (element instanceof PsiClass) { if (((PsiClass) element).getModifierList() != owner) { return TargetType.EMPTY_ARRAY; } if (((PsiClass) element).isAnnotationType()) { return ANNOTATION_TARGETS; } else { return TYPE_TARGETS; } } if (element instanceof PsiMethod) { if (((PsiMethod) element).isConstructor()) { return CONSTRUCTOR_TARGETS; } else { return METHOD_TARGETS; } } if (element instanceof PsiField) { return FIELD_TARGETS; } if (element instanceof PsiParameter) { // PARAMETER applies only to formal parameters (methods & lambdas) and catch parameters // see https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.6.4.1 PsiElement scope = element.getParent(); if (scope instanceof PsiForeachStatement) { return LOCAL_VARIABLE_TARGETS; } if (scope instanceof PsiParameterList && scope.getParent() instanceof PsiLambdaExpression && ((PsiParameter) element).getTypeElement() == null) { return TargetType.EMPTY_ARRAY; } return PARAMETER_TARGETS; } if (element instanceof PsiLocalVariable) { return LOCAL_VARIABLE_TARGETS; } if (element instanceof PsiReceiverParameter) { return TYPE_USE_TARGETS; } } return TargetType.EMPTY_ARRAY; } @Nullable public static Set<TargetType> extractRequiredAnnotationTargets( @Nullable PsiAnnotationMemberValue value) { if (value instanceof PsiReference) { TargetType targetType = translateTargetRef((PsiReference) value); if (targetType != null) { return Collections.singleton(targetType); } } else if (value instanceof PsiArrayInitializerMemberValue) { Set<TargetType> targets = ContainerUtil.newHashSet(); for (PsiAnnotationMemberValue initializer : ((PsiArrayInitializerMemberValue) value).getInitializers()) { if (initializer instanceof PsiReference) { TargetType targetType = translateTargetRef((PsiReference) initializer); if (targetType != null) { targets.add(targetType); } } } return targets; } return null; } @Nullable private static TargetType translateTargetRef(@NotNull PsiReference reference) { if (reference instanceof PsiJavaCodeReferenceElement) { String name = ((PsiJavaCodeReferenceElement) reference).getReferenceName(); if (name != null) { try { return TargetType.valueOf(name); } catch (IllegalArgumentException ignore) { } } } PsiElement field = reference.resolve(); if (field instanceof PsiEnumConstant) { String name = ((PsiEnumConstant) field).getName(); try { return TargetType.valueOf(name); } catch (IllegalArgumentException e) { LOG.warn("Unknown target: " + name); } } return null; } /** * Returns {@code true} if the annotation resolves to a class having {@link TargetType#TYPE_USE} * in it's targets. */ public static boolean isTypeAnnotation(@NotNull PsiAnnotation element) { return findAnnotationTarget(element, TargetType.TYPE_USE) == TargetType.TYPE_USE; } /** * From given targets, returns first where the annotation may be applied. Returns {@code null} * when the annotation is not applicable at any of the targets, or {@linkplain TargetType#UNKNOWN} * if the annotation does not resolve to a valid annotation type. */ @Nullable public static TargetType findAnnotationTarget( @NotNull PsiAnnotation annotation, @NotNull TargetType... types) { if (types.length != 0) { PsiJavaCodeReferenceElement ref = annotation.getNameReferenceElement(); if (ref != null) { PsiElement annotationType = ref.resolve(); if (annotationType instanceof PsiClass) { return findAnnotationTarget((PsiClass) annotationType, types); } } } return TargetType.UNKNOWN; } /** * From given targets, returns first where the annotation may be applied. Returns {@code null} * when the annotation is not applicable at any of the targets, or {@linkplain TargetType#UNKNOWN} * if the type is not a valid annotation (e.g. cannot be resolved). */ @Nullable public static TargetType findAnnotationTarget( @NotNull PsiClass annotationType, @NotNull TargetType... types) { if (types.length != 0) { Set<TargetType> targets = getAnnotationTargets(annotationType); if (targets != null) { for (TargetType type : types) { if (type != TargetType.UNKNOWN && targets.contains(type)) { return type; } } return null; } } return TargetType.UNKNOWN; } /** * Returns a set of targets where the given annotation may be applied, or {@code null} when the * type is not a valid annotation. */ @Nullable public static Set<TargetType> getAnnotationTargets(@NotNull PsiClass annotationType) { if (!annotationType.isAnnotationType()) return null; PsiModifierList modifierList = annotationType.getModifierList(); if (modifierList == null) return null; PsiAnnotation target = modifierList.findAnnotation(CommonClassNames.JAVA_LANG_ANNOTATION_TARGET); if (target == null) return DEFAULT_TARGETS; // if omitted it is applicable to all but Java 8 // TYPE_USE/TYPE_PARAMETERS targets return extractRequiredAnnotationTargets(target.findAttributeValue(null)); } }