From 3b277a36bffd3766995fa714d3c4a9eea2dab475 Mon Sep 17 00:00:00 2001 From: Denis Zavedeev Date: Mon, 8 Nov 2021 18:31:01 +0700 Subject: [PATCH] [java-inspections] IDEA-281190 MethodHandle inspections do not consider MethodType overloads PR#1798 Committed-by: tagir.valeev@jetbrains.com GitOrigin-RevId: e0c59271f5345b45291f177de610bd5f1fb9f6c6 --- .../impl/JavaReflectionReferenceUtil.java | 181 ++++++++++++++++-- ...avaLangReflectHandleInvocationChecker.java | 121 +++++++++--- .../invokeHandleSignature/Constructor.java | 10 + .../OverloadedMethod.java | 13 ++ .../invokeHandleSignature/Special.java | 12 ++ .../invokeHandleSignature/StaticMethod.java | 12 ++ .../Virtual.java | 43 +++++ .../JavaLangReflectHandleInvocationTest.kt | 16 +- 8 files changed, 351 insertions(+), 57 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java b/java/java-analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java index 0ccba067db82..37047a3225f2 100644 --- a/java/java-analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java +++ b/java/java-analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java @@ -21,6 +21,8 @@ import com.intellij.util.ArrayUtilRt; import com.intellij.util.ObjectUtils; import com.intellij.util.PlatformIcons; import com.intellij.util.containers.ContainerUtil; +import com.siyeh.ig.callMatcher.CallMapper; +import com.siyeh.ig.callMatcher.CallMatcher; import com.siyeh.ig.psiutils.DeclarationSearchUtils; import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ig.psiutils.MethodCallUtils; @@ -29,9 +31,14 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.lang.invoke.MethodType; import java.util.*; import java.util.function.Function; +import static com.intellij.psi.CommonClassNames.*; +import static com.siyeh.ig.callMatcher.CallMatcher.anyOf; +import static com.siyeh.ig.callMatcher.CallMatcher.staticCall; + /** * @author Pavel.Dolgov */ @@ -43,6 +50,34 @@ public final class JavaReflectionReferenceUtil { public static final String METHOD_TYPE = "methodType"; public static final String GENERIC_METHOD_TYPE = "genericMethodType"; + private static final CallMatcher LIST_FACTORY = anyOf( + staticCall(JAVA_UTIL_LIST, "of"), + staticCall(JAVA_UTIL_ARRAYS, "asList") + ); + private static final CallMatcher.Simple METHOD_TYPE_MATCHER = staticCall(JAVA_LANG_INVOKE_METHOD_TYPE, METHOD_TYPE); + public static final CallMatcher METHOD_TYPE_WITH_METHOD_TYPE_MATCHER = + METHOD_TYPE_MATCHER.parameterTypes(JAVA_LANG_CLASS, JAVA_LANG_INVOKE_METHOD_TYPE); + public static final CallMatcher METHOD_TYPE_WITH_LIST_MATCHER = + METHOD_TYPE_MATCHER.parameterTypes(JAVA_LANG_CLASS, JAVA_UTIL_LIST); + public static final CallMatcher METHOD_TYPE_WITH_CLASSES_MATCHER = anyOf( + METHOD_TYPE_MATCHER.parameterCount(3), + METHOD_TYPE_MATCHER.parameterCount(1), + METHOD_TYPE_MATCHER.parameterTypes(JAVA_LANG_CLASS, JAVA_LANG_CLASS) + ); + public static final CallMatcher METHOD_TYPE_WITH_ARRAY_MATCHER = + METHOD_TYPE_MATCHER.parameterTypes(JAVA_LANG_CLASS, JAVA_LANG_CLASS + "[]"); + public static final CallMatcher GENERIC_METHOD_TYPE_MATCHER = staticCall(JAVA_LANG_INVOKE_METHOD_TYPE, GENERIC_METHOD_TYPE); + private static final CallMapper SIGNATURE_MAPPER = new CallMapper() + .register(METHOD_TYPE_WITH_CLASSES_MATCHER, + call -> composeMethodSignatureFromTypes(call.getArgumentList().getExpressions())) + .register(METHOD_TYPE_WITH_LIST_MATCHER, + call -> composeMethodSignatureFromReturnTypeAndList(call.getArgumentList().getExpressions())) + .register(METHOD_TYPE_WITH_ARRAY_MATCHER, + call -> composeMethodSignatureFromReturnTypeAndArray(call.getArgumentList().getExpressions())) + .register(METHOD_TYPE_WITH_METHOD_TYPE_MATCHER, + call -> composeMethodSignatureFromReturnTypeAndMethodType(call.getArgumentList().getExpressions())) + .register(GENERIC_METHOD_TYPE_MATCHER, + call -> composeGenericMethodSignature(call.getArgumentList().getExpressions())); public static final String FIND_VIRTUAL = "findVirtual"; public static final String FIND_STATIC = "findStatic"; @@ -468,6 +503,31 @@ public final class JavaReflectionReferenceUtil { return null; } + @Nullable + public static List getListComponents(@Nullable PsiExpression maybeList) { + maybeList = PsiUtil.skipParenthesizedExprDown(maybeList); + if (LIST_FACTORY.matches(maybeList) && maybeList instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression callExpression = (PsiMethodCallExpression)maybeList; + final PsiExpression[] expressions = callExpression.getArgumentList().getExpressions(); + if (expressions.length == 0) { + return Collections.emptyList(); + } + final PsiExpression firstArgument = PsiUtil.skipParenthesizedExprDown(expressions[0]); + if (MethodCallUtils.isVarArgCall(callExpression)) { + final List varargs = getVarargs(firstArgument); + if (varargs != null) { + return varargs; + } + } + // Skip calls with explicit arrays, for example: List.of(new Class[0]) + if (isVarargAsArray(firstArgument)) { + return null; + } + return Arrays.asList(expressions); + } + return null; + } + @Contract("null -> false") public static boolean isVarargAsArray(@Nullable PsiExpression maybeArray) { final PsiType type = maybeArray != null ? maybeArray.getType() : null; @@ -482,26 +542,33 @@ public final class JavaReflectionReferenceUtil { */ @Nullable public static ReflectiveSignature composeMethodSignature(@Nullable PsiExpression methodTypeExpression) { - final PsiExpression typeDefinition = findDefinition(methodTypeExpression); + return composeMethodSignature(methodTypeExpression, true); + } + + @Nullable + private static ReflectiveSignature composeMethodSignature(@Nullable PsiExpression methodTypeExpression, boolean allowRecursion) { + final PsiExpression typeDefinition = findDefinition(PsiUtil.skipParenthesizedExprDown(methodTypeExpression)); + if (METHOD_TYPE_WITH_METHOD_TYPE_MATCHER.matches(typeDefinition) && !allowRecursion) { + return null; + } if (typeDefinition instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)typeDefinition; - final String referenceName = methodCallExpression.getMethodExpression().getReferenceName(); + return SIGNATURE_MAPPER.mapFirst((PsiMethodCallExpression)typeDefinition); + } + return null; + } - Function composer = null; - if (METHOD_TYPE.equals(referenceName)) { - composer = JavaReflectionReferenceUtil::composeMethodSignatureFromTypes; - } - else if (GENERIC_METHOD_TYPE.equals(referenceName)) { - composer = JavaReflectionReferenceUtil::composeGenericMethodSignature; - } - - if (composer != null) { - final PsiMethod method = methodCallExpression.resolveMethod(); - if (method != null) { - final PsiClass psiClass = method.getContainingClass(); - if (psiClass != null && JAVA_LANG_INVOKE_METHOD_TYPE.equals(psiClass.getQualifiedName())) { - final PsiExpression[] arguments = methodCallExpression.getArgumentList().getExpressions(); - return composer.apply(arguments); + @Nullable + private static ReflectiveSignature composeMethodSignatureFromReturnTypeAndMethodType( + PsiExpression @NotNull [] arguments + ) { + if (arguments.length == 2) { + final PsiExpression methodType = findInnermostMethodType(arguments[1]); + if (methodType != null) { + final ReflectiveSignature signature = composeMethodSignature(methodType, false); + if (signature != null) { + final String text = getTypeText(arguments[0]); + if (text != null) { + return signature.withReturnType(text); } } } @@ -509,6 +576,80 @@ public final class JavaReflectionReferenceUtil { return null; } + /** + * Find innermost {@link MethodType} for a {@link MethodType#methodType(Class, MethodType)} call + * + *

