diff --git a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java index 988438f97128..0702364daf7b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java @@ -30,7 +30,6 @@ import com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferen import com.intellij.util.PlatformIcons; import com.intellij.util.containers.ContainerUtil; import com.siyeh.ig.psiutils.ParenthesesUtils; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,7 +38,6 @@ import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; -import static com.intellij.psi.impl.source.resolve.reference.impl.JavaLangInvokeHandleReference.*; import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*; /** @@ -48,9 +46,6 @@ import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflection public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalInspectionTool { public static final Key> DEFAULT_SIGNATURE = Key.create("DEFAULT_SIGNATURE"); - private static final String METHOD_TYPE = "methodType"; - private static final String GENERIC_METHOD_TYPE = "genericMethodType"; - private static final String FIND_CONSTRUCTOR = "findConstructor"; private static final Set KNOWN_METHOD_NAMES = Collections.unmodifiableSet( ContainerUtil.union(Arrays.asList(HANDLE_FACTORY_METHOD_NAMES), Collections.singletonList(FIND_CONSTRUCTOR))); @@ -107,6 +102,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI case FIND_VAR_HANDLE: checkField(ownerClass, memberName, nameExpression, typeExpression, false, factoryMethodExpression, holder); break; + case FIND_STATIC_GETTER: case FIND_STATIC_SETTER: case FIND_STATIC_VAR_HANDLE: @@ -144,7 +140,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI } else if (!matchMethodSignature(constructors, constructorSignature)) { validSignatures = constructors.stream() - .map(JavaLangInvokeHandleSignatureInspection::getMethodSignature) + .map(JavaReflectionReferenceUtil::getMethodSignature) .filter(Objects::nonNull) .collect(Collectors.toList()); } @@ -230,7 +226,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI if (methodSignature != null && !matchMethodSignature(filteredMethods, methodSignature)) { final String declarationText = getMethodDeclarationText(methodName, methodSignature); final List> validSignatures = filteredMethods.stream() - .map(JavaLangInvokeHandleSignatureInspection::getMethodSignature) + .map(JavaReflectionReferenceUtil::getMethodSignature) .filter(Objects::nonNull) .collect(Collectors.toList()); final LocalQuickFix fix = @@ -266,7 +262,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI private static boolean matchMethodSignature(@NotNull List methods, @NotNull List expectedMethodSignature) { return methods.stream() - .map(JavaLangInvokeHandleSignatureInspection::getMethodSignature) + .map(JavaReflectionReferenceUtil::getMethodSignature) .anyMatch(expectedMethodSignature::equals); } @@ -342,24 +338,6 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI return typeNames; } - @Contract("null -> null") - @Nullable - private static List getMethodSignature(@Nullable PsiMethod method) { - if (method != null) { - final List types = new ArrayList<>(); - final PsiType returnType = !method.isConstructor() ? method.getReturnType() : PsiType.VOID; - types.add(getTypeText(returnType, method)); - - for (PsiParameter parameter : method.getParameterList().getParameters()) { - types.add(getTypeText(parameter.getType(), method)); - } - if (!types.contains(null)) { - return types; - } - } - return null; - } - private static class FieldTypeQuickFix implements LocalQuickFix { private final String myFieldTypeText; @@ -519,7 +497,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI } private static void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull List signature) { - final String replacementText = getReplacementText(signature); + final String replacementText = getMethodTypeExpressionText(signature); final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); final PsiExpression replacement = factory.createExpressionFromText(replacementText, element); final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project); @@ -531,14 +509,6 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI return myIsConstructor ? getConstructorDeclarationText(myName, signature) : getMethodDeclarationText(myName, signature); } - @NotNull - private static String getReplacementText(@NotNull List signature) { - final String types = signature.stream() - .map(text -> text + ".class") - .collect(Collectors.joining(", ")); - return JAVA_LANG_INVOKE_METHOD_TYPE + "." + METHOD_TYPE + "(" + types + ")"; - } - @Nullable private static LocalQuickFix createFix(@Nullable PsiElement element, @NotNull String methodName, diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java index 089d972f985b..87d6ce55d1e5 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java @@ -38,11 +38,6 @@ import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflection * @author Konstantin Bulenkov */ public class JavaLangClassMemberReference extends PsiReferenceBase implements InsertHandler { - private static final String FIELD = "getField"; - private static final String DECLARED_FIELD = "getDeclaredField"; - private static final String METHOD = "getMethod"; - private static final String DECLARED_METHOD = "getDeclaredMethod"; - private final PsiExpression myContext; public JavaLangClassMemberReference(@NotNull PsiLiteralExpression literal, @NotNull PsiExpression context) { @@ -67,16 +62,16 @@ public class JavaLangClassMemberReference extends PsiReferenceBase isRegularMethod(method) && isPublic(method)); if (publicMethod != null) { @@ -85,7 +80,7 @@ public class JavaLangClassMemberReference extends PsiReferenceBase isRegularMethod(method)); } - case DECLARED_METHOD: { + case GET_DECLARED_METHOD: { final PsiMethod[] methods = psiClass.findMethodsByName(name, false); return ContainerUtil.find(methods, method -> isRegularMethod(method) && isPotentiallyAccessible(method, psiClass)); } @@ -110,14 +105,14 @@ public class JavaLangClassMemberReference extends PsiReferenceBase field.getName() != null) .sorted(Comparator.comparing(PsiField::getName)) .map(field -> lookupField(field)) .toArray(); - case FIELD: { + case GET_FIELD: { final Set uniqueNames = new THashSet<>(); return Arrays.stream(psiClass.getAllFields()) .filter(field -> isPotentiallyAccessible(field, psiClass) && field.getName() != null && uniqueNames.add(field.getName())) @@ -126,14 +121,14 @@ public class JavaLangClassMemberReference extends PsiReferenceBase isRegularMethod(method)) .sorted(Comparator.comparing(PsiMethod::getName)) .map(method -> lookupMethod(method)) .toArray(); - case METHOD: { + case GET_METHOD: { return psiClass.getVisibleSignatures() .stream() .map(MethodSignatureBackedByPsiMethod::getMethod) diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java index be8bc11b98c5..84a26b0a6db7 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java @@ -22,7 +22,6 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; -import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.ProcessingContext; @@ -33,10 +32,9 @@ import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Comparator; +import java.util.List; import java.util.Set; import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*; @@ -44,26 +42,6 @@ import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflection * @author Pavel.Dolgov */ public class JavaLangInvokeHandleReference extends PsiReferenceBase implements InsertHandler { - public static final String JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP = "java.lang.invoke.MethodHandles.Lookup"; - public static final String JAVA_LANG_INVOKE_METHOD_TYPE = "java.lang.invoke.MethodType"; - - public static final String FIND_VIRTUAL = "findVirtual"; - public static final String FIND_STATIC = "findStatic"; - public static final String FIND_SPECIAL = "findSpecial"; - - public static final String FIND_GETTER = "findGetter"; - public static final String FIND_SETTER = "findSetter"; - public static final String FIND_STATIC_GETTER = "findStaticGetter"; - public static final String FIND_STATIC_SETTER = "findStaticSetter"; - - public static final String FIND_VAR_HANDLE = "findVarHandle"; - public static final String FIND_STATIC_VAR_HANDLE = "findStaticVarHandle"; - - public static final String[] HANDLE_FACTORY_METHOD_NAMES = { - FIND_VIRTUAL, FIND_STATIC, FIND_SPECIAL, - FIND_GETTER, FIND_SETTER, - FIND_STATIC_GETTER, FIND_STATIC_SETTER, - FIND_VAR_HANDLE, FIND_STATIC_VAR_HANDLE}; private final PsiExpression myContext; @@ -203,27 +181,19 @@ public class JavaLangInvokeHandleReference extends PsiReferenceBase returnType = Stream.of(method.getReturnType()) - .map(type -> type != null ? type : PsiType.VOID); - final Stream parametersTypes = Arrays.stream(method.getParameterList().getParameters()) - .map(parameter -> parameter.getType()); - - final String types = Stream.concat(returnType, parametersTypes) - .map(type -> TypeConversionUtil.erasure(type)) - .map(type -> (type instanceof PsiEllipsisType) ? new PsiArrayType(((PsiEllipsisType)type).getComponentType()) : type) - .map(type -> type.getPresentableText() + ".class") - .collect(Collectors.joining(", ")); - final String text = ", " + JAVA_LANG_INVOKE_METHOD_TYPE + ".methodType(" + types + ")"; - - replaceText(context, text); + final List signature = getMethodSignature((PsiMethod)object); + if (signature != null) { + final String text = ", " + getMethodTypeExpressionText(signature); + replaceText(context, text); + } } else if (object instanceof PsiField) { final PsiField field = (PsiField)object; - final PsiType type = TypeConversionUtil.erasure(field.getType()); - final String text = ", " + type.getCanonicalText() + ".class"; - - replaceText(context, text); + final String typeText = getTypeText(field.getType(), field); + if (typeText != null) { + final String text = ", " + typeText + ".class"; + replaceText(context, text); + } } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceContributor.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceContributor.java index ab4da5d778ee..706f9ec5a4dc 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceContributor.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceContributor.java @@ -26,25 +26,21 @@ import org.jetbrains.annotations.Nullable; import static com.intellij.patterns.PsiJavaPatterns.psiLiteral; import static com.intellij.patterns.PsiJavaPatterns.psiMethod; import static com.intellij.patterns.StandardPatterns.or; -import static com.intellij.patterns.StandardPatterns.string; import static com.intellij.psi.CommonClassNames.JAVA_LANG_CLASS; -import static com.intellij.psi.impl.source.resolve.reference.impl.JavaLangInvokeHandleReference.*; +import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*; /** * @author Konstantin Bulenkov */ public class JavaReflectionReferenceContributor extends PsiReferenceContributor { public static final PsiJavaElementPattern.Capture PATTERN = - psiLiteral().methodCallParameter(psiMethod().withName(string().oneOf("getDeclaredField", - "getField", - "getMethod", - "getDeclaredMethod")) - .definedInClass(JAVA_LANG_CLASS)); + psiLiteral().methodCallParameter(psiMethod().withName(GET_FIELD, GET_DECLARED_FIELD, GET_METHOD, GET_DECLARED_METHOD) + .definedInClass(JAVA_LANG_CLASS)); public static final PsiJavaElementPattern.Capture CLASS_PATTERN = psiLiteral().methodCallParameter(or( - psiMethod().withName(string().equalTo("forName")).definedInClass(JAVA_LANG_CLASS), - psiMethod().withName(string().equalTo("loadClass")).definedInClass("java.lang.ClassLoader"))); + psiMethod().withName(FOR_NAME).definedInClass(JAVA_LANG_CLASS), + psiMethod().withName(LOAD_CLASS).definedInClass(JAVA_LANG_CLASS_LOADER))); private static final ElementPattern METHOD_HANDLE_PATTERN = psiLiteral() .methodCallParameter(1, psiMethod() @@ -60,7 +56,7 @@ public class JavaReflectionReferenceContributor extends PsiReferenceContributor @NotNull PsiReferenceExpression methodReference, @NotNull ProcessingContext context) { - PsiExpression qualifier = methodReference.getQualifierExpression(); + final PsiExpression qualifier = methodReference.getQualifierExpression(); return qualifier != null ? new PsiReference[]{new JavaLangClassMemberReference(literalArgument, qualifier)} : null; } }); @@ -72,14 +68,14 @@ public class JavaReflectionReferenceContributor extends PsiReferenceContributor @NotNull PsiReferenceExpression methodReference, @NotNull ProcessingContext context) { - String referenceName = methodReference.getReferenceName(); - if ("forName".equals(referenceName) || "loadClass".equals(referenceName)) { + final String referenceName = methodReference.getReferenceName(); + if (FOR_NAME.equals(referenceName) || LOAD_CLASS.equals(referenceName)) { return new JavaClassReferenceProvider().getReferencesByElement(literalArgument, context); } return null; } }); - registrar.registerReferenceProvider(METHOD_HANDLE_PATTERN, new JavaLangInvokeHandleReferenceProvider()); + registrar.registerReferenceProvider(METHOD_HANDLE_PATTERN, new JavaLangInvokeHandleReference.JavaLangInvokeHandleReferenceProvider()); } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java index aefccdd41c36..e179bdce480f 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java @@ -34,12 +34,49 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; import java.util.StringJoiner; +import java.util.stream.Collectors; /** * @author Pavel.Dolgov */ public class JavaReflectionReferenceUtil { + public static final String JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP = "java.lang.invoke.MethodHandles.Lookup"; + public static final String JAVA_LANG_INVOKE_METHOD_TYPE = "java.lang.invoke.MethodType"; + + public static final String METHOD_TYPE = "methodType"; + public static final String GENERIC_METHOD_TYPE = "genericMethodType"; + + public static final String FIND_VIRTUAL = "findVirtual"; + public static final String FIND_STATIC = "findStatic"; + public static final String FIND_SPECIAL = "findSpecial"; + + public static final String FIND_GETTER = "findGetter"; + public static final String FIND_SETTER = "findSetter"; + public static final String FIND_STATIC_GETTER = "findStaticGetter"; + public static final String FIND_STATIC_SETTER = "findStaticSetter"; + + public static final String FIND_VAR_HANDLE = "findVarHandle"; + public static final String FIND_STATIC_VAR_HANDLE = "findStaticVarHandle"; + + public static final String GET_FIELD = "getField"; + public static final String GET_DECLARED_FIELD = "getDeclaredField"; + public static final String GET_METHOD = "getMethod"; + public static final String GET_DECLARED_METHOD = "getDeclaredMethod"; + + public static final String JAVA_LANG_CLASS_LOADER = "java.lang.ClassLoader"; + public static final String FOR_NAME = "forName"; + public static final String LOAD_CLASS = "loadClass"; + public static final String GET_CLASS = "getClass"; + + public static final String[] HANDLE_FACTORY_METHOD_NAMES = { + FIND_VIRTUAL, FIND_STATIC, FIND_SPECIAL, + FIND_GETTER, FIND_SETTER, + FIND_STATIC_GETTER, FIND_STATIC_SETTER, + FIND_VAR_HANDLE, FIND_STATIC_VAR_HANDLE}; + private static final RecursionGuard ourGuard = RecursionManager.createGuard("JavaLangClassMemberReference"); @Nullable @@ -56,7 +93,7 @@ public class JavaReflectionReferenceUtil { if (context instanceof PsiMethodCallExpression) { final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)context; final String methodReferenceName = methodCall.getMethodExpression().getReferenceName(); - if ("forName".equals(methodReferenceName)) { + if (FOR_NAME.equals(methodReferenceName)) { final PsiMethod method = methodCall.resolveMethod(); if (method != null && isJavaLangClass(method.getContainingClass())) { final PsiExpression[] expressions = methodCall.getArgumentList().getExpressions(); @@ -69,7 +106,7 @@ public class JavaReflectionReferenceUtil { } } } - else if ("getClass".equals(methodReferenceName) && methodCall.getArgumentList().getExpressions().length == 0) { + else if (GET_CLASS.equals(methodReferenceName) && methodCall.getArgumentList().getExpressions().length == 0) { final PsiMethod method = methodCall.resolveMethod(); if (method != null && isJavaLangObject(method.getContainingClass())) { final PsiExpression qualifier = ParenthesesUtils.stripParentheses(methodCall.getMethodExpression().getQualifierExpression()); @@ -242,6 +279,32 @@ public class JavaReflectionReferenceUtil { return reflectiveType != null ? reflectiveType.getQualifiedName() : null; } + @Contract("null -> null") + @Nullable + public static List getMethodSignature(@Nullable PsiMethod method) { + if (method != null) { + final List types = new ArrayList<>(); + final PsiType returnType = !method.isConstructor() ? method.getReturnType() : PsiType.VOID; + types.add(getTypeText(returnType, method)); + + for (PsiParameter parameter : method.getParameterList().getParameters()) { + types.add(getTypeText(parameter.getType(), method)); + } + if (!types.contains(null)) { + return types; + } + } + return null; + } + + @NotNull + public static String getMethodTypeExpressionText(@NotNull List signature) { + final String types = signature.stream() + .map(text -> text + ".class") + .collect(Collectors.joining(", ")); + return JAVA_LANG_INVOKE_METHOD_TYPE + "." + METHOD_TYPE + "(" + types + ")"; + } + public static class ReflectiveType { final PsiClass myPsiClass; diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/JavaLangInvokeMethodHandleSignatureFixTest.kt b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/JavaLangInvokeMethodHandleSignatureFixTest.kt index 92bbdc6aa52b..6fd0f1a88839 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/JavaLangInvokeMethodHandleSignatureFixTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/JavaLangInvokeMethodHandleSignatureFixTest.kt @@ -17,7 +17,7 @@ package com.intellij.codeInsight.daemon.quickFix import com.intellij.JavaTestUtil import com.intellij.codeInsight.intention.IntentionAction -import com.intellij.codeInspection.InspectionsBundle +import com.intellij.codeInspection.InspectionsBundle.message import com.intellij.codeInspection.reflectiveAccess.JavaLangInvokeHandleSignatureInspection import com.intellij.codeInspection.reflectiveAccess.JavaLangInvokeHandleSignatureInspection.DEFAULT_SIGNATURE import com.intellij.testFramework.LightProjectDescriptor @@ -50,16 +50,17 @@ class JavaLangInvokeMethodHandleSignatureFixTest : LightCodeInsightFixtureTestCa fun testStaticMethod() = doTestMethod(VOID) fun testStaticMethod2() = doTestMethod(STRING, STRING) fun testStaticMethod3() = doTestMethod(STRING, STRING, STRING_ARRAY) - fun testStaticMethod4() = doTest("Replace with 'findStatic'") + fun testStaticMethod4() = doTestReplace("findStatic") fun testVirtualMethod() = doTestMethod(VOID) fun testVirtualMethod2() = doTestMethod(STRING, STRING) fun testVirtualMethod3() = doTestMethod(STRING, STRING, STRING_ARRAY) - fun testVirtualMethod4() = doTest("Replace with 'findVirtual'") + fun testVirtualMethod4() = doTestReplace("findVirtual") fun doTestMethod(vararg withSignature: String) = doTest(USE_METHOD, *withSignature) fun doTestConstructor(vararg withSignature: String) = doTest(USE_CONSTRUCTOR, VOID, *withSignature) + fun doTestReplace(replacement: String) = doTest(message("inspection.handle.signature.replace.with.fix.name", replacement)) fun doTest(actionPrefix: String, vararg withSignature: String) { val testName = getTestName(false) @@ -80,8 +81,8 @@ class JavaLangInvokeMethodHandleSignatureFixTest : LightCodeInsightFixtureTestCa assertEquals("Too many actions", 0, actions.size) val familyName = when (actionPrefix) { - USE_CONSTRUCTOR -> InspectionsBundle.message("inspection.handle.signature.use.constructor.fix.family.name") - USE_METHOD -> InspectionsBundle.message("inspection.handle.signature.use.method.fix.family.name") + USE_CONSTRUCTOR -> message("inspection.handle.signature.use.constructor.fix.family.name") + USE_METHOD -> message("inspection.handle.signature.use.method.fix.family.name") else -> { fail("Unexpected action " + actionPrefix); "" }