From 0798a43f7daa49e6cdca18d80bb5034cee25a5b8 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 1 Sep 2016 16:58:12 +0300 Subject: [PATCH] Use resolveImaginarySuperCallInThisPlace() instead of enumerating all super class constructors; find usages of overloaded ctr should find only correct implicit super calls --- .../impl/analysis/HighlightClassUtil.java | 39 ++++++------------ .../ConstructorReferencesSearchHelper.java | 40 ++++++++++++------- .../impl/source/resolve/JavaResolveUtil.java | 23 +++++++++++ ...ChecksForImplicitSuperConstructorCall.java | 9 ----- .../implicitVarArgsConstructorsUsage/A.java | 9 +++++ .../intellij/psi/search/FindUsagesTest.java | 12 +++++- 6 files changed, 81 insertions(+), 51 deletions(-) create mode 100644 java/java-tests/testData/psi/search/findUsages/implicitVarArgsConstructorsUsage/A.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java index c15954d1368c..6037ffa96b4a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java @@ -39,15 +39,14 @@ import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.JavaResolveUtil; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.*; import com.intellij.refactoring.util.RefactoringChangeUtil; -import com.intellij.util.containers.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.HashSet; public class HighlightClassUtil { private static final QuickFixFactory QUICK_FIX_FACTORY = QuickFixFactory.getInstance(); @@ -527,7 +526,7 @@ public class HighlightClassUtil { PsiMethod[] constructors = baseClass.getConstructors(); if (constructors.length == 0) return null; - final HighlightInfo highlightInfo = constructors.length > 1 ? checkAmbiguityOfImplicitConstructorCall(constructors, range) : null; + final HighlightInfo highlightInfo = constructors.length > 1 ? checkAmbiguityOfImplicitConstructorCall(constructors, range, aClass, baseClass) : null; if (highlightInfo != null) { return highlightInfo; } @@ -561,37 +560,25 @@ public class HighlightClassUtil { } @Nullable - private static HighlightInfo checkAmbiguityOfImplicitConstructorCall(PsiMethod[] constructors, TextRange range) { + private static HighlightInfo checkAmbiguityOfImplicitConstructorCall(@NotNull PsiMethod[] constructors, + @NotNull TextRange range, + @NotNull PsiClass subClass, + @NotNull PsiClass superClass) { + PsiElement resolved = JavaResolveUtil.resolveImaginarySuperCallInThisPlace(subClass, subClass.getProject(), superClass); + if (resolved != null) { + return null; + } + + // find two ambiguous var-args-only constructors List varargConstructors = new ArrayList<>(); for (PsiMethod constructor : constructors) { final PsiParameter[] parameters = constructor.getParameterList().getParameters(); - if (parameters.length == 0) { - varargConstructors.clear(); - break; - } if (parameters.length == 1 && parameters[0].isVarArgs()) { varargConstructors.add(constructor); + if (varargConstructors.size() == 2) break; } } - if (varargConstructors.size() <= 1) return null; - Set lessSpecific = new HashSet<>(); - final PsiType[] types = varargConstructors.stream().map(c -> c.getParameterList().getParameters()[0].getType()).toArray(PsiType[]::new); - for (int i = 1; i < types.length; i++) { - PsiType t1 = types[i]; - for (int j = 0; j < i; j++) { - PsiType t2 = types[j]; - - if (t1.isAssignableFrom(t2)) { - lessSpecific.add(varargConstructors.get(i)); - } - else if (t2.isAssignableFrom(t1)) { - lessSpecific.add(varargConstructors.get(j)); - } - } - } - varargConstructors.removeAll(lessSpecific); - if (varargConstructors.size() > 1) { final String m1 = PsiFormatUtil.formatMethod(varargConstructors.get(0), PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_CONTAINING_CLASS | diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java index 7ac842b28928..a1c32a4793f7 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java @@ -17,11 +17,11 @@ package com.intellij.psi.impl.search; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.TextRange; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.impl.light.LightMemberReference; +import com.intellij.psi.impl.source.resolve.JavaResolveUtil; import com.intellij.psi.search.PsiSearchScopeUtil; import com.intellij.psi.search.SearchRequestCollector; import com.intellij.psi.search.SearchScope; @@ -60,18 +60,15 @@ class ConstructorReferencesSearchHelper { final boolean[] isEnum = new boolean[1]; final boolean[] isUnder18 = new boolean[1]; - DumbService.getInstance(project).runReadActionInSmartMode(new Computable() { - @Override - public Void compute() { - final PsiParameter[] parameters = constructor.getParameterList().getParameters(); - constructorCanBeCalledImplicitly[0] = parameters.length == 0; - if (!constructorCanBeCalledImplicitly[0]) { - constructorCanBeCalledImplicitly[0] = parameters.length == 1 && parameters[0].isVarArgs(); - } - isEnum[0] = containingClass.isEnum(); - isUnder18[0] = PsiUtil.getLanguageLevel(containingClass).isAtLeast(LanguageLevel.JDK_1_8); - return null; + DumbService.getInstance(project).runReadActionInSmartMode(() -> { + final PsiParameter[] parameters = constructor.getParameterList().getParameters(); + constructorCanBeCalledImplicitly[0] = parameters.length == 0; + if (!constructorCanBeCalledImplicitly[0]) { + constructorCanBeCalledImplicitly[0] = parameters.length == 1 && parameters[0].isVarArgs(); } + isEnum[0] = containingClass.isEnum(); + isUnder18[0] = PsiUtil.getLanguageLevel(containingClass).isAtLeast(LanguageLevel.JDK_1_8); + return null; }); if (isEnum[0]) { @@ -228,9 +225,22 @@ class ConstructorReferencesSearchHelper { @NotNull final Project project, @NotNull final PsiClass containingClass) { if (containingClass instanceof PsiAnonymousClass) return true; - boolean same = DumbService.getInstance(project).runReadActionInSmartMode( - () -> myManager.areElementsEquivalent(constructor.getContainingClass(), containingClass.getSuperClass())); - if (!same) { + + PsiClass ctrClass = constructor.getContainingClass(); + if (ctrClass == null) return true; + + boolean isImplicitSuper = DumbService.getInstance(project).runReadActionInSmartMode( + () -> myManager.areElementsEquivalent(ctrClass, containingClass.getSuperClass())); + if (!isImplicitSuper) { + return true; + } + + PsiElement resolved = JavaResolveUtil.resolveImaginarySuperCallInThisPlace(usage, project, ctrClass); + + boolean resolvesToThisConstructor = DumbService.getInstance(project).runReadActionInSmartMode( + () -> myManager.areElementsEquivalent(constructor, resolved)); + + if (!resolvesToThisConstructor) { return true; } return processor.process(new LightMemberReference(myManager, usage, PsiSubstitutor.EMPTY) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveUtil.java index 83ca4fc4b781..e97b5d6312a4 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveUtil.java @@ -24,11 +24,17 @@ import com.intellij.openapi.projectRoots.JavaSdkVersion; import com.intellij.openapi.projectRoots.JavaVersionService; import com.intellij.psi.*; import com.intellij.psi.impl.PsiImplUtil; +import com.intellij.psi.impl.source.DummyHolder; +import com.intellij.psi.impl.source.DummyHolderFactory; +import com.intellij.psi.impl.source.tree.FileElement; +import com.intellij.psi.impl.source.tree.TreeElement; +import com.intellij.psi.impl.source.tree.java.PsiExpressionListImpl; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiTypesUtil; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -270,4 +276,21 @@ public class JavaResolveUtil { containingFile); return results.length == 0 ? JavaResolveResult.EMPTY_ARRAY : (JavaResolveResult[])results; } + + /** + * @return the constructor (or a class if there are none) + * which the "{@code super();}" no-args call resolves to if inserted in the {@code place} (typically it would be inserted in the sub class constructor) + * No code modifications happen in this method; it's used for resolving multiple overloaded constructors. + */ + public static PsiElement resolveImaginarySuperCallInThisPlace(@NotNull PsiMember place, + @NotNull Project project, + @NotNull PsiClass superClassWhichTheSuperCallMustResolveTo) { + PsiExpressionListImpl expressionList = new PsiExpressionListImpl(); + final DummyHolder result = DummyHolderFactory.createHolder(PsiManager.getInstance(project), place); + final FileElement holder = result.getTreeElement(); + holder.rawAddChildren((TreeElement)expressionList.getNode()); + + return PsiResolveHelper.SERVICE.getInstance(project) + .resolveConstructor(PsiTypesUtil.getClassType(superClassWhichTheSuperCallMustResolveTo), expressionList, place).getElement(); + } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/AmbiguityChecksForImplicitSuperConstructorCall.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/AmbiguityChecksForImplicitSuperConstructorCall.java index 5ae51344f392..dd0f008e657c 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/AmbiguityChecksForImplicitSuperConstructorCall.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/AmbiguityChecksForImplicitSuperConstructorCall.java @@ -17,12 +17,3 @@ class B1 extends A1 { } class C1 extends A1 {} -class A2 { - A2(int... i){} - A2(Object... i){} -} -class B2 extends A2 { - public B2() { - } -} -class C2 extends A2 {} \ No newline at end of file diff --git a/java/java-tests/testData/psi/search/findUsages/implicitVarArgsConstructorsUsage/A.java b/java/java-tests/testData/psi/search/findUsages/implicitVarArgsConstructorsUsage/A.java new file mode 100644 index 000000000000..71cbc445bfec --- /dev/null +++ b/java/java-tests/testData/psi/search/findUsages/implicitVarArgsConstructorsUsage/A.java @@ -0,0 +1,9 @@ +class A1 { + A1(String... i){} + A1(Object... i){} +} +class B1 extends A1 { + public B1() { +// super(); + } +} diff --git a/java/java-tests/testSrc/com/intellij/psi/search/FindUsagesTest.java b/java/java-tests/testSrc/com/intellij/psi/search/FindUsagesTest.java index 37957883c373..633479c4b5ce 100644 --- a/java/java-tests/testSrc/com/intellij/psi/search/FindUsagesTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/search/FindUsagesTest.java @@ -45,7 +45,6 @@ import java.util.Collections; import java.util.List; public class FindUsagesTest extends PsiTestCase{ - @Override protected void setUp() throws Exception { super.setUp(); @@ -104,6 +103,17 @@ public class FindUsagesTest extends PsiTestCase{ assertEquals(1, ReferencesSearch.search(usedMethod).findAll().size()); } + public void testImplicitVarArgsConstructorsUsage() throws Throwable { + PsiMethod[] ctrs = myJavaFacade.findClass("A1", GlobalSearchScope.allScope(myProject)).getConstructors(); + PsiMethod usedCtr = ctrs[0]; + assertEquals("java.lang.String", ((PsiEllipsisType)usedCtr.getParameterList().getParameters()[0].getType()).getComponentType().getCanonicalText()); + assertEquals(1, ReferencesSearch.search(usedCtr).findAll().size()); + + PsiMethod unusedCtr = ctrs[1]; + assertEquals("java.lang.Object", ((PsiEllipsisType)unusedCtr.getParameterList().getParameters()[0].getType()).getComponentType().getCanonicalText()); + assertEquals(0, ReferencesSearch.search(unusedCtr).findAll().size()); + } + private static void addReference(PsiReference ref, ArrayList filesList, IntArrayList startsList, IntArrayList endsList) { PsiElement element = ref.getElement(); filesList.add(element.getContainingFile());