Java: Minor refactoring in completion assistance for Var/Method Handle - reuse utility methods, move shared methods and constants to utility class (IDEA-167319)

This commit is contained in:
Pavel Dolgov
2017-03-17 16:03:38 +03:00
parent 0b1d362be9
commit 7b7e87d44d
6 changed files with 104 additions and 109 deletions
@@ -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<List<String>> 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<String> 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<List<String>> 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<PsiMethod> methods, @NotNull List<String> 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<String> getMethodSignature(@Nullable PsiMethod method) {
if (method != null) {
final List<String> 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<String> 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<String> 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,
@@ -38,11 +38,6 @@ import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflection
* @author Konstantin Bulenkov
*/
public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExpression> implements InsertHandler<LookupElement> {
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<PsiLiteralExp
if (psiClass != null) {
switch (type) {
case FIELD: {
case GET_FIELD: {
return psiClass.findFieldByName(name, true);
}
case DECLARED_FIELD: {
case GET_DECLARED_FIELD: {
final PsiField field = psiClass.findFieldByName(name, false);
return isPotentiallyAccessible(field, psiClass) ? field : null;
}
case METHOD: {
case GET_METHOD: {
final PsiMethod[] methods = psiClass.findMethodsByName(name, true);
final PsiMethod publicMethod = ContainerUtil.find(methods, method -> isRegularMethod(method) && isPublic(method));
if (publicMethod != null) {
@@ -85,7 +80,7 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
return ContainerUtil.find(methods, method -> 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<PsiLiteralExp
if (psiClass != null) {
switch (type) {
case DECLARED_FIELD:
case GET_DECLARED_FIELD:
return Arrays.stream(psiClass.getFields())
.filter(field -> field.getName() != null)
.sorted(Comparator.comparing(PsiField::getName))
.map(field -> lookupField(field))
.toArray();
case FIELD: {
case GET_FIELD: {
final Set<String> 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<PsiLiteralExp
.toArray();
}
case DECLARED_METHOD:
case GET_DECLARED_METHOD:
return Arrays.stream(psiClass.getMethods())
.filter(method -> isRegularMethod(method))
.sorted(Comparator.comparing(PsiMethod::getName))
.map(method -> lookupMethod(method))
.toArray();
case METHOD: {
case GET_METHOD: {
return psiClass.getVisibleSignatures()
.stream()
.map(MethodSignatureBackedByPsiMethod::getMethod)
@@ -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<PsiLiteralExpression> implements InsertHandler<LookupElement> {
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<PsiLiteralEx
final Object object = item.getObject();
if (object instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)object;
final Stream<PsiType> returnType = Stream.of(method.getReturnType())
.map(type -> type != null ? type : PsiType.VOID);
final Stream<PsiType> 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<String> 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);
}
}
}
@@ -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<PsiLiteral> 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<PsiLiteral> 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<? extends PsiElement> 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());
}
}
@@ -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<String> getMethodSignature(@Nullable PsiMethod method) {
if (method != null) {
final List<String> 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<String> 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;
@@ -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); ""
}