mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
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:
+13
-26
@@ -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 |
|
||||
|
||||
+25
-15
@@ -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();
|
||||
}
|
||||
}
|
||||
-9
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user