From 998e5988463fe9a343db2a06a7c2e668ed1e3cde Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 30 Jul 2013 22:03:03 +0400 Subject: [PATCH] Refactor JavaConstructorResolver Extract methods, fix warnings, use JavaClass/JavaMethod instead of Psi --- .../resolver/JavaConstructorResolver.java | 155 ++++++++++-------- .../resolve/java/structure/JavaMethod.java | 10 ++ 2 files changed, 93 insertions(+), 72 deletions(-) diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaConstructorResolver.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaConstructorResolver.java index 9bce1ca9a0e..0322bf8e336 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaConstructorResolver.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaConstructorResolver.java @@ -17,7 +17,8 @@ package org.jetbrains.jet.lang.resolve.java.resolver; import com.google.common.collect.Lists; -import com.intellij.psi.*; +import com.intellij.psi.PsiArrayType; +import com.intellij.psi.PsiType; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,16 +29,19 @@ import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.DescriptorResolver; -import org.jetbrains.jet.lang.resolve.java.*; +import org.jetbrains.jet.lang.resolve.java.JavaBindingContext; +import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; +import org.jetbrains.jet.lang.resolve.java.JavaVisibilities; +import org.jetbrains.jet.lang.resolve.java.TypeVariableResolver; import org.jetbrains.jet.lang.resolve.java.kotlinSignature.AlternativeMethodSignatureData; import org.jetbrains.jet.lang.resolve.java.structure.JavaClass; import org.jetbrains.jet.lang.resolve.java.structure.JavaMethod; -import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.types.JetType; import javax.inject.Inject; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.List; import static org.jetbrains.jet.lang.resolve.java.resolver.JavaFunctionResolver.recordSamAdapter; @@ -69,83 +73,15 @@ public final class JavaConstructorResolver { @NotNull public Collection resolveConstructors(@NotNull JavaClass javaClass, @NotNull ClassDescriptor containingClass) { - PsiClass psiClass = javaClass.getPsi(); - Collection result = Lists.newArrayList(); - List typeParameters = containingClass.getTypeConstructor().getParameters(); - - TypeVariableResolver typeVariableResolver = - new TypeVariableResolver(typeParameters, containingClass, "class " + javaClass.getFqName()); - Collection constructors = javaClass.getConstructors(); if (containingClass.getKind() == ClassKind.OBJECT || containingClass.getKind() == ClassKind.CLASS_OBJECT) { result.add(DescriptorResolver.createPrimaryConstructorForObject(containingClass)); } else if (constructors.isEmpty()) { - if (trace.get(BindingContext.CONSTRUCTOR, psiClass) != null) { - result.add(trace.get(BindingContext.CONSTRUCTOR, psiClass)); - } - else { - Visibility constructorVisibility = getConstructorVisibility(containingClass); - // We need to create default constructors for classes and abstract classes. - // Example: - // class Kotlin() : Java() {} - // abstract public class Java {} - if (!javaClass.isInterface()) { - ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl( - containingClass, - Collections.emptyList(), - true); - constructorDescriptor - .initialize(typeParameters, Collections.emptyList(), constructorVisibility, - javaClass.isStatic()); - result.add(constructorDescriptor); - trace.record(BindingContext.CONSTRUCTOR, psiClass, constructorDescriptor); - } - if (javaClass.isAnnotationType()) { - // A constructor for an annotation type takes all the "methods" in the @interface as parameters - ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl( - containingClass, - Collections.emptyList(), - true); - - List valueParameters = Lists.newArrayList(); - PsiMethod[] methods = psiClass.getMethods(); - for (int i = 0; i < methods.length; i++) { - PsiMethod method = methods[i]; - if (method instanceof PsiAnnotationMethod) { - PsiAnnotationMethod annotationMethod = (PsiAnnotationMethod) method; - assert annotationMethod.getParameterList().getParameters().length == 0; - - PsiType returnType = annotationMethod.getReturnType(); - - // We take the following heuristical convention: - // if the last method of the @interface is an array, we convert it into a vararg - JetType varargElementType = null; - if (i == methods.length - 1 && (returnType instanceof PsiArrayType)) { - varargElementType = typeTransformer - .transformToType(((PsiArrayType) returnType).getComponentType(), typeVariableResolver); - } - - assert returnType != null; - valueParameters.add(new ValueParameterDescriptorImpl( - constructorDescriptor, - i, - Collections.emptyList(), - Name.identifier(method.getName()), - typeTransformer.transformToType(returnType, typeVariableResolver), - annotationMethod.getDefaultValue() != null, - varargElementType)); - } - } - - constructorDescriptor.initialize(typeParameters, valueParameters, constructorVisibility, javaClass.isStatic()); - result.add(constructorDescriptor); - trace.record(BindingContext.CONSTRUCTOR, psiClass, constructorDescriptor); - } - } + ContainerUtil.addIfNotNull(result, resolveDefaultConstructor(javaClass, containingClass)); } else { for (JavaMethod constructor : constructors) { @@ -162,6 +98,81 @@ public final class JavaConstructorResolver { return result; } + @Nullable + private ConstructorDescriptor resolveDefaultConstructor(@NotNull JavaClass javaClass, @NotNull ClassDescriptor containingClass) { + ConstructorDescriptor alreadyResolved = trace.get(BindingContext.CONSTRUCTOR, javaClass.getPsi()); + if (alreadyResolved != null) { + return alreadyResolved; + } + + boolean isAnnotation = javaClass.isAnnotationType(); + + if (javaClass.isInterface() && !isAnnotation) return null; + + ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl( + containingClass, + Collections.emptyList(), + true); + + List typeParameters = containingClass.getTypeConstructor().getParameters(); + + List valueParameters; + if (isAnnotation) { + TypeVariableResolver typeVariableResolver = + new TypeVariableResolver(typeParameters, containingClass, "class " + javaClass.getFqName()); + valueParameters = resolveAnnotationParameters(javaClass, constructorDescriptor, typeVariableResolver); + } + else { + valueParameters = Collections.emptyList(); + } + + constructorDescriptor.initialize(typeParameters, valueParameters, getConstructorVisibility(containingClass), javaClass.isStatic()); + + trace.record(BindingContext.CONSTRUCTOR, javaClass.getPsi(), constructorDescriptor); + + return constructorDescriptor; + } + + @NotNull + private List resolveAnnotationParameters( + @NotNull JavaClass javaClass, + @NotNull ConstructorDescriptor constructorDescriptor, + @NotNull TypeVariableResolver typeVariableResolver + ) { + // A constructor for an annotation type takes all the "methods" in the @interface as parameters + List result = Lists.newArrayList(); + + int index = 0; + for (Iterator iterator = javaClass.getMethods().iterator(); iterator.hasNext(); ) { + JavaMethod method = iterator.next(); + assert method.getValueParameters().isEmpty() : "Annotation method can't have parameters: " + method; + + PsiType returnType = method.getPsi().getReturnType(); + assert returnType != null : "Annotation method has no return type: " + method; + + // We take the following heuristic convention: + // if the last method of the @interface is an array, we convert it into a vararg + JetType varargElementType = null; + if (!iterator.hasNext() && returnType instanceof PsiArrayType) { + PsiType componentType = ((PsiArrayType) returnType).getComponentType(); + varargElementType = typeTransformer.transformToType(componentType, typeVariableResolver); + } + + result.add(new ValueParameterDescriptorImpl( + constructorDescriptor, + index, + Collections.emptyList(), + method.getName(), + typeTransformer.transformToType(returnType, typeVariableResolver), + method.getAnnotationParameterDefaultValue() != null, + varargElementType)); + + index++; + } + + return result; + } + @NotNull private static Visibility getConstructorVisibility(@NotNull ClassDescriptor classDescriptor) { Visibility visibility = classDescriptor.getVisibility(); diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaMethod.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaMethod.java index 6d8fc45f3c7..27baaf323bd 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaMethod.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaMethod.java @@ -16,6 +16,7 @@ package org.jetbrains.jet.lang.resolve.java.structure; +import com.intellij.psi.PsiAnnotationMethod; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; import org.jetbrains.annotations.NotNull; @@ -62,4 +63,13 @@ public class JavaMethod extends JavaMemberImpl implements JavaTypeParameterListO public Collection getValueParameters() { return valueParameters(getPsi().getParameterList().getParameters()); } + + @Nullable + public Object getAnnotationParameterDefaultValue() { + PsiMethod psiMethod = getPsi(); + if (psiMethod instanceof PsiAnnotationMethod) { + return ((PsiAnnotationMethod) psiMethod).getDefaultValue(); + } + return null; + } }