[java-inspections] IDEA-281190 MethodHandle inspections do not consider MethodType overloads

PR#1798
Committed-by: tagir.valeev@jetbrains.com

GitOrigin-RevId: e0c59271f5345b45291f177de610bd5f1fb9f6c6
This commit is contained in:
Denis Zavedeev
2021-11-10 07:29:03 +00:00
committed by intellij-monorepo-bot
parent 2bd4aebd1f
commit 3b277a36bf
8 changed files with 351 additions and 57 deletions
@@ -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<ReflectiveSignature> SIGNATURE_MAPPER = new CallMapper<ReflectiveSignature>()
.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<PsiExpression> 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<PsiExpression> 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<PsiExpression[], ReflectiveSignature> 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
*
* <p>
* Examples:
* <ol>
* <li>
* For {@code MethodType.methodType(void.class, MethodType.methodType(String.class)}
* will return {@link PsiExpression} for {@code MethodType.methodType(String.class)}
* </li>
* <li>
* For {@code MethodType.methodType(void.class, MethodType.methodType(String.class, MethodType.methodType(List.class))}
* will return {@link PsiExpression} for {@code MethodType.methodType(List.class)}
* </li>
* </ol>
*
* @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<PsiExpression> components = getListComponents(list);
if (components != null) {
final List<PsiExpression> 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<PsiExpression> components = getVarargs(array);
if (components != null) {
final List<PsiExpression> 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<String> 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;
@@ -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<List<Supplier<ReflectiveType>>> LAZY_SIGNATURE_MAPPER = new CallMapper<List<Supplier<ReflectiveType>>>()
.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<String> 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<Supplier<ReflectiveType>> 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<Supplier<ReflectiveType>> getLazyMethodSignatureForGenericMethodType(
@NotNull PsiMethodCallExpression methodTypeExpression
) {
final PsiExpression[] arguments = methodTypeExpression.getArgumentList().getExpressions();
final Pair.NonNull<Integer, Boolean> 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<ReflectiveType> 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<Supplier<ReflectiveType>> 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<Supplier<ReflectiveType>> nestedSignature = getLazyMethodSignature(methodType);
if (nestedSignature != null) {
final List<Supplier<ReflectiveType>> 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<Integer, Boolean> 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<ReflectiveType> 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<Supplier<ReflectiveType>> 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<PsiExpression> components = getVarargs(secondArgument);
if (components != null) {
final List<PsiExpression> signature = ContainerUtil.prepend(components, returnType);
return ContainerUtil.map(signature, parameter -> (() -> getReflectiveType(parameter)));
}
}
return null;
}
@Nullable
private static List<Supplier<ReflectiveType>> getLazyMethodSignatureForReturnTypeAndList(@NotNull PsiMethodCallExpression call) {
final PsiExpression[] arguments = call.getArgumentList().getExpressions();
if (arguments.length == 2) {
final PsiExpression list = arguments[1];
final List<PsiExpression> components = getListComponents(list);
if (components != null) {
final PsiExpression returnType = findDefinition(arguments[0]);
final List<PsiExpression> signature = ContainerUtil.prepend(components, returnType);
return ContainerUtil.map(signature, argument -> (() -> getReflectiveType(argument)));
}
}
return null;
}
@Nullable
private static List<Supplier<ReflectiveType>> 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,
@@ -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"), <warning descr="Cannot resolve constructor 'NoDefault()'">MethodType.methodType(void.class)</warning>);
}
void differentMethodTypeOverloads() throws Exception {
MethodHandles.Lookup l = MethodHandles.lookup();
l.findConstructor(Test.class, <warning descr="Cannot resolve constructor 'int Test()'">MethodType.methodType(int.class)</warning>);
l.findConstructor(Test.class, <warning descr="Cannot resolve constructor 'int Test()'">MethodType.methodType(int.class, List.of())</warning>);
l.findConstructor(Test.class, <warning descr="Cannot resolve constructor 'int Test()'">MethodType.methodType(int.class, Arrays.asList())</warning>);
}
}
class Test {
@@ -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.<warning descr="Method 'method' is not static">findStatic</warning>(Test.class, "method", MethodType.methodType(void.class));
l.findVirtual(Test.class, <warning descr="Cannot resolve method 'doesntExist'">"doesntExist"</warning>, MethodType.methodType(void.class));
}
void differentMethodTypeOverloads() throws Exception {
MethodHandles.Lookup l = MethodHandles.lookup();
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'void method(void)'">MethodType.methodType(void.class, List.of(void.class))</warning>);
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'void method(void)'">MethodType.methodType(void.class, new Class<?>[]{void.class})</warning>);
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'void method(void)'">MethodType.methodType(void.class, Arrays.asList(void.class))</warning>);
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'void method(void)'">MethodType.methodType(void.class, MethodType.methodType(String.class, void.class))</warning>);
MethodType methodType = MethodType.methodType(String.class, MethodType.methodType(void.class, void.class));
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'void method(void)'">MethodType.methodType(void.class, methodType)</warning>);
}
}
class Test {
@@ -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, <warning descr="Cannot resolve method 'baz'">"baz"</warning>, MethodType.methodType(String.class, double.class), A.class);
l.findSpecial(B.class, "baz", <warning descr="Cannot resolve method 'String baz(double)'">MethodType.methodType(String.class, double.class)</warning>, <warning descr="Caller class 'A' must be a subclass of 'B'">A.class</warning>);
}
void differentMethodTypeOverloads() throws Exception {
MethodHandles.Lookup l = MethodHandles.lookup();
l.findSpecial(B.class, "baz", <warning descr="Cannot resolve method 'String baz(double)'">MethodType.methodType(String.class, double.class)</warning>, C.class);
l.findSpecial(B.class, "baz", <warning descr="Cannot resolve method 'String baz(double)'">MethodType.methodType(String.class, List.of(double.class))</warning>, C.class);
l.findSpecial(B.class, "baz", <warning descr="Cannot resolve method 'String baz(double)'">MethodType.methodType(String.class, Arrays.asList(double.class))</warning>, C.class);
l.findSpecial(B.class, "baz", <warning descr="Cannot resolve method 'String baz(double)'">MethodType.methodType(String.class, new Class<?>[]{double.class})</warning>, C.class);
l.findSpecial(B.class, "baz", <warning descr="Cannot resolve method 'String baz(double)'">MethodType.methodType(String.class, MethodType.methodType(void.class, new Class<?>[]{double.class}))</warning>, C.class);
}
}
interface A {
@@ -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.<warning descr="Method 'method1' is static">findVirtual</warning>(Test.class, "method1", MethodType.methodType(void.class));
l.findStatic(Test.class, <warning descr="Cannot resolve method 'doesntExist'">"doesntExist"</warning>, MethodType.methodType(String.class));
}
void differentMethodTypeOverloads() throws Exception {
MethodHandles.Lookup l = MethodHandles.lookup();
l.findStatic(Test.class, "method2", <warning descr="Cannot resolve method 'int method2(String)'">MethodType.methodType(int.class, String.class)</warning>);
l.findStatic(Test.class, "method2", <warning descr="Cannot resolve method 'int method2(String)'">MethodType.methodType(int.class, List.of(String.class))</warning>);
l.findStatic(Test.class, "method2", <warning descr="Cannot resolve method 'int method2(String)'">MethodType.methodType(int.class, Arrays.asList(String.class))</warning>);
l.findStatic(Test.class, "method2", <warning descr="Cannot resolve method 'int method2(String)'">MethodType.methodType(int.class, new Class<?>[]{String.class})</warning>);
l.findStatic(Test.class, "method2", <warning descr="Cannot resolve method 'int method2(String)'">MethodType.methodType(int.class, MethodType.methodType(void.class, List.of(String.class)))</warning>);
}
}
class Test {
@@ -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 = (<warning descr="Should be cast to 'java.lang.String'">CharSequence</warning>) 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<warning descr="3 arguments are expected">(instance, 1, 2, 3)</warning>;
MethodType methodTypeFromListOf = MethodType.methodType(long.class, (List.of(int.class, long.class)));
MethodHandle methodHandle1 = lookup.findVirtual(Test.class, "foo", methodTypeFromListOf);
methodHandle1.invoke<warning descr="3 arguments are expected">(instance, 1, 2, 3)</warning>;
MethodHandle methodHandle2 = lookup.findVirtual(Test.class, "foo", MethodType.methodType(long.class, MethodType.methodType(Object.class, int.class, long.class)));
methodHandle2.invoke<warning descr="3 arguments are expected">(instance, 1, 2, 3)</warning>;
MethodHandle methodHandle3 = lookup.findVirtual(Test.class, "foo", MethodType.methodType(long.class, new Class<?>[]{int.class, long.class}));
methodHandle3.invoke<warning descr="3 arguments are expected">(instance, 1, 2, 3)</warning>;
MethodHandle methodHandle4 = lookup.findVirtual(Test.class, "foo", MethodType.methodType(long.class, new Class[]{int.class, long.class}));
methodHandle4.invoke<warning descr="3 arguments are expected">(instance, 1, 2, 3)</warning>;
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<warning descr="3 arguments are expected">(instance, 1, 2, 3)</warning>;
List<Class<?>> 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, <error descr="Variable 'noStackOverflowError' might not have been initialized">noStackOverflowError</error>);
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;
}
}
@@ -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")
}
}