method ref: initial ambiguity rules

This commit is contained in:
anna
2012-09-26 18:33:18 +02:00
parent 73e52e462d
commit 806d8384b4
7 changed files with 274 additions and 38 deletions
@@ -16,7 +16,6 @@
package com.intellij.psi;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.infos.MethodCandidateInfo;
@@ -561,18 +560,18 @@ public class LambdaUtil {
return true;
}
public static boolean isAcceptable(PsiMethodReferenceExpression methodReferenceExpression, PsiClassType left) {
final JavaResolveResult[] results = methodReferenceExpression.multiResolve(false);
for (JavaResolveResult result : results) {
final PsiElement resolve = result.getElement();
if (resolve instanceof PsiMethod) {
final PsiClassType.ClassResolveResult resolveResult = left.resolveGenerics();
final PsiMethod method = getFunctionalInterfaceMethod(resolveResult);
if (method != null) {
final MethodSignature signature1 = method.getSignature(resolveResult.getSubstitutor());
final MethodSignature signature2 = ((PsiMethod)resolve).getSignature(result.getSubstitutor());
if (areAcceptable(signature1, signature2)) return true;
}
public static boolean isAcceptable(@Nullable PsiMethodReferenceExpression methodReferenceExpression, PsiType left) {
if (methodReferenceExpression == null) return false;
final JavaResolveResult result = methodReferenceExpression.advancedResolve(false);
final PsiElement resolve = result.getElement();
if (resolve instanceof PsiMethod) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(left);
final PsiMethod method = getFunctionalInterfaceMethod(resolveResult);
if (method != null) {
final MethodSignature signature1 = method.getSignature(resolveResult.getSubstitutor());
final MethodSignature signature2 = ((PsiMethod)resolve).getSignature(JavaPsiFacade.getElementFactory(method.getProject()).createRawSubstitutor(
(PsiTypeParameterListOwner)resolve));
if (areAcceptable(signature1, signature2)) return true;
}
}
return false;
@@ -585,13 +584,24 @@ public class LambdaUtil {
for (int i = 0; i < signatureParameterTypes1.length; i++) {
final PsiType type1 = signatureParameterTypes1[i];
final PsiType type2 = signatureParameterTypes2[i];
if (!Comparing.equal(GenericsUtil.eliminateWildcards(type1), GenericsUtil.eliminateWildcards(type2))) {
if (!TypeConversionUtil.isAssignable(GenericsUtil.eliminateWildcards(type2), GenericsUtil.eliminateWildcards(type1))) {
return false;
}
}
return true;
}
public static PsiLocalVariable createMethodReferenceExpressionAccording2Type(PsiMethodReferenceExpression methodReferenceExpression,
PsiType leftType) {
final String uniqueVarName =
JavaCodeStyleManager.getInstance(methodReferenceExpression.getProject())
.suggestUniqueVariableName("l", methodReferenceExpression, true);
final PsiStatement assignmentFromText = JavaPsiFacade.getElementFactory(methodReferenceExpression.getProject())
.createStatementFromText(leftType.getCanonicalText() + " " + uniqueVarName + " = " + methodReferenceExpression.getText(),
methodReferenceExpression);
return (PsiLocalVariable)((PsiDeclarationStatement)assignmentFromText).getDeclaredElements()[0];
}
private static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
private PsiMethod myMethod;
private final PsiClass myClass;
@@ -135,7 +135,11 @@ public class MethodCandidateInfo extends CandidateInfo{
PsiTypeParameter[] typeParams = getElement().getTypeParameters();
if (myTypeArguments != null && typeParams.length != myTypeArguments.length) return false;
PsiSubstitutor substitutor = getSubstitutor();
return GenericsUtil.isTypeArgumentsApplicable(typeParams, substitutor, myArgumentList.getParent());
return GenericsUtil.isTypeArgumentsApplicable(typeParams, substitutor, getParent());
}
private PsiElement getParent() {
return myArgumentList != null ? myArgumentList.getParent() : myArgumentList;
}
@Override
@@ -150,8 +154,8 @@ public class MethodCandidateInfo extends CandidateInfo{
public PsiSubstitutor inferTypeArguments(final ParameterTypeInferencePolicy policy) {
return inferTypeArguments(policy, myArgumentList instanceof PsiExpressionList
? ((PsiExpressionList)myArgumentList).getExpressions()
: PsiExpression.EMPTY_ARRAY);
? ((PsiExpressionList)myArgumentList).getExpressions()
: PsiExpression.EMPTY_ARRAY);
}
public PsiSubstitutor inferTypeArguments(final ParameterTypeInferencePolicy policy, final PsiExpression[] arguments) {
@@ -166,12 +170,14 @@ public class MethodCandidateInfo extends CandidateInfo{
}
}
return javaPsiFacade.getResolveHelper().inferTypeArguments(typeParameters, method.getParameterList().getParameters(), arguments, mySubstitutor,
myArgumentList.getParent(), policy);
final PsiElement parent = getParent();
if (parent == null) return PsiSubstitutor.EMPTY;
return javaPsiFacade.getResolveHelper()
.inferTypeArguments(typeParameters, method.getParameterList().getParameters(), arguments, mySubstitutor, parent, policy);
}
public boolean isInferencePossible() {
return myArgumentList.isValid();
return myArgumentList != null && myArgumentList.isValid();
}
public static class ApplicabilityLevel {
@@ -212,7 +212,11 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
if (currentSubstitution == FAILED_INFERENCE || (currentSubstitution == null && lowerBound == PsiType.NULL)) return RAW_INFERENCE;
}
if (nullPassed && currentSubstitution == null) return RAW_INFERENCE;
} else {
} else if (argumentType instanceof PsiMethodReferenceType) {
final PsiMethodReferenceExpression referenceExpression = ((PsiMethodReferenceType)argumentType).getExpression();
currentSubstitution = inferConstraintFromFunctionalInterfaceMethod(typeParameter, referenceExpression, parameterType);
}
else {
currentSubstitution = getSubstitutionForTypeParameterConstraint(typeParameter, parameterType,
argumentType, true, PsiUtil.getLanguageLevel(typeParameter));
}
@@ -589,6 +593,40 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
return null;
}
@Nullable
private static Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(PsiTypeParameter typeParam,
final PsiMethodReferenceExpression methodReferenceExpression,
final PsiType functionalInterfaceType) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
if (method != null) {
final PsiSubstitutor subst = LambdaUtil.getSubstitutor(method, resolveResult);
final PsiParameter[] methodParameters = method.getParameterList().getParameters();
PsiType[] methodParamTypes = new PsiType[methodParameters.length];
for (int i = 0; i < methodParameters.length; i++) {
methodParamTypes[i] = subst.substitute(methodParameters[i].getType());
}
final PsiType[] args = new PsiType[methodParameters.length];
final PsiElement resolved = methodReferenceExpression.resolve();
if (resolved instanceof PsiMethod) {
final PsiParameter[] parameters = ((PsiMethod)resolved).getParameterList().getParameters();
if (parameters.length != methodParameters.length) return null;
for (int i = 0; i < parameters.length; i++) {
args[i] = subst.substitute(parameters[i].getType());
}
final Pair<PsiType, ConstraintType> constraint = inferTypeForMethodTypeParameterInner(typeParam, methodParamTypes, args, subst, null,
DefaultParameterTypeInferencePolicy.INSTANCE);
if (constraint != null){
return constraint;
}
return getSubstitutionForTypeParameterConstraint(typeParam, GenericsUtil.eliminateWildcards(subst.substitute(method.getReturnType())),
((PsiMethod)resolved).getReturnType(), true, PsiUtil.getLanguageLevel(method));
}
}
return null;
}
@Nullable
private static Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(PsiTypeParameter typeParam,
final PsiLambdaExpression lambdaExpression,
@@ -22,15 +22,16 @@ import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.scope.PsiConflictResolver;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.scope.conflictResolvers.DuplicateConflictResolver;
import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase implements PsiMethodReferenceExpression {
private static Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiMethodReferenceExpressionImpl");
@@ -153,8 +154,13 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
public ResolveResult[] resolve(@NotNull PsiJavaReference reference, boolean incompleteCode) {
PsiClass containingClass = null;
final PsiExpression expression = getQualifierExpression();
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
if (expression != null) {
containingClass = PsiUtil.resolveClassInType(expression.getType());
PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(expression.getType());
containingClass = result.getElement();
if (containingClass != null) {
substitutor = result.getSubstitutor();
}
if (containingClass == null && expression instanceof PsiReferenceExpression) {
final PsiElement resolve = ((PsiReferenceExpression)expression).resolve();
if (resolve instanceof PsiClass) {
@@ -165,7 +171,11 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
else {
final PsiTypeElement typeElement = getQualifierType();
if (typeElement != null) {
containingClass = PsiUtil.resolveClassInType(typeElement.getType());
PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(typeElement.getType());
containingClass = result.getElement();
if (containingClass != null) {
substitutor = result.getSubstitutor();
}
}
}
@@ -174,17 +184,28 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
if (element instanceof PsiIdentifier) {
final PsiType functionalInterfaceType = getFunctionalInterfaceType();
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
if (interfaceMethod == null) return JavaResolveResult.EMPTY_ARRAY;
final MethodSignature interfaceMethodSignature = interfaceMethod.getSignature(resolveResult.getSubstitutor());
final PsiMethod[] psiMethods = containingClass.findMethodsByName(element.getText(), false);
List<JavaResolveResult> result = new ArrayList<JavaResolveResult>();
for (PsiMethod method : psiMethods) {
if (LambdaUtil.areAcceptable(interfaceMethodSignature, method.getSignature(PsiSubstitutor.EMPTY))) {
result.add(new CandidateInfo(method, PsiSubstitutor.EMPTY));
PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
final MethodSignature interfaceMethodSignature = interfaceMethod != null ? interfaceMethod.getSignature(resolveResult.getSubstitutor()) : null;
MethodCandidatesProcessor processor = new MethodCandidatesProcessor(PsiMethodReferenceExpressionImpl.this,
new PsiConflictResolver[]{DuplicateConflictResolver.INSTANCE}, new SmartList<CandidateInfo>()) {
@Override
protected boolean isAccepted(PsiMethod candidate) {
if (super.isAccepted(candidate)) {
if (interfaceMethodSignature == null) return true;
return LambdaUtil.areAcceptable(interfaceMethodSignature,
candidate.getSignature(JavaPsiFacade.getElementFactory(getProject()).createRawSubstitutor(candidate)));
}
return false;
}
}
return result.toArray(new JavaResolveResult[result.size()]);
};
processor.setIsConstructor(false);
processor.setName(element.getText());
ResolveState state = ResolveState.initial().put(PsiSubstitutor.KEY, substitutor);
containingClass.processDeclarations(processor, state,
PsiMethodReferenceExpressionImpl.this,
PsiMethodReferenceExpressionImpl.this);
return processor.getResult();
}
}
return JavaResolveResult.EMPTY_ARRAY;
@@ -58,8 +58,9 @@ public class MethodCandidatesProcessor extends MethodsProcessor{
protected MethodCandidateInfo createCandidateInfo(final PsiMethod method, final PsiSubstitutor substitutor,
final boolean staticProblem, final boolean accessible) {
return new MethodCandidateInfo(method, substitutor, !accessible, staticProblem, getArgumentList(), myCurrentFileContext,
getArgumentList().getExpressionTypes(), getTypeArguments(), getLanguageLevel());
final PsiExpressionList argumentList = getArgumentList();
return new MethodCandidateInfo(method, substitutor, !accessible, staticProblem, argumentList, myCurrentFileContext,
argumentList != null ? argumentList.getExpressionTypes() : null, getTypeArguments(), getLanguageLevel());
}
protected boolean isAccepted(final PsiMethod candidate) {
@@ -0,0 +1,156 @@
package todelete;
/**
* Created with IntelliJ IDEA.
* User: Anna.Kozlova
* Date: 9/26/12
* Time: 1:18 PM
* To change this template use File | Settings | File Templates.
*/
class MyTest {
interface Bar1 {
int _(String s);
}
interface Bar2 {
int _(int s);
}
void bar(Bar1 b1){}
void bar(Bar2 b1){}
static int foo(int i) {
return 0;
}
static int foo(String i) {
return 0;
}
{
Bar1 b1 = MyTest :: foo;
bar<error descr="Cannot resolve method 'bar(<method reference>)'">(MyTest :: foo)</error>;
}
}
class MyTest1 {
interface Bar1 {
int _(String s);
}
interface Bar2 {
int _(int s);
}
//void bar(Bar1 b1){}
void bar(Bar2 b1){}
static int foo(int i) {
return 0;
}
static int foo(String i) {
return 0;
}
{
<error descr="Incompatible types. Found: '<method reference>', required: 'todelete.MyTest1.Bar1'">Bar1 b1 = MyTest2 :: foo;</error>
bar(MyTest1 :: foo);
}
}
class MyTest2 {
interface Bar1 {
int _(String s);
}
interface Bar2 {
int _(int s);
}
void bar(Bar1 b1){}
void bar(Bar2 b1){}
static int foo(int i) {
return 0;
}
/*static int foo(String i) {
return 0;
}*/
{
<error descr="Incompatible types. Found: '<method reference>', required: 'todelete.MyTest2.Bar1'">Bar1 b1 = MyTest2 :: foo;</error>
bar(MyTest2 :: foo);
}
}
class MyTest3 {
interface Bar1 {
int _(String s);
}
interface Bar2 {
int _(int s);
}
//void bar(Bar1 b1){}
void bar(Bar2 b1){}
/*static int foo(int i) {
return 0;
}*/
static int foo(String i) {
return 0;
}
{
<error descr="Incompatible types. Found: '<method reference>', required: 'todelete.MyTest3.Bar1'">Bar1 b1 = MyTest2 :: foo;</error>
bar<error descr="'bar(todelete.MyTest3.Bar2)' in 'todelete.MyTest3' cannot be applied to '(<method reference>)'">(MyTest3 :: foo)</error>;
}
}
class MyTest4 {
interface Bar1<T> {
int _(T s);
}
void bar(Bar1 b1){}
static int foo(int i) {
return 0;
}
static int foo(String i) {
return 0;
}
{
bar<error descr="'bar(todelete.MyTest4.Bar1)' in 'todelete.MyTest4' cannot be applied to '(<method reference>)'">(MyTest4:: foo)</error>;
}
}
class MyTest5 {
interface Bar1<T> {
int _(T s);
}
static <T1> void bar(Bar1<T1> b1){}
static <K> int foo(K k) {
return 0;
}
static int foo(String i) {
return 0;
}
{
//todo ambiguity rules checked MyTest5.<String>bar(MyTest5::foo);
}
}
@@ -28,6 +28,10 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testAssignability() throws Exception {
doTest();
}
public void testAmbiguity() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);