From 5aba52d3ad4c9414e91fb767453bfd85efcd0667 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 7 Dec 2021 14:18:11 +0700 Subject: [PATCH] [java-inspections] IDEA-172900 "MethodHandle/VarHandle invocation arguments mismatch" false positive with varargs GitOrigin-RevId: 49fa16434ab4e268ca8b667f118a4b97d71e20a4 --- ...avaLangReflectHandleInvocationChecker.java | 68 ++++++++++++------- ...LangReflectVarHandleInvocationChecker.java | 2 +- .../VarArgMethodHandle.java | 27 ++++++++ .../JavaLangInvokeHandleSignatureTest.kt | 2 + 4 files changed, 72 insertions(+), 27 deletions(-) create mode 100644 java/java-tests/testData/inspection/invokeHandleSignature/VarArgMethodHandle.java diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/reflectiveAccess/JavaLangReflectHandleInvocationChecker.java b/java/java-impl-inspections/src/com/intellij/codeInspection/reflectiveAccess/JavaLangReflectHandleInvocationChecker.java index 260b90c5e911..45e69f9a5797 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/reflectiveAccess/JavaLangReflectHandleInvocationChecker.java +++ b/java/java-impl-inspections/src/com/intellij/codeInspection/reflectiveAccess/JavaLangReflectHandleInvocationChecker.java @@ -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)) { diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/reflectiveAccess/JavaLangReflectVarHandleInvocationChecker.java b/java/java-impl-inspections/src/com/intellij/codeInspection/reflectiveAccess/JavaLangReflectVarHandleInvocationChecker.java index 5c6cd09d2e7e..69866cc262ed 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/reflectiveAccess/JavaLangReflectVarHandleInvocationChecker.java +++ b/java/java-impl-inspections/src/com/intellij/codeInspection/reflectiveAccess/JavaLangReflectVarHandleInvocationChecker.java @@ -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); } } diff --git a/java/java-tests/testData/inspection/invokeHandleSignature/VarArgMethodHandle.java b/java/java-tests/testData/inspection/invokeHandleSignature/VarArgMethodHandle.java new file mode 100644 index 000000000000..49a5e958753b --- /dev/null +++ b/java/java-tests/testData/inspection/invokeHandleSignature/VarArgMethodHandle.java @@ -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("one", "two").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(1, 2).toString()); + System.out.println(MH_main.invokeExact("one", "two").toString()); + System.out.println(MH_main.invokeExact(1, 2).toString()); + System.out.println(MH_main.invokeExact(new String[] {"one", "two"}).toString()); + System.out.println(MH_main.invokeExact(new Object[] {"one", "two"}).toString()); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/JavaLangInvokeHandleSignatureTest.kt b/java/java-tests/testSrc/com/intellij/java/codeInspection/JavaLangInvokeHandleSignatureTest.kt index e0d1b8fd9e72..3d28061c5619 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/JavaLangInvokeHandleSignatureTest.kt +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/JavaLangInvokeHandleSignatureTest.kt @@ -52,6 +52,8 @@ class JavaLangInvokeHandleSignatureTest : LightJavaCodeInsightFixtureTestCase() fun testSpecial() = doTest() + fun testVarArgMethodHandle() = doTest() + private fun doTest() { myFixture.testHighlighting("${getTestName(false)}.java") }