Use resolveImaginarySuperCallInThisPlace() instead of enumerating all super class constructors; find usages of overloaded ctr should find only correct implicit super calls

This commit is contained in:
Alexey Kudravtsev
2016-09-01 16:59:57 +03:00
parent b5ab580b0b
commit 0798a43f7d
6 changed files with 81 additions and 51 deletions
@@ -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<PsiMethod> 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<PsiMethod> 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 |
@@ -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<Void>() {
@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) {
@@ -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();
}
}
@@ -17,12 +17,3 @@ class B1 extends A1 {
}
class C1 extends A1 {}
class A2 {
A2(int... i){}
A2(Object... i){}
}
class B2 extends A2 {
<error descr="Ambiguous method call: both 'A2.A2(int...)' and 'A2.A2(Object...)' match">public B2()</error> {
}
}
<error descr="Ambiguous method call: both 'A2.A2(int...)' and 'A2.A2(Object...)' match">class C2 extends A2</error> {}
@@ -0,0 +1,9 @@
class A1 {
A1(String... i){}
A1(Object... i){}
}
class B1 extends A1 {
public B1() {
// super();
}
}
@@ -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<PsiFile> filesList, IntArrayList startsList, IntArrayList endsList) {
PsiElement element = ref.getElement();
filesList.add(element.getContainingFile());