new inference: method refs overload resolution compact

This commit is contained in:
Anna Kozlova
2014-02-11 14:20:47 +01:00
parent 1cf61ab414
commit 1b17bec2dd
@@ -19,7 +19,6 @@ import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy;
@@ -306,6 +305,12 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
return this;
}
@Override
public boolean isConstructor() {
final PsiElement element = getReferenceNameElement();
return element instanceof PsiKeyword && PsiKeyword.NEW.equals(element.getText());
}
@Override
public String toString() {
return "PsiMethodReferenceExpression:" + getText();
@@ -373,16 +378,8 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
final MethodReferenceConflictResolver conflictResolver =
new MethodReferenceConflictResolver(qualifierResolveResult, signature, interfaceMethod != null && interfaceMethod.isVarArgs());
final PsiConflictResolver[] resolvers;
if (signature != null) {
final PsiType[] parameterTypes = signature.getParameterTypes();
resolvers = new PsiConflictResolver[]{conflictResolver, new MethodRefsSpecificResolver(parameterTypes, PsiUtil.getLanguageLevel(PsiMethodReferenceExpressionImpl.this))};
}
else {
resolvers = new PsiConflictResolver[]{conflictResolver};
}
final MethodCandidatesProcessor processor =
new MethodCandidatesProcessor(PsiMethodReferenceExpressionImpl.this, getContainingFile(), resolvers, new SmartList<CandidateInfo>()) {
new MethodCandidatesProcessor(PsiMethodReferenceExpressionImpl.this, getContainingFile(), new PsiConflictResolver[] {conflictResolver}, new SmartList<CandidateInfo>()) {
@Override
protected MethodCandidateInfo createCandidateInfo(final PsiMethod method,
final PsiSubstitutor substitutor,
@@ -500,13 +497,14 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
.createType(containingClass, isRawSubst ? PsiSubstitutor.EMPTY : substitutor);
}
private class MethodReferenceConflictResolver implements PsiConflictResolver {
private class MethodReferenceConflictResolver extends JavaMethodsConflictResolver {
private final MethodSignature mySignature;
private final PsiMethodReferenceUtil.QualifierResolveResult myQualifierResolveResult;
private final boolean myFunctionalMethodVarArgs;
private MethodReferenceConflictResolver(PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult,
@Nullable MethodSignature signature, boolean varArgs) {
super(PsiMethodReferenceExpressionImpl.this, signature != null ? signature.getParameterTypes() : PsiType.EMPTY_ARRAY, PsiUtil.getLanguageLevel(PsiMethodReferenceExpressionImpl.this));
myQualifierResolveResult = qualifierResolveResult;
myFunctionalMethodVarArgs = varArgs;
mySignature = signature;
@@ -515,83 +513,101 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
@Nullable
@Override
public CandidateInfo resolveConflict(@NotNull List<CandidateInfo> conflicts) {
return resolveConflict(conflicts, false);
}
public CandidateInfo resolveConflict(@NotNull List<CandidateInfo> conflicts, boolean varargs) {
if (mySignature == null) return null;
checkSameSignatures(conflicts);
checkAccessStaticLevels(conflicts, true);
final PsiType[] parameterTypes = mySignature.getParameterTypes();
boolean hasReceiver = PsiMethodReferenceUtil.hasReceiver(parameterTypes, myQualifierResolveResult,
PsiMethodReferenceExpressionImpl.this);
final List<CandidateInfo> firstCandidates = new ArrayList<CandidateInfo>();
final List<CandidateInfo> secondCandidates = new ArrayList<CandidateInfo>();
for (CandidateInfo conflict : conflicts) {
if (!(conflict instanceof MethodCandidateInfo)) continue;
final PsiMethod psiMethod = ((MethodCandidateInfo)conflict).getElement();
if (psiMethod == null) continue;
PsiSubstitutor substitutor = conflict.getSubstitutor();
final PsiSubstitutor substitutor = conflict.getSubstitutor();
final PsiType[] signatureParameterTypes2 = psiMethod.getSignature(substitutor).getParameterTypes();
boolean varArgs = psiMethod.isVarArgs();
if (varargs && (!psiMethod.isVarArgs() || myFunctionalMethodVarArgs)) continue;
if (parameterTypes.length == signatureParameterTypes2.length &&
isCorrectAssignment(signatureParameterTypes2, parameterTypes, substitutor, false, 0)) {
if ((varargs || parameterTypes.length == signatureParameterTypes2.length) &&
isCorrectAssignment(signatureParameterTypes2, parameterTypes, substitutor, varargs, 0)) {
firstCandidates.add(conflict);
varArgs = false;
}
if (hasReceiver &&
parameterTypes.length == signatureParameterTypes2.length + 1 &&
isCorrectAssignment(signatureParameterTypes2, parameterTypes, substitutor, false, 1)) {
(varargs || parameterTypes.length == signatureParameterTypes2.length + 1) &&
isCorrectAssignment(signatureParameterTypes2, parameterTypes, substitutor, varargs, 1)) {
secondCandidates.add(conflict);
varArgs = false;
}
if (varArgs && !myFunctionalMethodVarArgs) {
if (isCorrectAssignment(signatureParameterTypes2, parameterTypes, substitutor, true, 0)) {
firstCandidates.add(conflict);
}
if (hasReceiver && isCorrectAssignment(signatureParameterTypes2, parameterTypes, substitutor, true, 1)) {
secondCandidates.add(conflict);
}
}
}
final boolean identifierRef = getReferenceNameElement() instanceof PsiIdentifier;
//If the first search produces a static method, and no non-static method is applicable for the second search, then the result of the first search is the compile-time declaration.
final List<CandidateInfo> firstCandidateInfos = myQualifierResolveResult.isReferenceTypeQualified() && identifierRef ? filterStaticCorrectCandidates(firstCandidates, true) : firstCandidates;
checkSpecifics(firstCandidates,
varargs ? MethodCandidateInfo.ApplicabilityLevel.VARARGS : MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY, myLanguageLevel);
//If the second search produces a non-static method, and no static method is applicable for the first search, then the result of the second search is the compile-time declaration.
final List<CandidateInfo> secondCandidateInfos = myQualifierResolveResult.isReferenceTypeQualified() && identifierRef ? filterStaticCorrectCandidates(secondCandidates, false) : secondCandidates;
checkSpecifics(secondCandidates,
varargs ? MethodCandidateInfo.ApplicabilityLevel.VARARGS : MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY, myLanguageLevel);
final int acceptedCount = firstCandidateInfos.size() + secondCandidateInfos.size();
if (myQualifierResolveResult.isReferenceTypeQualified() && getReferenceNameElement() instanceof PsiIdentifier) {
//If the first search produces a static method, and no non-static method is applicable for the second search, then the result of the first search is the compile-time declaration.
filterStaticCorrectCandidates(firstCandidates, true);
//If the second search produces a non-static method, and no static method is applicable for the first search, then the result of the second search is the compile-time declaration.
filterStaticCorrectCandidates(secondCandidates, false);
}
final int acceptedCount = firstCandidates.size() + secondCandidates.size();
if (acceptedCount == 1) {
return !firstCandidateInfos.isEmpty() ? firstCandidateInfos.get(0) : secondCandidateInfos.get(0);
return !firstCandidates.isEmpty() ? firstCandidates.get(0) : secondCandidates.get(0);
}
if (!varargs) {
return resolveConflict(conflicts, true);
}
conflicts.clear();
firstCandidateInfos.addAll(secondCandidateInfos);
conflicts.addAll(firstCandidateInfos);
firstCandidates.addAll(secondCandidates);
conflicts.addAll(firstCandidates);
return null;
}
@Override
protected boolean nonComparable(CandidateInfo method, CandidateInfo conflict) {
if (method == conflict) return true;
PsiElement psiElement = method.getElement();
PsiElement conflictElement = conflict.getElement();
if (psiElement instanceof PsiMethod && conflictElement instanceof PsiMethod) {
if (((PsiMethod)psiElement).getParameterList().getParametersCount() !=
((PsiMethod)conflictElement).getParameterList().getParametersCount()) {
return true;
}
}
return false;
}
/**
* 15.13.1
*/
private List<CandidateInfo> filterStaticCorrectCandidates(List<CandidateInfo> firstCandidates,
boolean shouldBeStatic) {
final List<CandidateInfo> result = new ArrayList<CandidateInfo>();
for (CandidateInfo candidate : firstCandidates) {
final CandidateInfo candidateInfo = firstCandidates.get(0);
final PsiElement element = candidateInfo.getElement();
private void filterStaticCorrectCandidates(List<CandidateInfo> firstCandidates,
boolean shouldBeStatic) {
for (Iterator<CandidateInfo> iterator = firstCandidates.iterator(); iterator.hasNext(); ) {
final PsiElement element = iterator.next().getElement();
if (element instanceof PsiMethod) {
final boolean isStatic = ((PsiMethod)element).hasModifierProperty(PsiModifier.STATIC);
if (shouldBeStatic && isStatic || !shouldBeStatic && !isStatic) {
result.add(candidate);
if (shouldBeStatic && !isStatic || !shouldBeStatic && isStatic) {
iterator.remove();
}
}
}
return result;
}
private boolean isCorrectAssignment(PsiType[] signatureParameterTypes2,
@@ -615,67 +631,5 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
return true;
}
}
private class MethodRefsSpecificResolver extends JavaMethodsConflictResolver {
public MethodRefsSpecificResolver(@NotNull PsiType[] parameterTypes, @NotNull LanguageLevel languageLevel) {
super(PsiMethodReferenceExpressionImpl.this, parameterTypes, languageLevel);
}
@Override
public CandidateInfo resolveConflict(@NotNull List<CandidateInfo> conflicts) {
checkSameSignatures(conflicts);
if (conflicts.size() == 1) return conflicts.get(0);
checkAccessStaticLevels(conflicts, true);
if (conflicts.size() == 1) return conflicts.get(0);
boolean varargs = false;
boolean fixedArity = false;
for (CandidateInfo conflict : conflicts) {
final PsiElement psiElement = conflict.getElement();
if (psiElement instanceof PsiMethod) {
final boolean isVarargs = ((PsiMethod)psiElement).isVarArgs();
if (isVarargs) {
varargs = true;
} else {
fixedArity = true;
}
if (varargs && fixedArity) break;
}
}
if (varargs && fixedArity) {
for (Iterator<CandidateInfo> iterator = conflicts.iterator(); iterator.hasNext(); ) {
CandidateInfo conflict = iterator.next();
final PsiElement element = conflict.getElement();
if (element instanceof PsiMethod && ((PsiMethod)element).isVarArgs()) iterator.remove();
}
}
checkSpecifics(conflicts,
varargs ? MethodCandidateInfo.ApplicabilityLevel.VARARGS : MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY,
myLanguageLevel);
return conflicts.size() == 1 ? conflicts.get(0) : null;
}
@Override
protected boolean nonComparable(CandidateInfo method, CandidateInfo conflict) {
if (method == conflict) return true;
PsiElement psiElement = method.getElement();
PsiElement conflictElement = conflict.getElement();
if (psiElement instanceof PsiMethod && conflictElement instanceof PsiMethod) {
if (((PsiMethod)psiElement).getParameterList().getParametersCount() !=
((PsiMethod)conflictElement).getParameterList().getParametersCount()) {
return true;
}
}
return false;
}
}
}
@Override
public boolean isConstructor() {
final PsiElement element = getReferenceNameElement();
return element instanceof PsiKeyword && PsiKeyword.NEW.equals(element.getText());
}
}