[java-inspections] JavaReflectionInvocationInspection: better message and range when number of arguments differs

Fixes IDEA-171801 Make "Reflective invocation arguments mismatch" more verbose

GitOrigin-RevId: 586b21e543959f53a082d6b0a1fdcb28c0f30039
This commit is contained in:
Tagir Valeev
2024-10-17 14:08:04 +00:00
committed by intellij-monorepo-bot
parent 3dea51a1c1
commit 9bcd5c38ea
4 changed files with 43 additions and 33 deletions
@@ -4,6 +4,7 @@ package com.intellij.codeInspection.reflectiveAccess;
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.java.JavaBundle;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.impl.source.resolve.reference.impl.JavaLangClassMemberReference;
@@ -65,10 +66,11 @@ public final class JavaReflectionInvocationInspection extends AbstractBaseJavaLo
getRequiredMethodArguments(methodCall.getMethodExpression().getQualifierExpression(), argumentOffset, methodPredicate);
if (requiredTypes != null) {
final PsiExpressionList argumentList = methodCall.getArgumentList();
final Arguments actualArguments = getActualMethodArguments(argumentList.getExpressions(), argumentOffset,
final Arguments actualArguments = getActualMethodArguments(argumentList.getExpressions(), argumentOffset,
MethodCallUtils.isVarArgCall(methodCall));
if (actualArguments != null) {
if (requiredTypes.size() != actualArguments.expressions.length) {
PsiExpression[] actualExpressions = actualArguments.expressions;
if (requiredTypes.size() != actualExpressions.length) {
if (actualArguments.varargAsArray) {
final PsiExpression[] expressions = argumentList.getExpressions();
final PsiElement element = expressions.length == argumentOffset + 1 ? expressions[argumentOffset] : argumentList;
@@ -76,8 +78,15 @@ public final class JavaReflectionInvocationInspection extends AbstractBaseJavaLo
"inspection.reflection.invocation.item.count", requiredTypes.size()));
}
else {
holder.registerProblem(argumentList, JavaBundle.message(
"inspection.reflection.invocation.argument.count", requiredTypes.size() + argumentOffset));
if (actualExpressions.length > 0) {
TextRange range =
actualExpressions[0].getTextRangeInParent().union(actualExpressions[actualExpressions.length - 1].getTextRangeInParent());
holder.registerProblem(argumentList, range, JavaBundle.message(
"inspection.reflection.invocation.reflective.argument.count", requiredTypes.size()));
} else {
holder.registerProblem(argumentList, JavaBundle.message(
"inspection.reflection.invocation.reflective.argument.count", requiredTypes.size()));
}
}
return;
}
@@ -85,7 +94,7 @@ public final class JavaReflectionInvocationInspection extends AbstractBaseJavaLo
for (int i = 0; i < requiredTypes.size(); i++) {
final ReflectiveType requiredType = getReflectiveType(requiredTypes.get(i));
if (requiredType != null) {
final PsiExpression argument = actualArguments.expressions[i];
final PsiExpression argument = actualExpressions[i];
if (argument != null) {
PsiType actualType = argument.getType();
if (TypeUtils.isJavaLangObject(actualType) && !requiredType.isAssignableFrom(actualType) &&
@@ -107,7 +116,7 @@ public final class JavaReflectionInvocationInspection extends AbstractBaseJavaLo
final PsiExpression[] expressions = argumentList.getExpressions();
final PsiElement element = expressions.length == argumentOffset + 1 ? expressions[argumentOffset] : argumentList;
holder.registerProblem(element, JavaBundle.message(
"inspection.reflection.invocation.array.not.assignable", actualArguments.expressions.length));
"inspection.reflection.invocation.array.not.assignable", actualExpressions.length));
break;
}
}
@@ -10,17 +10,17 @@ class ConstructorParamCount {
Constructor c2 = cls.getConstructor(int.class, String.class);
Constructor c3 = cls.getConstructor(int.class, String.class, String.class);
c1.newInstance<warning descr="One argument is expected">(42, "abc")</warning>;
c1.newInstance(<warning descr="Reflectively called method requires one argument">42, "abc"</warning>);
c2.newInstance(42, "abc");
c3.newInstance<warning descr="3 arguments are expected">(42, "abc")</warning>;
c3.newInstance(<warning descr="Reflectively called method requires 3 arguments">42, "abc"</warning>);
c1.newInstance(<warning descr="Single-item array is expected">new Object[]{42, "abc"}</warning>);
c2.newInstance(new Object[]{42, "abc"});
c3.newInstance(<warning descr="3 array items are expected">new Object[]{42, "abc"}</warning>);
c1.newInstance<warning descr="One argument is expected">()</warning>;
c2.newInstance<warning descr="2 arguments are expected">()</warning>;
c3.newInstance<warning descr="3 arguments are expected">()</warning>;
c1.newInstance<warning descr="Reflectively called method requires one argument">()</warning>;
c2.newInstance<warning descr="Reflectively called method requires 2 arguments">()</warning>;
c3.newInstance<warning descr="Reflectively called method requires 3 arguments">()</warning>;
cls.getConstructor(String.class).newInstance(new String[] {"abc"});
}
@@ -32,17 +32,17 @@ class ConstructorParamCount {
Constructor c2 = cls.getConstructor(new Class[]{int.class, String.class});
Constructor c3 = cls.getConstructor(new Class[]{int.class, String.class, String.class});
c1.newInstance<warning descr="One argument is expected">(42, "abc")</warning>;
c1.newInstance(<warning descr="Reflectively called method requires one argument">42, "abc"</warning>);
c2.newInstance(42, "abc");
c3.newInstance<warning descr="3 arguments are expected">(42, "abc")</warning>;
c3.newInstance(<warning descr="Reflectively called method requires 3 arguments">42, "abc"</warning>);
c1.newInstance(<warning descr="Single-item array is expected">new Object[]{42, "abc"}</warning>);
c2.newInstance(new Object[]{42, "abc"});
c3.newInstance(<warning descr="3 array items are expected">new Object[]{42, "abc"}</warning>);
c1.newInstance<warning descr="One argument is expected">()</warning>;
c2.newInstance<warning descr="2 arguments are expected">()</warning>;
c3.newInstance<warning descr="3 arguments are expected">()</warning>;
c1.newInstance<warning descr="Reflectively called method requires one argument">()</warning>;
c2.newInstance<warning descr="Reflectively called method requires 2 arguments">()</warning>;
c3.newInstance<warning descr="Reflectively called method requires 3 arguments">()</warning>;
cls.getConstructor(new Class[]{String.class}).newInstance(new String[] {"abc"});
}
@@ -57,7 +57,7 @@ class ConstructorParamCount {
Constructor m4 = cls.getConstructor(int.class, short.class, long.class, float.class);
Constructor m5 = cls.getConstructor(int.class, short.class, long.class, float.class, double.class);
m0.newInstance<warning descr="No arguments are expected">(0, 0)</warning>;
m0.newInstance(<warning descr="Reflectively called method requires no arguments">0, 0</warning>);
m1.newInstance(<warning descr="Argument is not assignable to 'int'">"abc"</warning>);
m2.newInstance(0, <warning descr="Argument is not assignable to 'short'">"abc"</warning>);
m3.newInstance(0, <warning descr="Argument is not assignable to 'short'">0</warning>, <warning descr="Argument is not assignable to 'long'">"abc"</warning>);
@@ -9,17 +9,17 @@ class MethodParamCount {
Method m2 = cls.getMethod("bar", int.class, String.class);
Method m3 = cls.getMethod("bar", int.class, String.class, String.class);
m1.invoke<warning descr="2 arguments are expected">(obj, 42, "abc")</warning>;
m1.invoke(obj, <warning descr="Reflectively called method requires one argument">42, "abc"</warning>);
m2.invoke(obj, 42, "abc");
m3.invoke<warning descr="4 arguments are expected">(obj, 42, "abc")</warning>;
m3.invoke(obj, <warning descr="Reflectively called method requires 3 arguments">42, "abc"</warning>);
m1.invoke(obj, <warning descr="Single-item array is expected">new Object[]{42, "abc"}</warning>);
m2.invoke(obj, new Object[]{42, "abc"});
m3.invoke(obj, <warning descr="3 array items are expected">new Object[]{42, "abc"}</warning>);
m1.invoke<warning descr="2 arguments are expected">(obj)</warning>;
m2.invoke<warning descr="3 arguments are expected">(obj)</warning>;
m3.invoke<warning descr="4 arguments are expected">(obj)</warning>;
m1.invoke<warning descr="Reflectively called method requires one argument">(obj)</warning>;
m2.invoke<warning descr="Reflectively called method requires 2 arguments">(obj)</warning>;
m3.invoke<warning descr="Reflectively called method requires 3 arguments">(obj)</warning>;
cls.getMethod("str", String.class).invoke(null, new String[] {"abc"});
}
@@ -32,17 +32,17 @@ class MethodParamCount {
Method m2 = cls.getMethod("bar", new Class[]{int.class, String.class});
Method m3 = cls.getMethod("bar", new Class[]{int.class, String.class, String.class});
m1.invoke<warning descr="2 arguments are expected">(obj, 42, "abc")</warning>;
m1.invoke(obj, <warning descr="Reflectively called method requires one argument">42, "abc"</warning>);
m2.invoke(obj, 42, "abc");
m3.invoke<warning descr="4 arguments are expected">(obj, 42, "abc")</warning>;
m3.invoke(obj, <warning descr="Reflectively called method requires 3 arguments">42, "abc"</warning>);
m1.invoke(obj, <warning descr="Single-item array is expected">new Object[]{42, "abc"}</warning>);
m2.invoke(obj, new Object[]{42, "abc"});
m3.invoke(obj, <warning descr="3 array items are expected">new Object[]{42, "abc"}</warning>);
m1.invoke<warning descr="2 arguments are expected">(obj)</warning>;
m2.invoke<warning descr="3 arguments are expected">(obj)</warning>;
m3.invoke<warning descr="4 arguments are expected">(obj)</warning>;
m1.invoke<warning descr="Reflectively called method requires one argument">(obj)</warning>;
m2.invoke<warning descr="Reflectively called method requires 2 arguments">(obj)</warning>;
m3.invoke<warning descr="Reflectively called method requires 3 arguments">(obj)</warning>;
cls.getMethod("str", new Class[]{String.class}).invoke(null, new String[] {"abc"});
}
@@ -58,12 +58,12 @@ class MethodParamCount {
Method m4 = cls.getMethod("m4", int.class, short.class, long.class, float.class);
Method m5 = cls.getMethod("m5", int.class, short.class, long.class, float.class, double.class);
m0.invoke<warning descr="One argument is expected">(0, 0)</warning>;
m1.invoke<warning descr="2 arguments are expected">("abc")</warning>;
m2.invoke<warning descr="3 arguments are expected">(0, "abc")</warning>;
m3.invoke<warning descr="4 arguments are expected">(0, 0, "abc")</warning>;
m4.invoke<warning descr="5 arguments are expected">(0, 0, 0, "abc")</warning>;
m5.invoke<warning descr="6 arguments are expected">(0, 0, 0, 0, "abc")</warning>;
m0.invoke(0, <warning descr="Reflectively called method requires no arguments">0</warning>);
m1.invoke<warning descr="Reflectively called method requires one argument">("abc")</warning>;
m2.invoke(0, <warning descr="Reflectively called method requires 2 arguments">"abc"</warning>);
m3.invoke(0, <warning descr="Reflectively called method requires 3 arguments">0, "abc"</warning>);
m4.invoke(0, <warning descr="Reflectively called method requires 4 arguments">0, 0, "abc"</warning>);
m5.invoke(0, <warning descr="Reflectively called method requires 5 arguments">0, 0, 0, "abc"</warning>);
Method m = cls.getMethod("m0", new Class[0]);
m.invoke(obj, new Object[0]);
@@ -660,6 +660,7 @@ inspection.reflect.handle.invocation.result.not.exact=Should be cast to ''{0}''
inspection.reflect.handle.invocation.result.null=Returned value is always 'null'
inspection.reflect.handle.invocation.result.void=Return type is 'void'
inspection.reflection.invocation.argument.count={0,choice,0#No arguments are|1#One argument is|1<{0} arguments are} expected
inspection.reflection.invocation.reflective.argument.count=Reflectively called method requires {0,choice,0#no arguments|1#one argument|1<{0} arguments}
inspection.reflection.invocation.argument.not.assignable=Argument is not assignable to ''{0}''
inspection.reflection.invocation.array.not.assignable=Array {0,choice,1#item has|1<items have} incompatible {0,choice,1#type|1<types}
inspection.reflection.invocation.item.count={0,choice,0#Empty array is|1#Single-item array is|1<{0} array items are} expected