[java-inspections] IDEA-172900 "MethodHandle/VarHandle invocation arguments mismatch" false positive with varargs

GitOrigin-RevId: 49fa16434ab4e268ca8b667f118a4b97d71e20a4
This commit is contained in:
Tagir Valeev
2021-12-08 02:48:44 +00:00
committed by intellij-monorepo-bot
parent 038044e84c
commit 5aba52d3ad
4 changed files with 72 additions and 27 deletions
@@ -176,12 +176,22 @@ final class JavaLangReflectHandleInvocationChecker {
if (actualArguments == null) return true;
final int requiredArgumentCount = lazyMethodSignature.size() - 1; // -1 excludes the return type
if (!checkArgumentCount(actualArguments.expressions, requiredArgumentCount, argumentOffset, argumentList, holder)) return false;
final boolean maybeVararg;
if (!isExact && requiredArgumentCount > 0) {
ReflectiveType type = lazyMethodSignature.get(requiredArgumentCount).get();
maybeVararg = type.getType() instanceof PsiArrayType;
} else {
maybeVararg = false;
}
PsiExpression[] expressions = actualArguments.expressions;
if (!maybeVararg || expressions.length < requiredArgumentCount - 1) {
if (!checkArgumentCount(expressions, requiredArgumentCount, argumentOffset, argumentList, holder)) return false;
}
LOG.assertTrue(actualArguments.expressions.length == requiredArgumentCount);
for (int i = 0; i < requiredArgumentCount; i++) {
final ReflectiveType requiredType = lazyMethodSignature.get(i + 1).get();
checkArgumentType(actualArguments.expressions[i], requiredType, argumentList, isExact, holder);
for (int i = 0; i < expressions.length; i++) {
int parameterIndex = maybeVararg && i >= requiredArgumentCount - 1 ? requiredArgumentCount : i + 1;
final ReflectiveType requiredType = lazyMethodSignature.get(parameterIndex).get();
checkArgumentType(expressions[i], requiredType, argumentList, isExact, maybeVararg && i >= requiredArgumentCount - 1, holder);
}
return true;
}
@@ -191,28 +201,34 @@ final class JavaLangReflectHandleInvocationChecker {
@Nullable ReflectiveType requiredType,
@NotNull PsiExpressionList argumentList,
boolean isExact,
boolean maybeVararg,
@NotNull ProblemsHolder holder) {
if (requiredType != null) {
final PsiType actualType = argument.getType();
if (actualType != null) {
if (!isCompatible(requiredType, actualType, isExact)) {
if (PsiTreeUtil.isAncestor(argumentList, argument, false)) {
holder.registerProblem(argument,
JavaBundle.message(isExact
? "inspection.reflect.handle.invocation.argument.not.exact"
: "inspection.reflection.invocation.argument.not.assignable",
requiredType.getQualifiedName()));
}
if (requiredType == null) return;
final PsiType actualType = argument.getType();
if (actualType == null) return;
if (!isCompatible(requiredType, actualType, isExact)) {
if (maybeVararg) {
ReflectiveType componentType = requiredType.getArrayComponentType();
if (componentType != null) {
requiredType = componentType;
if (isCompatible(requiredType, actualType, isExact)) return;
}
else if (requiredType.isPrimitive()) {
final PsiExpression definition = findDefinition(argument);
if (definition != null && PsiType.NULL.equals(definition.getType())) {
if (PsiTreeUtil.isAncestor(argumentList, argument, false)) {
holder.registerProblem(argument,
JavaBundle.message("inspection.reflect.handle.invocation.primitive.argument.null",
requiredType.getQualifiedName()));
}
}
}
if (PsiTreeUtil.isAncestor(argumentList, argument, false)) {
holder.registerProblem(argument,
JavaBundle.message(isExact
? "inspection.reflect.handle.invocation.argument.not.exact"
: "inspection.reflection.invocation.argument.not.assignable",
requiredType.getQualifiedName()));
}
}
else if (requiredType.isPrimitive()) {
final PsiExpression definition = findDefinition(argument);
if (definition != null && PsiType.NULL.equals(definition.getType())) {
if (PsiTreeUtil.isAncestor(argumentList, argument, false)) {
holder.registerProblem(argument,
JavaBundle.message("inspection.reflect.handle.invocation.primitive.argument.null",
requiredType.getQualifiedName()));
}
}
}
@@ -373,7 +389,7 @@ final class JavaLangReflectHandleInvocationChecker {
LOG.assertTrue(arguments.length == argumentOffset + 1);
final ReflectiveType requiredType = getReflectiveType(typeExpression);
checkArgumentType(arguments[argumentOffset], requiredType, argumentList, isExact, holder);
checkArgumentType(arguments[argumentOffset], requiredType, argumentList, isExact, false, holder);
final PsiElement invokeParent = invokeCall.getParent();
if (!(invokeParent instanceof PsiStatement)) {
@@ -155,7 +155,7 @@ final class JavaLangReflectVarHandleInvocationChecker {
LOG.assertTrue(accessArguments.length == requiredArgumentCount);
for (int i = coordinateArguments; i < requiredArgumentCount; i++) {
checkArgumentType(accessArguments[i], valueType, accessArgumentList, false, holder);
checkArgumentType(accessArguments[i], valueType, accessArgumentList, false, false, holder);
}
}
@@ -0,0 +1,27 @@
import java.lang.invoke.MethodHandle;
import java.util.Arrays;
import java.util.List;
import static java.lang.invoke.MethodHandles.publicLookup;
import static java.lang.invoke.MethodType.methodType;
public class VarArgMethodHandle {
public static void main(String... args) throws Throwable {
MethodHandle MH_asList = publicLookup().findStatic(Arrays.class,
"asList", methodType(List.class, Object[].class));
System.out.println(MH_asList.invoke("one", "two").toString());
System.out.println(MH_asList.invokeExact<warning descr="One argument is expected">("one", "two")</warning>.toString());
System.out.println(MH_asList.invokeExact(new Object[] {"one", "two"}).toString());
System.out.println(MH_asList.invoke(new Object[] {"one", "two"}).toString());
MethodHandle MH_main = publicLookup().findStatic(VarArgMethodHandle.class,
"main", methodType(void.class, String[].class));
System.out.println(MH_main.invoke("one", "two").toString());
System.out.println(MH_main.invoke(new String[] {"one", "two"}).toString());
System.out.println(MH_main.invoke(new Object[] {"one", "two"}).toString());
System.out.println(MH_main.invoke(<warning descr="Argument is not assignable to 'java.lang.String'">1</warning>, <warning descr="Argument is not assignable to 'java.lang.String'">2</warning>).toString());
System.out.println(MH_main.invokeExact<warning descr="One argument is expected">("one", "two")</warning>.toString());
System.out.println(MH_main.invokeExact<warning descr="One argument is expected">(1, 2)</warning>.toString());
System.out.println(MH_main.invokeExact(new String[] {"one", "two"}).toString());
System.out.println(MH_main.invokeExact(<warning descr="Argument type should be exactly 'java.lang.String[]'">new Object[] {"one", "two"}</warning>).toString());
}
}
@@ -52,6 +52,8 @@ class JavaLangInvokeHandleSignatureTest : LightJavaCodeInsightFixtureTestCase()
fun testSpecial() = doTest()
fun testVarArgMethodHandle() = doTest()
private fun doTest() {
myFixture.testHighlighting("${getTestName(false)}.java")
}