mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-21 22:11:40 +07:00
method reference: additional diagnostics for invalid method references (IDEA-149688)
This commit is contained in:
@@ -1343,26 +1343,29 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
!((MethodCandidateInfo)results[0]).isApplicable() &&
|
||||
expression.getFunctionalInterfaceType() != null) {
|
||||
String description = null;
|
||||
if (results.length == 1) {
|
||||
description = ((MethodCandidateInfo)results[0]).getInferenceErrorMessage();
|
||||
}
|
||||
if (expression.isConstructor()) {
|
||||
final PsiClass containingClass = PsiMethodReferenceUtil.getQualifierResolveResult(expression).getContainingClass();
|
||||
|
||||
if (containingClass != null) {
|
||||
if (!myHolder.add(HighlightClassUtil.checkInstantiationOfAbstractClass(containingClass, expression)) &&
|
||||
!myHolder.add(GenericsHighlightUtil.checkEnumInstantiation(expression, containingClass)) &&
|
||||
containingClass.isPhysical()) {
|
||||
containingClass.isPhysical() &&
|
||||
description == null) {
|
||||
description = JavaErrorMessages.message("cannot.resolve.constructor", containingClass.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
else if (description == null){
|
||||
description = JavaErrorMessages.message("cannot.resolve.method", expression.getReferenceName());
|
||||
}
|
||||
|
||||
if (description != null) {
|
||||
final PsiElement referenceNameElement = expression.getReferenceNameElement();
|
||||
final HighlightInfo highlightInfo =
|
||||
HighlightInfo.newHighlightInfo(results.length == 0 ? HighlightInfoType.WRONG_REF
|
||||
: HighlightInfoType.ERROR)
|
||||
HighlightInfo.newHighlightInfo(results.length == 0 ? HighlightInfoType.WRONG_REF : HighlightInfoType.ERROR)
|
||||
.descriptionAndTooltip(description).range(referenceNameElement).create();
|
||||
myHolder.add(highlightInfo);
|
||||
final TextRange fixRange = HighlightMethodUtil.getFixRange(referenceNameElement);
|
||||
|
||||
@@ -54,28 +54,6 @@ public class PsiMethodReferenceUtil {
|
||||
isSecondSearchPossible(functionalMethodParameterTypes, qualifierResolveResult, methodRef);
|
||||
}
|
||||
|
||||
public static boolean isCorrectAssignment(PsiType[] parameterTypes,
|
||||
PsiType[] argTypes,
|
||||
boolean varargs,
|
||||
int offset) {
|
||||
final int min = Math.min(parameterTypes.length, argTypes.length - offset);
|
||||
for (int i = 0; i < min; i++) {
|
||||
final PsiType argType = argTypes[i + offset];
|
||||
PsiType parameterType = parameterTypes[i];
|
||||
parameterType = GenericsUtil.getVariableTypeByExpressionType(parameterType, true);
|
||||
if (varargs && i == parameterTypes.length - 1) {
|
||||
if (!TypeConversionUtil.isAssignable(parameterType, argType) &&
|
||||
!TypeConversionUtil.isAssignable(((PsiArrayType)parameterType).getComponentType(), argType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!TypeConversionUtil.isAssignable(parameterType, argType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !varargs || parameterTypes.length - 1 <= argTypes.length - offset;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiType getQualifierType(PsiMethodReferenceExpression expression) {
|
||||
PsiType qualifierType = null;
|
||||
|
||||
@@ -140,7 +140,7 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
|
||||
final PsiType[] argTypes = signature.getParameterTypes();
|
||||
boolean hasReceiver = PsiMethodReferenceUtil.isSecondSearchPossible(argTypes, qualifierResolveResult, reference);
|
||||
|
||||
return MethodReferenceConflictResolver.isApplicableByFirstSearch(this, argTypes, hasReceiver, interfaceMethod.isVarArgs()) != null;
|
||||
return MethodReferenceConflictResolver.isApplicableByFirstSearch(this, argTypes, hasReceiver, reference, interfaceMethod.isVarArgs()) != null;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -237,7 +237,7 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
|
||||
|
||||
for (CandidateInfo conflict : conflicts) {
|
||||
if (!(conflict instanceof MethodCandidateInfo)) continue;
|
||||
final Boolean applicableByFirstSearch = isApplicableByFirstSearch(conflict, argTypes, hasReceiver, myFunctionalMethodVarArgs);
|
||||
final Boolean applicableByFirstSearch = isApplicableByFirstSearch(conflict, argTypes, hasReceiver, myReferenceExpression, myFunctionalMethodVarArgs);
|
||||
if (applicableByFirstSearch != null) {
|
||||
(applicableByFirstSearch ? firstCandidates : secondCandidates).add(conflict);
|
||||
}
|
||||
@@ -275,9 +275,12 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Boolean isApplicableByFirstSearch(CandidateInfo conflict, PsiType[] argTypes,
|
||||
boolean hasReceiver,
|
||||
boolean functionalMethodVarArgs) {
|
||||
private static Boolean isApplicableByFirstSearch(CandidateInfo conflict,
|
||||
PsiType[] functionalInterfaceParamTypes,
|
||||
boolean hasReceiver,
|
||||
PsiMethodReferenceExpression referenceExpression,
|
||||
boolean functionalMethodVarArgs) {
|
||||
|
||||
final PsiMethod psiMethod = ((MethodCandidateInfo)conflict).getElement();
|
||||
|
||||
final PsiSubstitutor substitutor = ((MethodCandidateInfo)conflict).getSubstitutor(false);
|
||||
@@ -288,19 +291,55 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((varargs || argTypes.length == parameterTypes.length) &&
|
||||
PsiMethodReferenceUtil.isCorrectAssignment(parameterTypes, argTypes, varargs, 0)) {
|
||||
if ((varargs || functionalInterfaceParamTypes.length == parameterTypes.length) &&
|
||||
isCorrectAssignment(parameterTypes, functionalInterfaceParamTypes, varargs, referenceExpression, conflict, 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasReceiver &&
|
||||
(varargs || argTypes.length == parameterTypes.length + 1) &&
|
||||
PsiMethodReferenceUtil.isCorrectAssignment(parameterTypes, argTypes, varargs, 1)) {
|
||||
(varargs || functionalInterfaceParamTypes.length == parameterTypes.length + 1) &&
|
||||
isCorrectAssignment(parameterTypes, functionalInterfaceParamTypes, varargs, referenceExpression, conflict, 1)) {
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isCorrectAssignment(PsiType[] parameterTypes,
|
||||
PsiType[] functionalInterfaceParamTypes,
|
||||
boolean varargs,
|
||||
PsiMethodReferenceExpression referenceExpression,
|
||||
CandidateInfo conflict,
|
||||
int offset) {
|
||||
final int min = Math.min(parameterTypes.length, functionalInterfaceParamTypes.length - offset);
|
||||
for (int i = 0; i < min; i++) {
|
||||
final PsiType argType = PsiUtil.captureToplevelWildcards(functionalInterfaceParamTypes[i + offset], referenceExpression);
|
||||
final PsiType parameterType = parameterTypes[i];
|
||||
if (varargs && i == parameterTypes.length - 1) {
|
||||
if (!TypeConversionUtil.isAssignable(parameterType, argType) &&
|
||||
!TypeConversionUtil.isAssignable(((PsiArrayType)parameterType).getComponentType(), argType)) {
|
||||
reportParameterConflict(referenceExpression, conflict, argType, parameterType);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!TypeConversionUtil.isAssignable(parameterType, argType)) {
|
||||
reportParameterConflict(referenceExpression, conflict, argType, parameterType);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !varargs || parameterTypes.length - 1 <= functionalInterfaceParamTypes.length - offset;
|
||||
}
|
||||
|
||||
private static void reportParameterConflict(PsiMethodReferenceExpression referenceExpression,
|
||||
CandidateInfo conflict,
|
||||
PsiType argType,
|
||||
PsiType parameterType) {
|
||||
if (conflict instanceof MethodCandidateInfo) {
|
||||
((MethodCandidateInfo)conflict).setInferenceError("Invalid " +
|
||||
(referenceExpression.isConstructor() ? "constructor" :"method") +
|
||||
" reference: " + argType.getPresentableText() + " cannot be converted to " + parameterType.getPresentableText());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean resolveConflicts(List<CandidateInfo> firstCandidates, List<CandidateInfo> secondCandidates, int applicabilityLevel) {
|
||||
|
||||
final int firstApplicability = checkApplicability(firstCandidates);
|
||||
|
||||
@@ -54,7 +54,7 @@ class MyTest1 {
|
||||
}
|
||||
|
||||
{
|
||||
Bar1 b1 = MyTest2 :: <error descr="Cannot resolve method 'foo'">foo</error>;
|
||||
Bar1 b1 = MyTest2 :: <error descr="Invalid method reference: String cannot be converted to int">foo</error>;
|
||||
bar(MyTest1 :: foo);
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ class MyTest2 {
|
||||
}*/
|
||||
|
||||
{
|
||||
Bar1 b1 = MyTest2 :: <error descr="Cannot resolve method 'foo'">foo</error>;
|
||||
Bar1 b1 = MyTest2 :: <error descr="Invalid method reference: String cannot be converted to int">foo</error>;
|
||||
bar(MyTest2 :: foo);
|
||||
}
|
||||
}
|
||||
@@ -106,8 +106,8 @@ class MyTest3 {
|
||||
}
|
||||
|
||||
{
|
||||
Bar1 b1 = MyTest2 :: <error descr="Cannot resolve method 'foo'">foo</error>;
|
||||
bar(MyTest3 :: <error descr="Cannot resolve method 'foo'">foo</error>);
|
||||
Bar1 b1 = MyTest2 :: <error descr="Invalid method reference: String cannot be converted to int">foo</error>;
|
||||
bar(MyTest3 :: <error descr="Invalid method reference: int cannot be converted to String">foo</error>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ class Test {
|
||||
{
|
||||
Runnable b = Test :: <error descr="Cannot resolve method 'length'">length</error>;
|
||||
Comparable<String> c = Test :: length;
|
||||
Comparable<Integer> c1 = Test :: <error descr="Cannot resolve method 'length'">length</error>;
|
||||
Comparable<Integer> c1 = Test :: <error descr="Invalid method reference: Integer cannot be converted to String">length</error>;
|
||||
}
|
||||
|
||||
public static Integer length(String s) {
|
||||
|
||||
@@ -49,7 +49,7 @@ class MyTest {
|
||||
static {
|
||||
I1 i1 = MyTest::static_1;
|
||||
I1 i2 = MyTest::<error descr="Cannot resolve method 'static_2'">static_2</error>;
|
||||
I1 i3 = MyTest::<error descr="Cannot resolve method 'static_3'">static_3</error>;
|
||||
I1 i3 = MyTest::<error descr="Invalid method reference: int cannot be converted to String">static_3</error>;
|
||||
I1 i4 = MyTest::<error descr="Cannot resolve method 'static_4'">static_4</error>;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ class MyTest {
|
||||
|
||||
I1 i1 = this::_1;
|
||||
I1 i2 = this::<error descr="Cannot resolve method '_2'">_2</error>;
|
||||
I1 i3 = this::<error descr="Cannot resolve method '_3'">_3</error>;
|
||||
I1 i3 = this::<error descr="Invalid method reference: int cannot be converted to String">_3</error>;
|
||||
I1 i4 = this::<error descr="Cannot resolve method '_4'">_4</error>;
|
||||
|
||||
I2 i21 = MyTest::<error descr="Cannot resolve method 'm1'">m1</error>;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
class A {
|
||||
static class B<T> {
|
||||
int foo(T x) {return 0;}
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
B<? extends CharSequence> q = new B<>();
|
||||
|
||||
Func x = q::<error descr="Invalid method reference: CharSequence cannot be converted to capture of ? extends CharSequence">foo</error>;
|
||||
x.invoke("");
|
||||
}
|
||||
|
||||
interface Func {
|
||||
int invoke(CharSequence x);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ interface B<BT> {
|
||||
|
||||
class Test {
|
||||
public static void test() {
|
||||
method1(Test::<error descr="Cannot resolve method 'method2'">method2</error>);
|
||||
method1(Test::<error descr="Invalid method reference: A<capture of ? super M> cannot be converted to A<? super String>">method2</error>);
|
||||
}
|
||||
|
||||
static <M> void method1(B<A<? super M>> arg) { }
|
||||
|
||||
@@ -29,14 +29,14 @@ class Test {
|
||||
static void meth4(I3 s) { }
|
||||
|
||||
static {
|
||||
meth1(Foo::<error descr="Cannot resolve constructor 'Foo'">new</error>);
|
||||
meth1(Foo::<error descr="Invalid constructor reference: String cannot be converted to X">new</error>);
|
||||
meth2(Foo::new);
|
||||
meth3(Foo::<error descr="Cannot resolve constructor 'Foo'">new</error>);
|
||||
meth3(Foo::<error descr="Invalid constructor reference: Object cannot be converted to X">new</error>);
|
||||
meth4<error descr="Ambiguous method call: both 'Test.meth4(I1)' and 'Test.meth4(I2)' match">(Foo::new)</error>;
|
||||
|
||||
meth1(Test::<error descr="Cannot resolve method 'foo'">foo</error>);
|
||||
meth1(Test::<error descr="Invalid method reference: String cannot be converted to X">foo</error>);
|
||||
meth2(Test::foo);
|
||||
meth3(Test::<error descr="Cannot resolve method 'foo'">foo</error>);
|
||||
meth3(Test::<error descr="Invalid method reference: Object cannot be converted to X">foo</error>);
|
||||
meth4<error descr="Ambiguous method call: both 'Test.meth4(I1)' and 'Test.meth4(I2)' match">(Test::foo)</error>;
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ class Test {
|
||||
}
|
||||
|
||||
void test() {
|
||||
II1 i1 = this::<error descr="Cannot resolve method 'fooInstance'">fooInstance</error>;
|
||||
II1 i1 = this::<error descr="Invalid method reference: X cannot be converted to X">fooInstance</error>;
|
||||
II2 i2 = this::fooInstance;
|
||||
II3 i3 = this::<error descr="Cannot resolve method 'fooInstance'">fooInstance</error>;
|
||||
II3 i3 = this::<error descr="Invalid method reference: X cannot be converted to X">fooInstance</error>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ abstract class Test {
|
||||
|
||||
{
|
||||
I i = Test::<String>foo;
|
||||
I i1 = Test::<Integer><error descr="Cannot resolve method 'foo'">foo</error>;
|
||||
I i1 = Test::<Integer><error descr="Invalid method reference: String cannot be converted to Integer">foo</error>;
|
||||
I i2 = Test::foo;
|
||||
|
||||
}
|
||||
|
||||
@@ -474,6 +474,10 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testApplicabilityConflictMessage() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user