mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[groovy] make inferred substitutor lazy and do not compute it until returned from resolve.
- check applicability by shape in MethodResolverProcessor. - erase argument types if by shape but use non-erased types for inferring substitutor anyways.
This commit is contained in:
+20
-3
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.jetbrains.plugins.groovy.lang.psi.impl;
|
||||
|
||||
import com.intellij.openapi.util.NotNullLazyValue;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiSubstitutor;
|
||||
@@ -24,20 +25,25 @@ import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState;
|
||||
|
||||
public class GroovyMethodResult extends GroovyResolveResultImpl {
|
||||
|
||||
private final NotNullLazyValue<PsiSubstitutor> mySubstitutorInferer;
|
||||
|
||||
public GroovyMethodResult(@NotNull PsiMethod method,
|
||||
@Nullable PsiElement resolveContext,
|
||||
@Nullable SpreadState spreadState,
|
||||
@NotNull PsiSubstitutor substitutor,
|
||||
boolean isAccessible, boolean staticsOK) {
|
||||
super(method, resolveContext, spreadState, substitutor, isAccessible, staticsOK, true, true);
|
||||
mySubstitutorInferer = NotNullLazyValue.createConstantValue(substitutor);
|
||||
}
|
||||
|
||||
public GroovyMethodResult(@NotNull PsiMethod method,
|
||||
public GroovyMethodResult(@NotNull PsiMethod element,
|
||||
@Nullable PsiElement resolveContext,
|
||||
@Nullable SpreadState spreadState,
|
||||
@NotNull PsiSubstitutor substitutor,
|
||||
@NotNull PsiSubstitutor partialSubstitutor,
|
||||
@NotNull NotNullLazyValue<PsiSubstitutor> substitutorInferer,
|
||||
boolean isAccessible, boolean staticsOK, boolean isApplicable) {
|
||||
super(method, resolveContext, spreadState, substitutor, isAccessible, staticsOK, false, isApplicable);
|
||||
super(element, resolveContext, spreadState, partialSubstitutor, isAccessible, staticsOK, false, isApplicable);
|
||||
mySubstitutorInferer = substitutorInferer;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -45,4 +51,15 @@ public class GroovyMethodResult extends GroovyResolveResultImpl {
|
||||
public PsiMethod getElement() {
|
||||
return (PsiMethod)super.getElement();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiSubstitutor getSubstitutor() {
|
||||
return mySubstitutorInferer.getValue();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiSubstitutor getSubstitutor(boolean infer) {
|
||||
return infer ? mySubstitutorInferer.getValue() : super.getSubstitutor();
|
||||
}
|
||||
}
|
||||
|
||||
+21
-75
@@ -18,7 +18,10 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.RecursionManager;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.resolve.ResolveCache;
|
||||
@@ -42,7 +45,6 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement;
|
||||
@@ -265,8 +267,8 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
|
||||
return resolveMethodOrProperty(true, upToArgument, true);
|
||||
}
|
||||
|
||||
private void processMethods(@NotNull MethodResolverProcessor methodResolver) {
|
||||
new GrReferenceResolveRunner(this).resolveImpl(methodResolver);
|
||||
private void processMethods(GrReferenceResolveRunner runner, @NotNull MethodResolverProcessor methodResolver) {
|
||||
runner.resolveImpl(methodResolver);
|
||||
if (methodResolver.hasApplicableCandidates()) {
|
||||
return;
|
||||
}
|
||||
@@ -301,29 +303,21 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
|
||||
}
|
||||
}
|
||||
|
||||
final Pair<Boolean, GroovyResolveResult[]> shapeResults = resolveByShape(allVariants, upToArgument);
|
||||
if (!genericsMatter && !allVariants && shapeResults.first) {
|
||||
assertAllAreValid(shapeResults.second);
|
||||
return shapeResults.second;
|
||||
}
|
||||
final MethodResolverProcessor methodProcessor = new MethodResolverProcessor(
|
||||
name, this, false,
|
||||
PsiImplUtil.getQualifierType(this),
|
||||
PsiUtil.getArgumentTypes(this, false, upToArgument, !genericsMatter),
|
||||
getTypeArguments(),
|
||||
allVariants,
|
||||
!genericsMatter
|
||||
);
|
||||
processMethods(resolveRunner, methodProcessor);
|
||||
final GroovyResolveResult[] methodCandidates = hasMemberPointer() ? collapseReflectedMethods(methodProcessor.getCandidates())
|
||||
: methodProcessor.getCandidates();
|
||||
assertAllAreValid(methodCandidates);
|
||||
|
||||
MethodResolverProcessor methodResolver = null;
|
||||
if (genericsMatter) {
|
||||
methodResolver = createMethodProcessor(allVariants, name, false, upToArgument);
|
||||
|
||||
for (GroovyResolveResult result : shapeResults.second) {
|
||||
final ResolveState state = ResolveState.initial().
|
||||
put(PsiSubstitutor.KEY, result.getSubstitutor()).
|
||||
put(ClassHint.RESOLVE_CONTEXT, result.getCurrentFileResolveContext()).
|
||||
put(SpreadState.SPREAD_STATE, result.getSpreadState());
|
||||
PsiElement element = result.getElement();
|
||||
assert element != null;
|
||||
methodResolver.execute(element, state);
|
||||
}
|
||||
|
||||
if (!allVariants && methodResolver.hasApplicableCandidates()) {
|
||||
return methodResolver.getCandidates();
|
||||
}
|
||||
if (!allVariants && methodProcessor.hasApplicableCandidates()) {
|
||||
return methodCandidates;
|
||||
}
|
||||
|
||||
//search for fields inside its class
|
||||
@@ -339,7 +333,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
|
||||
|
||||
List<GroovyResolveResult> allCandidates = new ArrayList<GroovyResolveResult>();
|
||||
ContainerUtil.addAll(allCandidates, propertyCandidates);
|
||||
ContainerUtil.addAll(allCandidates, genericsMatter ? methodResolver.getCandidates() : shapeResults.second);
|
||||
ContainerUtil.addAll(allCandidates, methodCandidates);
|
||||
|
||||
filterOutBindings(allCandidates);
|
||||
|
||||
@@ -379,39 +373,6 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Pair<Boolean, GroovyResolveResult[]> resolveByShape(boolean allVariants, @Nullable GrExpression upToArgument) {
|
||||
if (allVariants) {
|
||||
return doResolveByShape(true, upToArgument);
|
||||
}
|
||||
|
||||
LOG.assertTrue(upToArgument == null);
|
||||
|
||||
return TypeInferenceHelper.getCurrentContext().getCachedValue(this, new NullableComputable<Pair<Boolean, GroovyResolveResult[]>>() {
|
||||
@Override
|
||||
public Pair<Boolean, GroovyResolveResult[]> compute() {
|
||||
return doResolveByShape(false, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Pair<Boolean, GroovyResolveResult[]> doResolveByShape(boolean allVariants, @Nullable GrExpression upToArgument) {
|
||||
final String name = getReferenceName();
|
||||
LOG.assertTrue(name != null);
|
||||
|
||||
final MethodResolverProcessor shapeProcessor = createMethodProcessor(allVariants, name, true, upToArgument);
|
||||
processMethods(shapeProcessor);
|
||||
GroovyResolveResult[] candidates = shapeProcessor.getCandidates();
|
||||
assertAllAreValid(candidates);
|
||||
|
||||
if (hasMemberPointer()) {
|
||||
candidates = collapseReflectedMethods(candidates);
|
||||
}
|
||||
|
||||
return Pair.create(shapeProcessor.hasApplicableCandidates(), candidates);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static GroovyResolveResult[] collapseReflectedMethods(GroovyResolveResult[] candidates) {
|
||||
Set<GrMethod> visited = ContainerUtil.newHashSet();
|
||||
@@ -437,21 +398,6 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl<GrExpressi
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private MethodResolverProcessor createMethodProcessor(boolean allVariants,
|
||||
@Nullable String name,
|
||||
final boolean byShape,
|
||||
@Nullable GrExpression upToArgument) {
|
||||
final PsiType[] argTypes = PsiUtil.getArgumentTypes(this, false, upToArgument, byShape);
|
||||
if (byShape && argTypes != null) {
|
||||
for (int i = 0; i < argTypes.length; i++) {
|
||||
argTypes[i] = TypeConversionUtil.erasure(argTypes[i]);
|
||||
}
|
||||
}
|
||||
PsiType qualifierType = PsiImplUtil.getQualifierType(this);
|
||||
return new MethodResolverProcessor(name, this, false, qualifierType, argTypes, getTypeArguments(), allVariants, byShape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(GroovyElementVisitor visitor) {
|
||||
visitor.visitReferenceExpression(this);
|
||||
|
||||
+59
-21
@@ -61,6 +61,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatem
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyMethodResult;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
|
||||
@@ -543,34 +544,71 @@ public class ResolveUtil {
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* The point is that we do not want to see repeating methods in completion.
|
||||
* Candidates can have multiple toString() methods (e.g. from Object and from some inheritor) and we want to show only one.
|
||||
*/
|
||||
public static GroovyResolveResult[] filterSameSignatureCandidates(Collection<? extends GroovyResolveResult> candidates) {
|
||||
GroovyResolveResult[] array = candidates.toArray(new GroovyResolveResult[candidates.size()]);
|
||||
if (array.length == 1) return array;
|
||||
if (candidates.size() == 0) return GroovyResolveResult.EMPTY_ARRAY;
|
||||
if (candidates.size() == 1) return candidates.toArray(new GroovyResolveResult[candidates.size()]);
|
||||
|
||||
List<GroovyResolveResult> result = new ArrayList<GroovyResolveResult>();
|
||||
result.add(array[0]);
|
||||
final List<GroovyResolveResult> result = new ArrayList<GroovyResolveResult>();
|
||||
|
||||
final Iterator<? extends GroovyResolveResult> allIterator = candidates.iterator();
|
||||
result.add(allIterator.next());
|
||||
|
||||
Outer:
|
||||
for (int i = 1; i < array.length; i++) {
|
||||
PsiElement currentElement = array[i].getElement();
|
||||
if (currentElement instanceof PsiMethod) {
|
||||
PsiMethod currentMethod = (PsiMethod)currentElement;
|
||||
for (Iterator<GroovyResolveResult> iterator = result.iterator(); iterator.hasNext();) {
|
||||
final GroovyResolveResult otherResolveResult = iterator.next();
|
||||
PsiElement element = otherResolveResult.getElement();
|
||||
if (element instanceof PsiMethod) {
|
||||
PsiMethod method = (PsiMethod)element;
|
||||
if (dominated(currentMethod, array[i].getSubstitutor(), method, otherResolveResult.getSubstitutor())) {
|
||||
continue Outer;
|
||||
}
|
||||
else if (dominated(method, otherResolveResult.getSubstitutor(), currentMethod, array[i].getSubstitutor())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
while (allIterator.hasNext()) {
|
||||
final GroovyResolveResult currentResult = allIterator.next();
|
||||
|
||||
final PsiMethod currentMethod;
|
||||
final PsiSubstitutor currentSubstitutor;
|
||||
if (currentResult instanceof GroovyMethodResult) {
|
||||
final GroovyMethodResult currentMethodResult = (GroovyMethodResult)currentResult;
|
||||
currentMethod = currentMethodResult.getElement();
|
||||
currentSubstitutor = currentMethodResult.getSubstitutor(false);
|
||||
}
|
||||
else if (currentResult.getElement() instanceof PsiMethod) {
|
||||
currentMethod = (PsiMethod)currentResult.getElement();
|
||||
currentSubstitutor = currentResult.getSubstitutor();
|
||||
}
|
||||
else {
|
||||
result.add(currentResult);
|
||||
continue;
|
||||
}
|
||||
|
||||
Inner:
|
||||
for (Iterator<GroovyResolveResult> resultIterator = result.iterator(); resultIterator.hasNext(); ) {
|
||||
final GroovyResolveResult otherResult = resultIterator.next();
|
||||
|
||||
final PsiMethod otherMethod;
|
||||
final PsiSubstitutor otherSubstitutor;
|
||||
if (otherResult instanceof GroovyMethodResult) {
|
||||
final GroovyMethodResult otherMethodResult = (GroovyMethodResult)otherResult;
|
||||
otherMethod = otherMethodResult.getElement();
|
||||
otherSubstitutor = otherMethodResult.getSubstitutor(false);
|
||||
}
|
||||
else if (otherResult.getElement() instanceof PsiMethod) {
|
||||
otherMethod = (PsiMethod)otherResult.getElement();
|
||||
otherSubstitutor = otherResult.getSubstitutor();
|
||||
}
|
||||
else {
|
||||
continue Inner;
|
||||
}
|
||||
|
||||
if (dominated(currentMethod, currentSubstitutor, otherMethod, otherSubstitutor)) {
|
||||
// if current method is dominated by other method
|
||||
// then do not add current method to result and skip rest other methods
|
||||
continue Outer;
|
||||
}
|
||||
else if (dominated(otherMethod, otherSubstitutor, currentMethod, currentSubstitutor)) {
|
||||
// if other method is dominated by current method
|
||||
// then remove other from result
|
||||
resultIterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
result.add(array[i]);
|
||||
result.add(currentResult);
|
||||
}
|
||||
|
||||
return result.toArray(new GroovyResolveResult[result.size()]);
|
||||
|
||||
+24
-13
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.plugins.groovy.lang.resolve.processors;
|
||||
|
||||
import com.intellij.openapi.util.NotNullLazyValue;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.scope.JavaScopeProcessorEvent;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
@@ -82,10 +83,15 @@ public class MethodResolverProcessor extends ResolverProcessor<GroovyMethodResul
|
||||
myIsConstructor = isConstructor;
|
||||
myThisType = thisType;
|
||||
myArgumentTypes = argumentTypes;
|
||||
if (byShape && myArgumentTypes != null) {
|
||||
for (int i = 0; i < myArgumentTypes.length; i++) {
|
||||
myArgumentTypes[i] = TypeConversionUtil.erasure(myArgumentTypes[i]);
|
||||
}
|
||||
}
|
||||
myAllVariants = allVariants;
|
||||
myByShape = byShape;
|
||||
|
||||
mySubstitutorComputer = new SubstitutorComputer(myThisType, myArgumentTypes, typeArguments, myPlace, myPlace.getParent());
|
||||
mySubstitutorComputer = new SubstitutorComputer(myThisType, argumentTypes, typeArguments, myPlace, myPlace.getParent());
|
||||
}
|
||||
|
||||
|
||||
@@ -95,22 +101,29 @@ public class MethodResolverProcessor extends ResolverProcessor<GroovyMethodResul
|
||||
return false;
|
||||
}
|
||||
if (element instanceof PsiMethod) {
|
||||
PsiMethod method = (PsiMethod) element;
|
||||
final PsiMethod method = (PsiMethod)element;
|
||||
|
||||
if (method.isConstructor() != myIsConstructor) return true;
|
||||
|
||||
PsiSubstitutor substitutor = inferSubstitutor(method, state);
|
||||
|
||||
PsiElement resolveContext = state.get(RESOLVE_CONTEXT);
|
||||
final PsiElement resolveContext = state.get(RESOLVE_CONTEXT);
|
||||
final SpreadState spreadState = state.get(SpreadState.SPREAD_STATE);
|
||||
final PsiSubstitutor partialSubstitutor = getSubstitutor(state);
|
||||
final NotNullLazyValue<PsiSubstitutor> substitutorInferer
|
||||
= myByShape ? NotNullLazyValue.createConstantValue(partialSubstitutor) : new NotNullLazyValue<PsiSubstitutor>() {
|
||||
@NotNull
|
||||
@Override
|
||||
protected PsiSubstitutor compute() {
|
||||
return mySubstitutorComputer.obtainSubstitutor(partialSubstitutor, method, resolveContext);
|
||||
}
|
||||
};
|
||||
|
||||
boolean isAccessible = isAccessible(method);
|
||||
boolean isStaticsOK = isStaticsOK(method, resolveContext, false);
|
||||
boolean isApplicable = PsiUtil.isApplicable(myArgumentTypes, method, substitutor, myPlace, myByShape);
|
||||
boolean isApplicable = PsiUtil.isApplicable(myArgumentTypes, method, null, myPlace, true);
|
||||
boolean isValidResult = isStaticsOK && isAccessible && isApplicable;
|
||||
|
||||
GroovyMethodResult candidate = new GroovyMethodResult(
|
||||
method, resolveContext, spreadState, substitutor, isAccessible, isStaticsOK, isValidResult
|
||||
method, resolveContext, spreadState, partialSubstitutor, substitutorInferer, isAccessible, isStaticsOK, isValidResult
|
||||
);
|
||||
|
||||
if (!myAllVariants && isValidResult) {
|
||||
@@ -132,12 +145,10 @@ public class MethodResolverProcessor extends ResolverProcessor<GroovyMethodResul
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private PsiSubstitutor inferSubstitutor(@NotNull PsiMethod method, @NotNull ResolveState state) {
|
||||
private static PsiSubstitutor getSubstitutor(@NotNull final ResolveState state) {
|
||||
PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
|
||||
if (substitutor == null) substitutor = PsiSubstitutor.EMPTY;
|
||||
|
||||
return myByShape ? substitutor
|
||||
: mySubstitutorComputer.obtainSubstitutor(substitutor, method, state.get(RESOLVE_CONTEXT));
|
||||
return substitutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -230,10 +241,10 @@ public class MethodResolverProcessor extends ResolverProcessor<GroovyMethodResul
|
||||
//method1 has more general parameter types thn method2
|
||||
private boolean secondMethodIsPreferable(@NotNull GroovyMethodResult result1, @NotNull GroovyMethodResult result2) {
|
||||
PsiMethod method1 = result1.getElement();
|
||||
PsiSubstitutor substitutor1 = result1.getSubstitutor();
|
||||
PsiSubstitutor substitutor1 = result1.getSubstitutor(false);
|
||||
PsiElement resolveContext1 = result1.getCurrentFileResolveContext();
|
||||
PsiMethod method2 = result2.getElement();
|
||||
PsiSubstitutor substitutor2 = result2.getSubstitutor();
|
||||
PsiSubstitutor substitutor2 = result2.getSubstitutor(false);
|
||||
PsiElement resolveContext2 = result2.getCurrentFileResolveContext();
|
||||
|
||||
final Boolean custom = GrMethodComparator.checkDominated(method1, substitutor1, method2, substitutor2, this);
|
||||
|
||||
Reference in New Issue
Block a user