+ * Examples: + *

    + *
  1. + * For {@code MethodType.methodType(void.class, MethodType.methodType(String.class)} + * will return {@link PsiExpression} for {@code MethodType.methodType(String.class)} + *
  2. + *
  3. + * For {@code MethodType.methodType(void.class, MethodType.methodType(String.class, MethodType.methodType(List.class))} + * will return {@link PsiExpression} for {@code MethodType.methodType(List.class)} + *
  4. + *
+ * + * @param methodType the origin {@link MethodType} + * @return innermost {@link MethodType} as {@link PsiExpression} or {@code null}, if unable to resolve or there are too many nested calls + */ + @Nullable + public static PsiExpression findInnermostMethodType(@Nullable PsiExpression methodType) { + methodType = findDefinition(methodType); + int preventEndlessLoop = 5; + while (METHOD_TYPE_WITH_METHOD_TYPE_MATCHER.matches(methodType)) { + if (--preventEndlessLoop == 0) { + return null; + } + methodType = PsiUtil.skipParenthesizedExprDown(methodType); + if (!(methodType instanceof PsiMethodCallExpression)) { + return null; + } + final PsiMethodCallExpression call = (PsiMethodCallExpression)methodType; + final PsiExpression[] expressions = call.getArgumentList().getExpressions(); + if (expressions.length != 2) { + return null; + } + methodType = findDefinition(expressions[1]); + } + + return METHOD_TYPE_MATCHER.matches(methodType) ? methodType : null; + } + + @Nullable + private static ReflectiveSignature composeMethodSignatureFromReturnTypeAndList(PsiExpression @NotNull [] arguments) { + if (arguments.length == 2) { + final PsiExpression returnType = findDefinition(arguments[0]); + if (returnType != null) { + final PsiExpression list = arguments[1]; + final List components = getListComponents(list); + if (components != null) { + final List signature = ContainerUtil.prepend(components, returnType); + return ReflectiveSignature.create(ContainerUtil.map(signature, typeExpression -> getTypeText(typeExpression))); + } + } + } + return null; + } + + @Nullable + private static ReflectiveSignature composeMethodSignatureFromReturnTypeAndArray(PsiExpression @NotNull [] arguments) { + if (arguments.length == 2) { + final PsiExpression returnType = findDefinition(arguments[0]); + if (returnType != null) { + final PsiExpression array = arguments[1]; + final List components = getVarargs(array); + if (components != null) { + final List signature = ContainerUtil.prepend(components, returnType); + return ReflectiveSignature.create(ContainerUtil.map(signature, typeExpression -> getTypeText(typeExpression))); + } + } + } + return null; + } + @Nullable private static ReflectiveSignature composeMethodSignatureFromTypes(PsiExpression @NotNull [] returnAndParameterTypes) { final List typeTexts = ContainerUtil.map(returnAndParameterTypes, JavaReflectionReferenceUtil::getTypeText); @@ -734,6 +875,10 @@ public final class JavaReflectionReferenceUtil { return myIcon != null ? myIcon : PlatformIcons.METHOD_ICON; } + public ReflectiveSignature withReturnType(@NotNull String returnType) { + return new ReflectiveSignature(this.myIcon, returnType, this.myArgumentTypes); + } + @Override public int compareTo(@NotNull ReflectiveSignature other) { int c = myArgumentTypes.length - other.myArgumentTypes.length; 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 f8ba3bf42851..260b90c5e911 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 @@ -9,6 +9,7 @@ import com.intellij.psi.*; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.ContainerUtil; +import com.siyeh.ig.callMatcher.CallMapper; import com.siyeh.ig.psiutils.ExpressionUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,6 +34,13 @@ final class JavaLangReflectHandleInvocationChecker { private static final String INVOKE_WITH_ARGUMENTS = "invokeWithArguments"; private static final String JAVA_LANG_INVOKE_METHOD_HANDLE = "java.lang.invoke.MethodHandle"; + private static final CallMapper>> LAZY_SIGNATURE_MAPPER = new CallMapper>>() + .register(METHOD_TYPE_WITH_CLASSES_MATCHER, call -> getLazyMethodSignatureForTypes(call)) + .register(METHOD_TYPE_WITH_LIST_MATCHER, call -> getLazyMethodSignatureForReturnTypeAndList(call)) + .register(METHOD_TYPE_WITH_ARRAY_MATCHER, call -> getLazyMethodSignatureForReturnTypeAndArray(call)) + .register(METHOD_TYPE_WITH_METHOD_TYPE_MATCHER, call -> getLazyMethodSignatureForReturnTypeAndMethodType(call)) + .register(GENERIC_METHOD_TYPE_MATCHER, call -> getLazyMethodSignatureForGenericMethodType(call)); + private static final Set METHOD_HANDLE_INVOKE_NAMES = ContainerUtil.set(INVOKE, INVOKE_EXACT, INVOKE_WITH_ARGUMENTS); static boolean checkMethodHandleInvocation(@NotNull PsiMethodCallExpression methodCall, @NotNull ProblemsHolder holder) { @@ -247,41 +255,100 @@ final class JavaLangReflectHandleInvocationChecker { private static List> getLazyMethodSignature(@Nullable PsiExpression methodTypeExpression) { final PsiExpression typeDefinition = findDefinition(methodTypeExpression); if (typeDefinition instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression typeDefinitionCall = (PsiMethodCallExpression)typeDefinition; + return LAZY_SIGNATURE_MAPPER.mapFirst(((PsiMethodCallExpression)typeDefinition)); + } + return null; + } - if (isCallToMethod(typeDefinitionCall, JAVA_LANG_INVOKE_METHOD_TYPE, METHOD_TYPE)) { - final PsiExpression[] arguments = typeDefinitionCall.getArgumentList().getExpressions(); - if (arguments.length != 0) { - return ContainerUtil.map(arguments, argument -> (() -> getReflectiveType(argument))); + @Nullable + private static List> getLazyMethodSignatureForGenericMethodType( + @NotNull PsiMethodCallExpression methodTypeExpression + ) { + final PsiExpression[] arguments = methodTypeExpression.getArgumentList().getExpressions(); + final Pair.NonNull signature = getGenericSignature(arguments); + if (signature != null) { + final int objectArgCount = signature.getFirst(); + final boolean finalArray = signature.getSecond(); + if (objectArgCount == 0 && !finalArray) { + return Collections.emptyList(); + } + final PsiClassType javaLangObject = + PsiType.getJavaLangObject(methodTypeExpression.getManager(), methodTypeExpression.getResolveScope()); + final ReflectiveType objectType = ReflectiveType.create(javaLangObject, false); + final List argumentTypes = new ArrayList<>(); + argumentTypes.add(objectType); // return type + for (int i = 0; i < objectArgCount; i++) { + argumentTypes.add(objectType); + } + if (finalArray) { + argumentTypes.add(ReflectiveType.arrayOf(objectType)); + } + return ContainerUtil.map(argumentTypes, type -> (() -> type)); + } + return null; + } + + @Nullable + private static List> getLazyMethodSignatureForReturnTypeAndMethodType( + @NotNull PsiMethodCallExpression callExpression + ) { + final PsiExpression[] arguments = callExpression.getArgumentList().getExpressions(); + if (arguments.length == 2) { + final PsiExpression methodType = findInnermostMethodType(arguments[1]); + if (methodType != null) { + final List> nestedSignature = getLazyMethodSignature(methodType); + if (nestedSignature != null) { + final List> signature = new ArrayList<>(nestedSignature); + if (!signature.isEmpty()) { + final PsiExpression returnType = arguments[0]; + signature.set(0, () -> getReflectiveType(returnType)); + } + return signature; } } - else if (isCallToMethod(typeDefinitionCall, JAVA_LANG_INVOKE_METHOD_TYPE, GENERIC_METHOD_TYPE)) { - final PsiExpression[] arguments = typeDefinitionCall.getArgumentList().getExpressions(); - final Pair.NonNull signature = getGenericSignature(arguments); - if (signature != null) { - final int objectArgCount = signature.getFirst(); - final boolean finalArray = signature.getSecond(); - if (objectArgCount == 0 && !finalArray) { - return Collections.emptyList(); - } - final PsiClassType javaLangObject = - PsiType.getJavaLangObject(methodTypeExpression.getManager(), methodTypeExpression.getResolveScope()); - final ReflectiveType objectType = ReflectiveType.create(javaLangObject, false); - final List argumentTypes = new ArrayList<>(); - argumentTypes.add(objectType); // return type - for (int i = 0; i < objectArgCount; i++) { - argumentTypes.add(objectType); - } - if (finalArray) { - argumentTypes.add(ReflectiveType.arrayOf(objectType)); - } - return ContainerUtil.map(argumentTypes, type -> (() -> type)); - } + } + return null; + } + + @Nullable + private static List> getLazyMethodSignatureForReturnTypeAndArray(@NotNull PsiMethodCallExpression call) { + final PsiExpression[] arguments = call.getArgumentList().getExpressions(); + if (arguments.length == 2) { + final PsiExpression returnType = findDefinition(arguments[0]); + final PsiExpression secondArgument = arguments[1]; + final List components = getVarargs(secondArgument); + if (components != null) { + final List signature = ContainerUtil.prepend(components, returnType); + return ContainerUtil.map(signature, parameter -> (() -> getReflectiveType(parameter))); } } return null; } + @Nullable + private static List> getLazyMethodSignatureForReturnTypeAndList(@NotNull PsiMethodCallExpression call) { + final PsiExpression[] arguments = call.getArgumentList().getExpressions(); + if (arguments.length == 2) { + final PsiExpression list = arguments[1]; + final List components = getListComponents(list); + if (components != null) { + final PsiExpression returnType = findDefinition(arguments[0]); + final List signature = ContainerUtil.prepend(components, returnType); + return ContainerUtil.map(signature, argument -> (() -> getReflectiveType(argument))); + } + } + return null; + } + + @Nullable + private static List> getLazyMethodSignatureForTypes(@NotNull PsiMethodCallExpression call) { + final PsiExpression[] expressions = call.getArgumentList().getExpressions(); + if (expressions.length != 0) { + return ContainerUtil.map(expressions, argument -> (() -> getReflectiveType(argument))); + } + return null; + } + private static boolean checkGetter(@NotNull PsiMethodCallExpression invokeCall, @NotNull PsiExpression typeExpression, boolean isExact, diff --git a/java/java-tests/testData/inspection/invokeHandleSignature/Constructor.java b/java/java-tests/testData/inspection/invokeHandleSignature/Constructor.java index 652d80d7f446..342db2aee32f 100644 --- a/java/java-tests/testData/inspection/invokeHandleSignature/Constructor.java +++ b/java/java-tests/testData/inspection/invokeHandleSignature/Constructor.java @@ -1,4 +1,6 @@ import java.lang.invoke.*; +import java.util.Arrays; +import java.util.List; class Main { void foo() throws Exception { @@ -25,6 +27,14 @@ class Main { l.findConstructor(Class.forName("NoDefault"), MethodType.methodType(void.class)); } + + void differentMethodTypeOverloads() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + + l.findConstructor(Test.class, MethodType.methodType(int.class)); + l.findConstructor(Test.class, MethodType.methodType(int.class, List.of())); + l.findConstructor(Test.class, MethodType.methodType(int.class, Arrays.asList())); + } } class Test { diff --git a/java/java-tests/testData/inspection/invokeHandleSignature/OverloadedMethod.java b/java/java-tests/testData/inspection/invokeHandleSignature/OverloadedMethod.java index 34f249d17360..9c02715807ae 100644 --- a/java/java-tests/testData/inspection/invokeHandleSignature/OverloadedMethod.java +++ b/java/java-tests/testData/inspection/invokeHandleSignature/OverloadedMethod.java @@ -1,4 +1,6 @@ import java.lang.invoke.*; +import java.util.Arrays; +import java.util.List; class Main { void foo() throws Exception { @@ -26,6 +28,17 @@ class Main { l.findStatic(Test.class, "method", MethodType.methodType(void.class)); l.findVirtual(Test.class, "doesntExist", MethodType.methodType(void.class)); } + + void differentMethodTypeOverloads() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + + l.findVirtual(Test.class, "method", MethodType.methodType(void.class, List.of(void.class))); + l.findVirtual(Test.class, "method", MethodType.methodType(void.class, new Class[]{void.class})); + l.findVirtual(Test.class, "method", MethodType.methodType(void.class, Arrays.asList(void.class))); + l.findVirtual(Test.class, "method", MethodType.methodType(void.class, MethodType.methodType(String.class, void.class))); + MethodType methodType = MethodType.methodType(String.class, MethodType.methodType(void.class, void.class)); + l.findVirtual(Test.class, "method", MethodType.methodType(void.class, methodType)); + } } class Test { diff --git a/java/java-tests/testData/inspection/invokeHandleSignature/Special.java b/java/java-tests/testData/inspection/invokeHandleSignature/Special.java index 8335858a1e6e..1a0b91e9e351 100644 --- a/java/java-tests/testData/inspection/invokeHandleSignature/Special.java +++ b/java/java-tests/testData/inspection/invokeHandleSignature/Special.java @@ -1,4 +1,6 @@ import java.lang.invoke.*; +import java.util.Arrays; +import java.util.List; class Main { void foo() throws Exception { @@ -47,6 +49,16 @@ class Main { l.findSpecial(A.class, "baz", MethodType.methodType(String.class, double.class), A.class); l.findSpecial(B.class, "baz", MethodType.methodType(String.class, double.class), A.class); } + + void differentMethodTypeOverloads() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + + l.findSpecial(B.class, "baz", MethodType.methodType(String.class, double.class), C.class); + l.findSpecial(B.class, "baz", MethodType.methodType(String.class, List.of(double.class)), C.class); + l.findSpecial(B.class, "baz", MethodType.methodType(String.class, Arrays.asList(double.class)), C.class); + l.findSpecial(B.class, "baz", MethodType.methodType(String.class, new Class[]{double.class}), C.class); + l.findSpecial(B.class, "baz", MethodType.methodType(String.class, MethodType.methodType(void.class, new Class[]{double.class})), C.class); + } } interface A { diff --git a/java/java-tests/testData/inspection/invokeHandleSignature/StaticMethod.java b/java/java-tests/testData/inspection/invokeHandleSignature/StaticMethod.java index f16af0c68d30..4b9904db3c30 100644 --- a/java/java-tests/testData/inspection/invokeHandleSignature/StaticMethod.java +++ b/java/java-tests/testData/inspection/invokeHandleSignature/StaticMethod.java @@ -1,4 +1,6 @@ import java.lang.invoke.*; +import java.util.Arrays; +import java.util.List; class Main { void foo() throws Exception { @@ -16,6 +18,16 @@ class Main { l.findVirtual(Test.class, "method1", MethodType.methodType(void.class)); l.findStatic(Test.class, "doesntExist", MethodType.methodType(String.class)); } + + void differentMethodTypeOverloads() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + + l.findStatic(Test.class, "method2", MethodType.methodType(int.class, String.class)); + l.findStatic(Test.class, "method2", MethodType.methodType(int.class, List.of(String.class))); + l.findStatic(Test.class, "method2", MethodType.methodType(int.class, Arrays.asList(String.class))); + l.findStatic(Test.class, "method2", MethodType.methodType(int.class, new Class[]{String.class})); + l.findStatic(Test.class, "method2", MethodType.methodType(int.class, MethodType.methodType(void.class, List.of(String.class)))); + } } class Test { diff --git a/java/java-tests/testData/inspection/javaLangReflectHandleInvocation/Virtual.java b/java/java-tests/testData/inspection/javaLangReflectHandleInvocation/Virtual.java index eec0142a6b7f..66270150a5e7 100644 --- a/java/java-tests/testData/inspection/javaLangReflectHandleInvocation/Virtual.java +++ b/java/java-tests/testData/inspection/javaLangReflectHandleInvocation/Virtual.java @@ -1,6 +1,11 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; class Main { void fooInt() throws Throwable { @@ -122,10 +127,48 @@ class Main { CharSequence superclassResult3 = (CharSequence) handle.invokeExact(instance, "c"); } + void fooVariousTypesVariousMethodTypes() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Test instance = new Test(); + + MethodType methodTypeFromArraysAsList = MethodType.methodType(long.class, Arrays.asList(int.class, long.class)); + MethodHandle methodHandle0 = lookup.findVirtual(Test.class, "foo", methodTypeFromArraysAsList); + methodHandle0.invoke(instance, 1, 2, 3); + + MethodType methodTypeFromListOf = MethodType.methodType(long.class, (List.of(int.class, long.class))); + MethodHandle methodHandle1 = lookup.findVirtual(Test.class, "foo", methodTypeFromListOf); + methodHandle1.invoke(instance, 1, 2, 3); + + MethodHandle methodHandle2 = lookup.findVirtual(Test.class, "foo", MethodType.methodType(long.class, MethodType.methodType(Object.class, int.class, long.class))); + methodHandle2.invoke(instance, 1, 2, 3); + + MethodHandle methodHandle3 = lookup.findVirtual(Test.class, "foo", MethodType.methodType(long.class, new Class[]{int.class, long.class})); + methodHandle3.invoke(instance, 1, 2, 3); + + MethodHandle methodHandle4 = lookup.findVirtual(Test.class, "foo", MethodType.methodType(long.class, new Class[]{int.class, long.class})); + methodHandle4.invoke(instance, 1, 2, 3); + + MethodType nestedMethodType5 = MethodType.methodType(long.class, new Class[]{int.class, long.class}); + MethodHandle methodHandle5 = lookup.findVirtual(Test.class, "foo", MethodType.methodType(long.class, nestedMethodType5)); + methodHandle5.invoke(instance, 1, 2, 3); + + List> noWarningsForMutableLists = Arrays.asList(int.class, String.class); + noWarningsForMutableLists.set(1, long.class); + MethodHandle methodHandle6 = lookup.findVirtual(Test.class, "foo", MethodType.methodType(long.class, noWarningsForMutableLists)); + methodHandle6.invoke(instance, 1, 2L); + + MethodType noStackOverflowError = MethodType.methodType(long.class, noStackOverflowError); + MethodHandle methodHandle7 = lookup.findVirtual(Test.class, "foo", noStackOverflowError); + methodHandle7.invoke(instance, 1, 2L); + } + private static CharSequence charSequence() { return "abc"; } } class Test { public int foo(int n) {return n;} public String foo(String s) {return s;} + public long foo(int i, long l) { + return l + i; + } } diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/JavaLangReflectHandleInvocationTest.kt b/java/java-tests/testSrc/com/intellij/java/codeInspection/JavaLangReflectHandleInvocationTest.kt index f27f417e102e..7bc7c5b93964 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/JavaLangReflectHandleInvocationTest.kt +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/JavaLangReflectHandleInvocationTest.kt @@ -25,9 +25,7 @@ import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase /** * @author Pavel.Dolgov */ - -class JavaLangReflectHandleInvocationTest : JavaLangReflectHandleInvocationTestBase(LanguageLevel.JDK_1_7, - LightJavaCodeInsightFixtureTestCase.JAVA_8) { +class JavaLangReflectHandleInvocationTest : LightJavaCodeInsightFixtureTestCase() { fun testVirtual() = doTest() fun testStatic() = doTest() fun testConstructor() = doTest() @@ -37,28 +35,22 @@ class JavaLangReflectHandleInvocationTest : JavaLangReflectHandleInvocationTestB fun testStaticGetter() = doTest() fun testStaticSetter() = doTest() -} -class Java9LangReflectHandleInvocationTest : JavaLangReflectHandleInvocationTestBase(LanguageLevel.JDK_1_9, - LightJavaCodeInsightFixtureTestCase.JAVA_9) { fun testVarHandle() = doTest() fun testStaticVarHandle() = doTest() fun testArrayVarHandle() = doTest() -} -abstract class JavaLangReflectHandleInvocationTestBase(val languageLevel: LanguageLevel, - val descriptor: LightProjectDescriptor) : LightJavaCodeInsightFixtureTestCase() { override fun setUp() { super.setUp() - LanguageLevelProjectExtension.getInstance(project).languageLevel = languageLevel + LanguageLevelProjectExtension.getInstance(project).languageLevel = LanguageLevel.JDK_1_9 myFixture.enableInspections(JavaLangInvokeHandleSignatureInspection()) } - override fun getProjectDescriptor(): LightProjectDescriptor = descriptor + override fun getProjectDescriptor(): LightProjectDescriptor = JAVA_9 override fun getBasePath() = JavaTestUtil.getRelativeJavaTestDataPath() + "/inspection/javaLangReflectHandleInvocation" - protected fun doTest() { + private fun doTest() { myFixture.testHighlighting("${getTestName(false)}.java") } }