mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
method refs: infer from functional interface return types
This commit is contained in:
@@ -16,9 +16,11 @@
|
||||
package com.intellij.psi;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.infos.CandidateInfo;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
@@ -43,7 +45,7 @@ public class LambdaUtil {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiType getFunctionalInterfaceReturnType(PsiType functionalInterfaceType) {
|
||||
public static PsiType getFunctionalInterfaceReturnType(@Nullable PsiType functionalInterfaceType) {
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
|
||||
final PsiClass psiClass = resolveResult.getElement();
|
||||
if (psiClass != null) {
|
||||
@@ -596,15 +598,19 @@ public class LambdaUtil {
|
||||
final MethodSignature signature2 = ((PsiMethod)resolve).getSignature(subst);
|
||||
|
||||
final PsiType interfaceReturnType = getFunctionalInterfaceReturnType(left);
|
||||
final PsiType methodReturnType = subst.substitute(((PsiMethod)resolve).getReturnType());
|
||||
if (interfaceReturnType != null && methodReturnType != null && interfaceReturnType != PsiType.VOID &&
|
||||
!TypeConversionUtil.isAssignable(interfaceReturnType, methodReturnType)) return false;
|
||||
PsiType methodReturnType = subst.substitute(((PsiMethod)resolve).getReturnType());
|
||||
if (interfaceReturnType != null && interfaceReturnType != PsiType.VOID) {
|
||||
if (methodReturnType == null) {
|
||||
methodReturnType = JavaPsiFacade.getElementFactory(methodReferenceExpression.getProject()).createType(((PsiMethod)resolve).getContainingClass(), subst);
|
||||
}
|
||||
if (!TypeConversionUtil.isAssignable(interfaceReturnType, methodReturnType, false)) return false;
|
||||
}
|
||||
if (areAcceptable(signature1, signature2, classRef.get(), substRef.get(), ((PsiMethod)resolve).isVarArgs())) return true;
|
||||
} else if (resolve instanceof PsiClass) {
|
||||
final PsiType interfaceReturnType = getFunctionalInterfaceReturnType(left);
|
||||
if (interfaceReturnType != null) {
|
||||
final PsiClassType classType = JavaPsiFacade.getElementFactory(methodReferenceExpression.getProject()).createType((PsiClass)resolve, result.getSubstitutor());
|
||||
if (TypeConversionUtil.isAssignable(interfaceReturnType, classType)) {
|
||||
if (TypeConversionUtil.isAssignable(interfaceReturnType, classType, false)) {
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
if (parameters.length == 0) return true;
|
||||
if (parameters.length == 1) {
|
||||
@@ -751,6 +757,36 @@ public class LambdaUtil {
|
||||
return getFunctionalInterfaceReturnType(functionalInterfaceType);
|
||||
}
|
||||
|
||||
public static PsiSubstitutor inferFromReturnType(final PsiTypeParameter[] typeParameters,
|
||||
final PsiType returnType,
|
||||
@Nullable final PsiType interfaceMethodReturnType,
|
||||
PsiSubstitutor psiSubstitutor,
|
||||
final LanguageLevel languageLevel,
|
||||
final Project project) {
|
||||
if (interfaceMethodReturnType == null) return psiSubstitutor;
|
||||
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(project).getResolveHelper();
|
||||
for (PsiTypeParameter typeParameter : typeParameters) {
|
||||
final PsiType constraint = resolveHelper.getSubstitutionForTypeParameter(typeParameter, returnType, interfaceMethodReturnType, false, languageLevel);
|
||||
if (constraint != PsiType.NULL) {
|
||||
PsiType inferredType = null;
|
||||
final PsiClassType[] bounds = typeParameter.getExtendsListTypes();
|
||||
for (PsiClassType classTypeBound : bounds) {
|
||||
if (TypeConversionUtil.isAssignable(classTypeBound, constraint)) {
|
||||
inferredType = constraint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bounds.length == 0) {
|
||||
inferredType = constraint;
|
||||
}
|
||||
if (inferredType != null) {
|
||||
psiSubstitutor = psiSubstitutor.put(typeParameter, inferredType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return psiSubstitutor;
|
||||
}
|
||||
|
||||
private static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
|
||||
private PsiMethod myMethod;
|
||||
private final PsiClass myClass;
|
||||
|
||||
+71
-34
@@ -19,6 +19,7 @@ import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
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;
|
||||
@@ -235,7 +236,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
|
||||
final boolean beginsWithReferenceType = process(classRef, substRef);
|
||||
|
||||
final PsiClass containingClass = classRef.get();
|
||||
final PsiSubstitutor substitutor = substRef.get();
|
||||
PsiSubstitutor substitutor = substRef.get();
|
||||
|
||||
if (containingClass != null) {
|
||||
final PsiElement element = getReferenceNameElement();
|
||||
@@ -251,31 +252,31 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
|
||||
}
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
|
||||
final PsiType interfaceMethodReturnType = LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType);
|
||||
final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(PsiMethodReferenceExpressionImpl.this);
|
||||
if (isConstructor && interfaceMethod != null) {
|
||||
final PsiTypeParameter[] typeParameters = containingClass.getTypeParameters();
|
||||
final boolean isRawSubst = PsiUtil.isRawSubstitutor(containingClass, substitutor);
|
||||
final PsiClassType returnType = JavaPsiFacade.getElementFactory(getProject()).createType(containingClass,
|
||||
isRawSubst ? PsiSubstitutor.EMPTY : substitutor);
|
||||
|
||||
substitutor = LambdaUtil.inferFromReturnType(typeParameters, returnType, interfaceMethodReturnType, substitutor, languageLevel,
|
||||
PsiMethodReferenceExpressionImpl.this.getProject());
|
||||
|
||||
if (containingClass.getConstructors().length == 0 &&
|
||||
!containingClass.isEnum() &&
|
||||
!containingClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
return new JavaResolveResult[]{new ClassCandidateInfo(containingClass, substitutor)};
|
||||
}
|
||||
}
|
||||
|
||||
final MethodSignature signature = interfaceMethod != null ? interfaceMethod.getSignature(resolveResult.getSubstitutor()) : null;
|
||||
final MethodReferenceConflictResolver conflictResolver =
|
||||
new MethodReferenceConflictResolver(containingClass, substitutor, signature, beginsWithReferenceType);
|
||||
final PsiConflictResolver[] resolvers;
|
||||
if (signature != null) {
|
||||
if (isConstructor && containingClass.getConstructors().length == 0 && !containingClass.isEnum() && !containingClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
return new JavaResolveResult[]{new ClassCandidateInfo(containingClass, substitutor)};
|
||||
}
|
||||
final PsiType[] parameterTypes = signature.getParameterTypes();
|
||||
resolvers = new PsiConflictResolver[]{conflictResolver,
|
||||
new JavaMethodsConflictResolver(PsiMethodReferenceExpressionImpl.this, parameterTypes) {
|
||||
@Override
|
||||
public CandidateInfo resolveConflict(List<CandidateInfo> conflicts) {
|
||||
boolean varargs = false;
|
||||
for (CandidateInfo conflict : conflicts) {
|
||||
final PsiElement psiElement = conflict.getElement();
|
||||
if (psiElement instanceof PsiMethod && ((PsiMethod)psiElement).isVarArgs()) {
|
||||
varargs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
checkSpecifics(conflicts, varargs ? MethodCandidateInfo.ApplicabilityLevel.VARARGS : MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY);
|
||||
return conflicts.size() == 1 ? conflicts.get(0) : null;
|
||||
}
|
||||
}};
|
||||
resolvers = new PsiConflictResolver[]{conflictResolver, new MethodRefsSpecificResolver(parameterTypes)};
|
||||
}
|
||||
else {
|
||||
resolvers = new PsiConflictResolver[]{conflictResolver};
|
||||
@@ -284,26 +285,16 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
|
||||
new MethodCandidatesProcessor(PsiMethodReferenceExpressionImpl.this, resolvers, new SmartList<CandidateInfo>()) {
|
||||
@Override
|
||||
protected MethodCandidateInfo createCandidateInfo(final PsiMethod method,
|
||||
PsiSubstitutor substitutor,
|
||||
boolean staticProblem,
|
||||
boolean accessible) {
|
||||
final PsiSubstitutor substitutor,
|
||||
final boolean staticProblem,
|
||||
final boolean accessible) {
|
||||
final PsiExpressionList argumentList = getArgumentList();
|
||||
return new MethodCandidateInfo(method, substitutor, !accessible, staticProblem, argumentList, myCurrentFileContext,
|
||||
argumentList != null ? argumentList.getExpressionTypes() : null, getTypeArguments(),
|
||||
getLanguageLevel()) {
|
||||
@Override
|
||||
public PsiSubstitutor inferTypeArguments(ParameterTypeInferencePolicy policy) {
|
||||
if (signature == null) return PsiSubstitutor.EMPTY;
|
||||
final PsiType[] types = method.getSignature(PsiSubstitutor.EMPTY).getParameterTypes();
|
||||
final PsiType[] rightTypes = signature.getParameterTypes();
|
||||
if (types.length < rightTypes.length) {
|
||||
return PsiUtil.resolveGenericsClassInType(rightTypes[0]).getSubstitutor();
|
||||
} else if (types.length > rightTypes.length) {
|
||||
return PsiUtil.resolveGenericsClassInType(types[0]).getSubstitutor();
|
||||
}
|
||||
return JavaPsiFacade.getInstance(getProject()).getResolveHelper()
|
||||
.inferTypeArguments(method.getTypeParameters(), types, rightTypes,
|
||||
PsiUtil.getLanguageLevel(PsiMethodReferenceExpressionImpl.this));
|
||||
return inferTypeArgumentsFromInterfaceMethod(signature, interfaceMethodReturnType, method, substitutor, languageLevel);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -337,6 +328,31 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
|
||||
return JavaResolveResult.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
private PsiSubstitutor inferTypeArgumentsFromInterfaceMethod(@Nullable MethodSignature signature,
|
||||
@Nullable PsiType interfaceMethodReturnType,
|
||||
PsiMethod method,
|
||||
PsiSubstitutor substitutor,
|
||||
LanguageLevel languageLevel) {
|
||||
if (signature == null) return PsiSubstitutor.EMPTY;
|
||||
final PsiType[] types = method.getSignature(substitutor).getParameterTypes();
|
||||
final PsiType[] rightTypes = signature.getParameterTypes();
|
||||
if (types.length < rightTypes.length) {
|
||||
return PsiUtil.resolveGenericsClassInType(rightTypes[0]).getSubstitutor();
|
||||
} else if (types.length > rightTypes.length) {
|
||||
return PsiUtil.resolveGenericsClassInType(types[0]).getSubstitutor();
|
||||
}
|
||||
|
||||
PsiSubstitutor psiSubstitutor = JavaPsiFacade.getInstance(getProject()).getResolveHelper().inferTypeArguments(
|
||||
method.getTypeParameters(), types, rightTypes, languageLevel);
|
||||
psiSubstitutor = psiSubstitutor.putAll(substitutor);
|
||||
|
||||
return LambdaUtil.inferFromReturnType(method.getTypeParameters(),
|
||||
psiSubstitutor.substitute(method.getReturnType()),
|
||||
interfaceMethodReturnType,
|
||||
psiSubstitutor,
|
||||
languageLevel, PsiMethodReferenceExpressionImpl.this.getProject());
|
||||
}
|
||||
|
||||
private class MethodReferenceConflictResolver implements PsiConflictResolver {
|
||||
private final PsiClass myContainingClass;
|
||||
private final PsiSubstitutor mySubstitutor;
|
||||
@@ -418,5 +434,26 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
|
||||
return !firstCandidates.isEmpty() ? firstCandidates.get(0) : secondCandidates.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
private class MethodRefsSpecificResolver extends JavaMethodsConflictResolver {
|
||||
public MethodRefsSpecificResolver(PsiType[] parameterTypes) {
|
||||
super(PsiMethodReferenceExpressionImpl.this, parameterTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CandidateInfo resolveConflict(List<CandidateInfo> conflicts) {
|
||||
boolean varargs = false;
|
||||
for (CandidateInfo conflict : conflicts) {
|
||||
final PsiElement psiElement = conflict.getElement();
|
||||
if (psiElement instanceof PsiMethod && ((PsiMethod)psiElement).isVarArgs()) {
|
||||
varargs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
checkSpecifics(conflicts,
|
||||
varargs ? MethodCandidateInfo.ApplicabilityLevel.VARARGS : MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY);
|
||||
return conflicts.size() == 1 ? conflicts.get(0) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
class MyTestDefaultConstructor {
|
||||
static class SuperFoo<<warning descr="Type parameter 'X' is never used">X</warning>> { }
|
||||
|
||||
static class Foo<X extends Number> extends SuperFoo<X> {
|
||||
}
|
||||
|
||||
interface I1 {
|
||||
SuperFoo<String> _();
|
||||
}
|
||||
|
||||
interface I2 {
|
||||
SuperFoo<Integer> _();
|
||||
}
|
||||
|
||||
interface I3 {
|
||||
SuperFoo<Object> _();
|
||||
}
|
||||
|
||||
private static void <warning descr="Private method 'foo(MyTestDefaultConstructor.I1)' is never used">foo</warning>(I1 i) {System.out.println(i);}
|
||||
private static void foo(I2 i) {System.out.println(i);}
|
||||
private static void <warning descr="Private method 'foo(MyTestDefaultConstructor.I3)' is never used">foo</warning>(I3 i) {System.out.println(i);}
|
||||
|
||||
static {
|
||||
foo(Foo::new);
|
||||
}
|
||||
}
|
||||
|
||||
class MyTestConstructor {
|
||||
static class SuperFoo<<warning descr="Type parameter 'X' is never used">X</warning>> { }
|
||||
|
||||
static class Foo<X extends Number> extends SuperFoo<X> {
|
||||
Foo(){}
|
||||
}
|
||||
|
||||
interface I1 {
|
||||
SuperFoo<String> _();
|
||||
}
|
||||
|
||||
interface I2 {
|
||||
SuperFoo<Integer> _();
|
||||
}
|
||||
|
||||
interface I3 {
|
||||
SuperFoo<Object> _();
|
||||
}
|
||||
|
||||
private static void <warning descr="Private method 'foo(MyTestConstructor.I1)' is never used">foo</warning>(I1 i) {System.out.println(i);}
|
||||
private static void foo(I2 i) {System.out.println(i);}
|
||||
private static void <warning descr="Private method 'foo(MyTestConstructor.I3)' is never used">foo</warning>(I3 i) {System.out.println(i);}
|
||||
|
||||
static {
|
||||
foo(Foo::new);
|
||||
}
|
||||
}
|
||||
|
||||
class MyTestMethod {
|
||||
static class SuperFoo<<warning descr="Type parameter 'X' is never used">X</warning>> { }
|
||||
|
||||
static class Foo<X extends Number> extends SuperFoo<X> {
|
||||
}
|
||||
|
||||
interface I1 {
|
||||
SuperFoo<String> _();
|
||||
}
|
||||
|
||||
interface I2 {
|
||||
SuperFoo<Integer> _();
|
||||
}
|
||||
|
||||
interface I3 {
|
||||
SuperFoo<Object> _();
|
||||
}
|
||||
|
||||
static <X extends Number> Foo<X> m() { return null; }
|
||||
|
||||
private static void <warning descr="Private method 'foo(MyTestMethod.I1)' is never used">foo</warning>(I1 i) {System.out.println(i);}
|
||||
private static void foo(I2 i) {System.out.println(i);}
|
||||
private static void <warning descr="Private method 'foo(MyTestMethod.I3)' is never used">foo</warning>(I3 i) {System.out.println(i);}
|
||||
|
||||
static {
|
||||
foo(MyTestMethod::m);
|
||||
}
|
||||
}
|
||||
+4
@@ -105,6 +105,10 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testInferenceFromReturnType() throws Exception {
|
||||
doTest(true);
|
||||
}
|
||||
|
||||
public void testReturnTypeSpecific() throws Exception {
|
||||
doTest(true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user