mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
method ref: initial ambiguity rules
This commit is contained in:
@@ -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 {
|
||||
|
||||
+39
-1
@@ -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,
|
||||
|
||||
+36
-15
@@ -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;
|
||||
|
||||
+3
-2
@@ -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) {
|
||||
|
||||
+156
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user