Java: Implemented "MethodHandle/VarHandle type mismatch" inspection (IDEA-167318)

This commit is contained in:
Pavel Dolgov
2017-03-13 15:40:58 +03:00
parent 6d88d9439b
commit 2d7375a526
15 changed files with 983 additions and 37 deletions
@@ -0,0 +1,494 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.reflectiveAccess;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.codeInspection.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ObjectUtils;
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;
import java.util.*;
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.*;
/**
* @author Pavel.Dolgov
*/
public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalInspectionTool {
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)));
private static final List<String> NO_ARGUMENT_CONSTRUCTOR_SIGNATURE = Collections.singletonList(PsiKeyword.VOID);
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression callExpression) {
super.visitMethodCallExpression(callExpression);
final PsiReferenceExpression methodExpression = callExpression.getMethodExpression();
final String methodName = methodExpression.getReferenceName();
if (methodName != null && KNOWN_METHOD_NAMES.contains(methodName)) {
final PsiMethod method = callExpression.resolveMethod();
final PsiClass psiClass = method != null ? method.getContainingClass() : null;
if (psiClass != null && JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP.equals(psiClass.getQualifiedName())) {
final PsiExpression[] arguments = callExpression.getArgumentList().getExpressions();
checkHandlerFactory(methodName, methodExpression, arguments, holder);
}
}
}
};
}
private static void checkHandlerFactory(@NotNull String factoryMethodName,
@NotNull PsiReferenceExpression factoryMethodExpression,
@NotNull PsiExpression[] arguments,
@NotNull ProblemsHolder holder) {
if (arguments.length == 2) {
if (FIND_CONSTRUCTOR.equals(factoryMethodName)) {
final PsiClass ownerClass = getReflectiveClass(arguments[0]);
if (ownerClass != null) {
final PsiExpression typeExpression = ParenthesesUtils.stripParentheses(arguments[1]);
checkConstructor(ownerClass, typeExpression, holder);
}
}
}
else if (arguments.length >= 3) {
final PsiClass ownerClass = getReflectiveClass(arguments[0]);
if (ownerClass != null) {
final PsiExpression nameExpression = ParenthesesUtils.stripParentheses(arguments[1]);
final PsiExpression nameDefinition = findDefinition(nameExpression);
final Object value = JavaConstantExpressionEvaluator.computeConstantExpression(nameDefinition, false);
final String name = ObjectUtils.tryCast(value, String.class);
if (!StringUtil.isEmpty(name)) {
final PsiExpression typeExpression = ParenthesesUtils.stripParentheses(arguments[2]);
switch (factoryMethodName) {
case FIND_GETTER:
case FIND_SETTER:
case FIND_VAR_HANDLE:
checkField(ownerClass, name, nameExpression, typeExpression, false, factoryMethodExpression, holder);
break;
case FIND_STATIC_GETTER:
case FIND_STATIC_SETTER:
case FIND_STATIC_VAR_HANDLE:
checkField(ownerClass, name, nameExpression, typeExpression, true, factoryMethodExpression, holder);
break;
case FIND_VIRTUAL:
checkMethod(ownerClass, name, nameExpression, typeExpression, false, factoryMethodExpression, holder);
break;
case FIND_STATIC:
checkMethod(ownerClass, name, nameExpression, typeExpression, true, factoryMethodExpression, holder);
break;
case FIND_SPECIAL:
checkMethod(ownerClass, name, nameExpression, typeExpression, false, factoryMethodExpression, holder);
break;
}
}
}
}
}
private static void checkConstructor(@NotNull PsiClass ownerClass,
@NotNull PsiExpression typeExpression,
@NotNull ProblemsHolder holder) {
final List<String> methodSignature = extractMethodSignature(typeExpression);
if (methodSignature != null) {
final List<PsiMethod> constructors = ContainerUtil.filter(ownerClass.getMethods(), PsiMethod::isConstructor);
LocalQuickFix[] fixes = null;
if (constructors.isEmpty()) {
if (!methodSignature.equals(NO_ARGUMENT_CONSTRUCTOR_SIGNATURE)) {
final LocalQuickFix fix = ReplaceSignatureQuickFix.createConstructorSignatureFix(ownerClass, NO_ARGUMENT_CONSTRUCTOR_SIGNATURE);
fixes = fix != null ? new LocalQuickFix[]{fix} : LocalQuickFix.EMPTY_ARRAY;
}
}
else if (!matchMethodSignature(constructors, methodSignature)) {
fixes = constructors.stream()
.map(constructor -> ReplaceSignatureQuickFix.createConstructorSignatureFix(ownerClass, constructor))
.filter(Objects::nonNull)
.toArray(LocalQuickFix[]::new);
}
if (fixes != null) {
final String declarationText = getConstructorDeclarationText(ownerClass, methodSignature);
if (declarationText != null) {
holder.registerProblem(typeExpression, JavaErrorMessages.message("cannot.resolve.constructor", declarationText), fixes);
}
}
}
}
private static void checkField(@NotNull PsiClass ownerClass,
@NotNull String name,
@NotNull PsiExpression nameExpression,
@NotNull PsiExpression typeExpression,
boolean isStatic,
@NotNull PsiReferenceExpression factoryMethodExpression,
@NotNull ProblemsHolder holder) {
final PsiField field = ownerClass.findFieldByName(name, true);
if (field == null) {
holder.registerProblem(nameExpression, InspectionsBundle.message("inspection.handle.signature.field.cannot.resolve", name));
return;
}
if (field.hasModifierProperty(PsiModifier.STATIC) != isStatic) {
final String factoryMethodName = factoryMethodExpression.getReferenceName();
final PsiElement factoryMethodNameElement = factoryMethodExpression.getReferenceNameElement();
if (factoryMethodName != null && factoryMethodNameElement != null) {
final LocalQuickFix fix = SwitchStaticnessQuickFix.createFix(factoryMethodName, isStatic);
final String message = InspectionsBundle.message(
isStatic ? "inspection.handle.signature.field.static" : "inspection.handle.signature.field.not.static", name);
holder.registerProblem(factoryMethodNameElement, message, fix != null ? new LocalQuickFix[]{fix} : LocalQuickFix.EMPTY_ARRAY);
return;
}
}
final ReflectiveType reflectiveType = getReflectiveType(typeExpression);
if (reflectiveType != null && !reflectiveType.isEqualTo(field.getType())) {
final String expectedTypeText = getTypeText(field.getType());
if (expectedTypeText != null) {
final String message = InspectionsBundle.message("inspection.handle.signature.field.type", name, expectedTypeText);
holder.registerProblem(typeExpression, message, new FieldTypeQuickFix(expectedTypeText));
}
}
}
private static void checkMethod(@NotNull PsiClass ownerClass,
@NotNull String name,
@NotNull PsiExpression nameExpression,
@NotNull PsiExpression typeExpression,
boolean isStatic,
@NotNull PsiReferenceExpression factoryMethodExpression,
@NotNull ProblemsHolder holder) {
final PsiMethod[] methods = ownerClass.findMethodsByName(name, true);
if (methods.length == 0) {
holder.registerProblem(nameExpression, JavaErrorMessages.message("cannot.resolve.method", name));
return;
}
final List<PsiMethod> filteredMethods =
ContainerUtil.filter(methods, method -> method.hasModifierProperty(PsiModifier.STATIC) == isStatic);
if (filteredMethods.isEmpty()) {
final String factoryMethodName = factoryMethodExpression.getReferenceName();
final PsiElement factoryMethodNameElement = factoryMethodExpression.getReferenceNameElement();
if (factoryMethodName != null && factoryMethodNameElement != null) {
final LocalQuickFix fix = SwitchStaticnessQuickFix.createFix(factoryMethodName, isStatic);
final String message = InspectionsBundle.message(
isStatic ? "inspection.handle.signature.method.static" : "inspection.handle.signature.method.not.static", name);
holder.registerProblem(factoryMethodNameElement, message, fix != null ? new LocalQuickFix[]{fix} : LocalQuickFix.EMPTY_ARRAY);
return;
}
}
final List<String> methodSignature = extractMethodSignature(typeExpression);
if (methodSignature != null && !matchMethodSignature(filteredMethods, methodSignature)) {
final String declarationText = getMethodDeclarationText(name, methodSignature);
if (declarationText != null) {
final LocalQuickFix[] fixes = filteredMethods.stream()
.map(ReplaceSignatureQuickFix::createMethodSignatureFix)
.filter(Objects::nonNull)
.toArray(LocalQuickFix[]::new);
holder.registerProblem(typeExpression, JavaErrorMessages.message("cannot.resolve.method", declarationText), fixes);
}
}
}
@Nullable
private static String getMethodDeclarationText(@NotNull String name, @NotNull List<String> methodSignature) {
if (methodSignature.isEmpty()) {
return null;
}
final String argumentTypes = methodSignature.stream().skip(1).collect(Collectors.joining(", "));
return methodSignature.get(0) + " " + name + "(" + argumentTypes + ")";
}
@Nullable
private static String getConstructorDeclarationText(@NotNull PsiClass ownerClass, List<String> methodSignature) {
final String name = ownerClass.getName();
if (name == null || methodSignature.isEmpty()) {
return null;
}
// Return type of the constructor should be 'void'. If it isn't so let's make that mistake more noticeable.
final String returnType = methodSignature.get(0);
final String fakeReturnType = !PsiKeyword.VOID.equals(returnType) ? returnType + " " : "";
final String argumentTypes = methodSignature.stream().skip(1).collect(Collectors.joining(", "));
return fakeReturnType + name + "(" + argumentTypes + ")";
}
private static boolean matchMethodSignature(@NotNull List<PsiMethod> methods, @NotNull List<String> expectedMethodSignature) {
return methods.stream()
.map(JavaLangInvokeHandleSignatureInspection::extractMethodSignature)
.anyMatch(expectedMethodSignature::equals);
}
/**
* Extract the types from arguments of MethodType.methodType(Class...) and MethodType.genericMethodType(int, boolean?)
*/
private static List<String> extractMethodSignature(@Nullable PsiExpression typeExpression) {
final PsiExpression typeDefinition = findDefinition(typeExpression);
if (typeDefinition instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)typeDefinition;
final String referenceName = methodCallExpression.getMethodExpression().getReferenceName();
final boolean isGeneric;
if (METHOD_TYPE.equals(referenceName)) {
isGeneric = false;
}
else if (GENERIC_METHOD_TYPE.equals(referenceName)) {
isGeneric = true;
}
else {
return 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 isGeneric ? extractGenericMethodSignature(arguments) : extractMethodSignature(arguments);
}
}
}
return null;
}
@Nullable
private static List<String> extractMethodSignature(PsiExpression[] arguments) {
final List<String> typeNames = Arrays.stream(arguments)
.map(JavaLangInvokeHandleSignatureInspection::getTypeText)
.collect(Collectors.toList());
return !typeNames.isEmpty() && !typeNames.contains(null) ? typeNames : null;
}
private static List<String> extractGenericMethodSignature(PsiExpression[] arguments) {
if (arguments.length == 0 || arguments.length > 2) {
return null;
}
final PsiExpression countArgument = ParenthesesUtils.stripParentheses(arguments[0]);
final Object countArgumentValue = JavaConstantExpressionEvaluator.computeConstantExpression(countArgument, false);
if (!(countArgumentValue instanceof Integer)) {
return null;
}
final int objectArgCount = (int)countArgumentValue;
if (objectArgCount < 0 || objectArgCount > 255) {
return null;
}
boolean finalArray = false;
if (arguments.length == 2) {
final PsiExpression hasArrayArgument = ParenthesesUtils.stripParentheses(arguments[1]);
final Object hasArrayArgumentValue = JavaConstantExpressionEvaluator.computeConstantExpression(hasArrayArgument, false);
if (!(hasArrayArgumentValue instanceof Boolean)) {
return null;
}
finalArray = (boolean)hasArrayArgumentValue;
if (finalArray && objectArgCount > 254) {
return null;
}
}
final List<String> typeNames = new ArrayList<>();
typeNames.add(CommonClassNames.JAVA_LANG_OBJECT); // return type
for (int i = 0; i < objectArgCount; i++) {
typeNames.add(CommonClassNames.JAVA_LANG_OBJECT);
}
if (finalArray) {
typeNames.add(CommonClassNames.JAVA_LANG_OBJECT + "[]");
}
return typeNames;
}
@Contract("null -> null")
@Nullable
private static List<String> extractMethodSignature(@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));
for (PsiParameter parameter : method.getParameterList().getParameters()) {
types.add(getTypeText(parameter.getType()));
}
if (!types.contains(null)) {
return types;
}
}
return null;
}
@Nullable
private static String getTypeText(@Nullable PsiExpression argument) {
final ReflectiveType reflectiveType = getReflectiveType(argument);
return reflectiveType != null ? reflectiveType.getQualifiedName() : null;
}
@Nullable
private static String getTypeText(@Nullable PsiType type) {
PsiType erased = TypeConversionUtil.erasure(type);
if (erased instanceof PsiEllipsisType) {
erased = ((PsiEllipsisType)erased).toArrayType();
}
return erased != null ? erased.getCanonicalText() : null;
}
private static class FieldTypeQuickFix implements LocalQuickFix {
private final String myFieldTypeText;
public FieldTypeQuickFix(String fieldTypeText) {myFieldTypeText = fieldTypeText;}
@Nls
@NotNull
@Override
public String getFamilyName() {
return InspectionsBundle.message("inspection.handle.signature.change.type.fix.name", myFieldTypeText);
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
final PsiExpression typeExpression = factory.createExpressionFromText(myFieldTypeText + ".class", element);
final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project);
styleManager.shortenClassReferences(element.replace(typeExpression));
}
}
private static class SwitchStaticnessQuickFix implements LocalQuickFix {
private static final Map<String, String> STATIC_TO_NON_STATIC = ContainerUtil.<String, String>immutableMapBuilder()
.put(FIND_STATIC_GETTER, FIND_GETTER)
.put(FIND_STATIC_SETTER, FIND_SETTER)
.put(FIND_STATIC_VAR_HANDLE, FIND_VAR_HANDLE)
.put(FIND_STATIC, FIND_VIRTUAL)
.build();
private static final Map<String, String> NON_STATIC_TO_STATIC = ContainerUtil.<String, String>immutableMapBuilder()
.put(FIND_GETTER, FIND_STATIC_GETTER)
.put(FIND_SETTER, FIND_STATIC_SETTER)
.put(FIND_VAR_HANDLE, FIND_STATIC_VAR_HANDLE)
.put(FIND_VIRTUAL, FIND_STATIC)
.build();
private final String myReplacementName;
public SwitchStaticnessQuickFix(@NotNull String replacementName) {
myReplacementName = replacementName;
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return InspectionsBundle.message("inspection.handle.signature.replace.with.fix.name", myReplacementName);
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
final PsiIdentifier identifier = factory.createIdentifier(myReplacementName);
final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project);
styleManager.shortenClassReferences(element.replace(identifier));
}
@Nullable
public static LocalQuickFix createFix(@NotNull String methodName, boolean isStatic) {
final String replacementName = isStatic ? STATIC_TO_NON_STATIC.get(methodName) : NON_STATIC_TO_STATIC.get(methodName);
return replacementName != null ? new SwitchStaticnessQuickFix(replacementName) : null;
}
}
private static class ReplaceSignatureQuickFix implements LocalQuickFix {
private final String myFixName;
private final List<String> myMethodSignature;
public ReplaceSignatureQuickFix(@NotNull String fixName, @NotNull List<String> methodSignature) {
myFixName = fixName;
myMethodSignature = methodSignature;
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return myFixName;
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
final String types = myMethodSignature.stream()
.map(text -> text + ".class")
.collect(Collectors.joining(", "));
final String text = JAVA_LANG_INVOKE_METHOD_TYPE + "." + METHOD_TYPE + "(" + types + ")";
final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
final PsiExpression replacement = factory.createExpressionFromText(text, element);
final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project);
styleManager.shortenClassReferences(element.replace(replacement));
}
@Nullable
public static LocalQuickFix createMethodSignatureFix(@Nullable PsiMethod method) {
final List<String> methodSignature = extractMethodSignature(method);
if (methodSignature != null) {
final String declarationText = getMethodDeclarationText(method.getName(), methodSignature);
if (declarationText != null) {
final String message = InspectionsBundle.message("inspection.handle.signature.use.method.fix.name", declarationText);
return new ReplaceSignatureQuickFix(message, methodSignature);
}
}
return null;
}
@Nullable
public static LocalQuickFix createConstructorSignatureFix(@NotNull PsiClass ownerClass, @Nullable PsiMethod constructor) {
final List<String> methodSignature = extractMethodSignature(constructor);
return methodSignature != null ? createConstructorSignatureFix(ownerClass, methodSignature) : null;
}
@Nullable
public static LocalQuickFix createConstructorSignatureFix(@NotNull PsiClass ownerClass, @NotNull List<String> methodSignature) {
final String declarationText = getConstructorDeclarationText(ownerClass, methodSignature);
if (declarationText != null) {
final String message = InspectionsBundle.message("inspection.handle.signature.use.constructor.fix.name", declarationText);
return new ReplaceSignatureQuickFix(message, methodSignature);
}
return null;
}
}
}
@@ -44,20 +44,26 @@ import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflection
* @author Pavel.Dolgov
*/
public class JavaLangInvokeHandleReference extends PsiReferenceBase<PsiLiteralExpression> implements InsertHandler<LookupElement> {
static final String JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP = "java.lang.invoke.MethodHandles.Lookup";
static final String JAVA_LANG_INVOKE_METHOD_TYPE = "java.lang.invoke.MethodType";
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";
static final String FIND_VIRTUAL = "findVirtual";
static final String FIND_STATIC = "findStatic";
static final String FIND_SPECIAL = "findSpecial";
public static final String FIND_VIRTUAL = "findVirtual";
public static final String FIND_STATIC = "findStatic";
public static final String FIND_SPECIAL = "findSpecial";
static final String FIND_GETTER = "findGetter";
static final String FIND_SETTER = "findSetter";
static final String FIND_STATIC_GETTER = "findStaticGetter";
static final String FIND_STATIC_SETTER = "findStaticSetter";
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";
static final String FIND_VAR_HANDLE = "findVarHandle";
static final String FIND_STATIC_VAR_HANDLE = "findStaticVarHandle";
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;
@@ -48,10 +48,7 @@ public class JavaReflectionReferenceContributor extends PsiReferenceContributor
private static final ElementPattern<? extends PsiElement> METHOD_HANDLE_PATTERN = psiLiteral()
.methodCallParameter(1, psiMethod()
.withName(FIND_VIRTUAL, FIND_STATIC, FIND_SPECIAL,
FIND_GETTER, FIND_SETTER,
FIND_STATIC_GETTER, FIND_STATIC_SETTER,
FIND_VAR_HANDLE, FIND_STATIC_VAR_HANDLE)
.withName(HANDLE_FACTORY_METHOD_NAMES)
.definedInClass(JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP));
@Override
@@ -26,10 +26,7 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.util.*;
import com.siyeh.ig.psiutils.DeclarationSearchUtils;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.Contract;
@@ -42,18 +39,18 @@ import java.util.stream.Collectors;
/**
* @author Pavel.Dolgov
*/
class JavaReflectionReferenceUtil {
public class JavaReflectionReferenceUtil {
private static final RecursionGuard ourGuard = RecursionManager.createGuard("JavaLangClassMemberReference");
@Nullable
static PsiClass getReflectiveClass(@Nullable PsiExpression context) {
public static ReflectiveType getReflectiveType(@Nullable PsiExpression context) {
context = ParenthesesUtils.stripParentheses(context);
if (context == null) {
return null;
}
if (context instanceof PsiClassObjectAccessExpression) { // special case for JDK 1.4
PsiTypeElement operand = ((PsiClassObjectAccessExpression)context).getOperand();
return PsiTypesUtil.getPsiClass(operand.getType());
final PsiTypeElement operand = ((PsiClassObjectAccessExpression)context).getOperand();
return ReflectiveType.create(operand.getType(), context);
}
if (context instanceof PsiMethodCallExpression) {
@@ -64,14 +61,10 @@ class JavaReflectionReferenceUtil {
if (method != null && isJavaLangClass(method.getContainingClass())) {
final PsiExpression[] expressions = methodCall.getArgumentList().getExpressions();
if (expressions.length == 1) {
PsiExpression argument = ParenthesesUtils.stripParentheses(expressions[0]);
if (argument instanceof PsiReferenceExpression) {
argument = findVariableDefinition(((PsiReferenceExpression)argument));
}
final PsiExpression argument = findDefinition(ParenthesesUtils.stripParentheses(expressions[0]));
final Object value = JavaConstantExpressionEvaluator.computeConstantExpression(argument, false);
if (value instanceof String) {
final Project project = context.getProject();
return JavaPsiFacade.getInstance(project).findClass((String)value, GlobalSearchScope.allScope(project));
return ReflectiveType.create(findClass((String)value, context));
}
}
}
@@ -83,21 +76,18 @@ class JavaReflectionReferenceUtil {
if (qualifier instanceof PsiReferenceExpression) {
final PsiExpression definition = findVariableDefinition((PsiReferenceExpression)qualifier);
if (definition != null) {
final PsiClass actualClass = PsiTypesUtil.getPsiClass(definition.getType());
if (actualClass != null) {
return actualClass;
}
return ReflectiveType.create(definition.getType(), context);
}
}
//TODO type of the qualifier may be a supertype of the actual value - need to compute the type of the actual value
// otherwise getDeclaredField and getDeclaredMethod may work not reliably
if (qualifier != null) {
return PsiTypesUtil.getPsiClass(qualifier.getType());
return ReflectiveType.create(qualifier.getType(), context);
}
}
}
}
PsiType type = context.getType();
final PsiType type = context.getType();
if (type instanceof PsiClassType) {
PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
if (!isJavaLangClass(resolveResult.getElement())) return null;
@@ -109,7 +99,7 @@ class JavaReflectionReferenceUtil {
}
final PsiClass argumentClass = PsiTypesUtil.getPsiClass(typeArgument);
if (argumentClass != null && !isJavaLangObject(argumentClass)) {
return argumentClass;
return ReflectiveType.create(argumentClass);
}
}
}
@@ -118,18 +108,34 @@ class JavaReflectionReferenceUtil {
if (resolved instanceof PsiVariable) {
final PsiExpression definition = findVariableDefinition((PsiReferenceExpression)context, (PsiVariable)resolved);
if (definition != null) {
return ourGuard.doPreventingRecursion(resolved, false, () -> getReflectiveClass(definition));
return ourGuard.doPreventingRecursion(resolved, false, () -> getReflectiveType(definition));
}
}
}
return null;
}
@Nullable
public static PsiClass getReflectiveClass(PsiExpression context) {
final ReflectiveType reflectiveType = getReflectiveType(context);
return reflectiveType != null ? reflectiveType.myPsiClass : null;
}
@Nullable
public static PsiExpression findDefinition(@Nullable PsiExpression expression) {
if (expression instanceof PsiReferenceExpression) {
return findVariableDefinition((PsiReferenceExpression)expression);
}
return expression;
}
@Nullable
private static PsiExpression findVariableDefinition(@NotNull PsiReferenceExpression referenceExpression) {
final PsiElement resolved = referenceExpression.resolve();
return resolved instanceof PsiVariable ? findVariableDefinition(referenceExpression, (PsiVariable)resolved) : null;
}
@Nullable
private static PsiExpression findVariableDefinition(@NotNull PsiReferenceExpression referenceExpression, @NotNull PsiVariable variable) {
if (variable.hasModifierProperty(PsiModifier.FINAL)) {
final PsiExpression initializer = variable.getInitializer();
@@ -140,6 +146,11 @@ class JavaReflectionReferenceUtil {
return DeclarationSearchUtils.findDefinition(referenceExpression, variable);
}
private static PsiClass findClass(@NotNull String qualifiedName, @NotNull PsiElement context) {
final Project project = context.getProject();
return JavaPsiFacade.getInstance(project).findClass(qualifiedName, GlobalSearchScope.allScope(project));
}
static boolean isJavaLangClass(@Nullable PsiClass aClass) {
return aClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(aClass.getQualifiedName());
}
@@ -209,4 +220,87 @@ class JavaReflectionReferenceUtil {
context.commitDocument();
shortenArgumentsClassReferences(context);
}
public static class ReflectiveType {
final PsiClass myPsiClass;
final PsiPrimitiveType myPrimitiveType;
final int myArrayDimensions;
public ReflectiveType(PsiClass psiClass, PsiPrimitiveType primitiveType, int arrayDimensions) {
myPsiClass = psiClass;
myPrimitiveType = primitiveType;
myArrayDimensions = arrayDimensions;
}
@Nullable
public String getQualifiedName() {
String text = null;
if (myPrimitiveType != null) {
text = myPrimitiveType.getCanonicalText();
}
else if (myPsiClass != null) {
text = myPsiClass.getQualifiedName();
}
if (myArrayDimensions == 0 || text == null) {
return text;
}
final StringBuilder sb = new StringBuilder(text);
for (int i = 0; i < myArrayDimensions; i++) {
sb.append("[]");
}
return sb.toString();
}
@Override
public String toString() {
final String name = getQualifiedName();
return name != null ? name : "null";
}
public boolean isEqualTo(@Nullable PsiType otherType) {
if (otherType == null || myArrayDimensions != otherType.getArrayDimensions()) {
return false;
}
final PsiType otherComponentType = otherType.getDeepComponentType();
if (myPrimitiveType != null) {
return myPrimitiveType.equals(otherComponentType);
}
if (myPsiClass != null) {
final PsiClass otherClass = PsiUtil.resolveClassInType(otherComponentType);
if (otherClass != null) {
final String otherClassName = otherClass instanceof PsiTypeParameter
? CommonClassNames.JAVA_LANG_OBJECT : otherClass.getQualifiedName();
if (otherClassName != null) {
return otherClassName.equals(myPsiClass.getQualifiedName());
}
}
}
return false;
}
@Nullable
public static ReflectiveType create(@Nullable PsiType originalType, @NotNull PsiElement context) {
if (originalType == null) {
return null;
}
final int arrayDimensions = originalType.getArrayDimensions();
final PsiType type = originalType.getDeepComponentType();
if (type instanceof PsiPrimitiveType) {
return new ReflectiveType(null, (PsiPrimitiveType)type, arrayDimensions);
}
PsiClass psiClass = PsiUtil.resolveClassInType(type);
if (psiClass instanceof PsiTypeParameter) {
psiClass = findClass(CommonClassNames.JAVA_LANG_OBJECT, context);
}
if (psiClass != null) {
return new ReflectiveType(psiClass, null, arrayDimensions);
}
return null;
}
@Nullable
public static ReflectiveType create(@Nullable PsiClass psiClass) {
return psiClass != null ? new ReflectiveType(psiClass, null, 0) : null;
}
}
}
@@ -0,0 +1,43 @@
import java.lang.invoke.*;
class Main {
void foo() throws Exception {
MethodHandles.Lookup l = MethodHandles.lookup();
Class c = Test.class;
l.findConstructor(Test.class, MethodType.methodType(void.class));
l.findConstructor(Test.class, MethodType.methodType(void.class, int.class));
l.findConstructor(Test.class, MethodType.methodType(void.class, int.class, String.class));
l.findConstructor(Test.class, MethodType.methodType(void.class, int.class, String[].class));
l.findConstructor(Test.class, MethodType.methodType(void.class, String[][].class));
l.findConstructor((c), MethodType.methodType(void.class));
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 'Test Test()'">MethodType.methodType(Test.class)</warning>);
l.findConstructor(Test.class, <warning descr="Cannot resolve constructor 'Test Test(int)'">MethodType.methodType(Test.class, int.class)</warning>);
l.findConstructor(Test.class, <warning descr="Cannot resolve constructor 'Test(java.lang.String)'">MethodType.methodType(void.class, String.class)</warning>);
l.findConstructor(Test.class, <warning descr="Cannot resolve constructor 'Test(int, java.lang.String[][])'">MethodType.methodType(void.class, int.class, String[][].class)</warning>);
l.findConstructor(Test.class, <warning descr="Cannot resolve constructor 'Test(java.lang.String[])'">MethodType.methodType(void.class, String[].class)</warning>);
l.findConstructor(WithDefault.class, MethodType.methodType(void.class));
l.findConstructor(Class.forName("WithDefault"), MethodType.methodType(void.class));
l.findConstructor(NoDefault.class, <warning descr="Cannot resolve constructor 'NoDefault()'">MethodType.methodType(void.class)</warning>);
l.findConstructor(Class.forName("NoDefault"), <warning descr="Cannot resolve constructor 'NoDefault()'">MethodType.methodType(void.class)</warning>);
}
}
class Test {
public Test() {}
public Test(int a) {}
public Test(int a, String b) {}
public Test(int a, String... b) {}
public Test(String[]... b) {}
}
class WithDefault {
}
class NoDefault {
public NoDefault(int n) {}
}
@@ -0,0 +1,25 @@
import java.lang.invoke.*;
class Main {
void foo() throws Exception {
MethodHandles.Lookup l = MethodHandles.lookup();
l.findVirtual(Test.class, "method1", MethodType.genericMethodType(2));
l.findVirtual(Test.class, "method1", MethodType.genericMethodType(2, false));
l.findVirtual(Test.class, "method1", <warning descr="Cannot resolve method 'java.lang.Object method1(java.lang.Object, java.lang.Object, java.lang.Object[])'">MethodType.genericMethodType(2, true)</warning>);
l.findVirtual(Test.class, "method1", <warning descr="Cannot resolve method 'java.lang.Object method1(java.lang.Object)'">MethodType.genericMethodType(1)</warning>);
l.findVirtual(Test.class, "method1", <warning descr="Cannot resolve method 'java.lang.Object method1(java.lang.Object, java.lang.Object, java.lang.Object)'">MethodType.genericMethodType(3)</warning>);
l.findVirtual(Test.class, "method2", MethodType.genericMethodType(2, true));
l.findVirtual(Test.class, "method2", <warning descr="Cannot resolve method 'java.lang.Object method2(java.lang.Object, java.lang.Object)'">MethodType.genericMethodType(2, false)</warning>);
l.findVirtual(Test.class, "method2", <warning descr="Cannot resolve method 'java.lang.Object method2(java.lang.Object)'">MethodType.genericMethodType(1)</warning>);
l.findVirtual(Test.class, "method2", <warning descr="Cannot resolve method 'java.lang.Object method2(java.lang.Object, java.lang.Object[])'">MethodType.genericMethodType(1, true)</warning>);
l.findVirtual(Test.class, "method2", <warning descr="Cannot resolve method 'java.lang.Object method2(java.lang.Object, java.lang.Object)'">MethodType.genericMethodType(2)</warning>);
l.findVirtual(Test.class, "method2", <warning descr="Cannot resolve method 'java.lang.Object method2(java.lang.Object, java.lang.Object, java.lang.Object)'">MethodType.genericMethodType(3)</warning>);
}
}
class Test {
public <T> T method1(T a, T b) {return null;}
public <T> T method2(T a, T b, T... c) {return null;}
}
@@ -0,0 +1,47 @@
import java.lang.invoke.*;
import java.util.*;
class Main {
void foo() throws Exception {
MethodHandles.Lookup l = MethodHandles.lookup();
l.findGetter(Test.class, "myInt", int.class);
l.findGetter(Test.class, "myInts", int[].class);
l.findGetter(Test.class, "myList", List.class);
l.findGetter(Test.class, "myLists", List[].class);
l.findGetter(Test.class, "myString", String.class);
l.findStaticGetter(Test.class, "ourInt", int.class);
l.findStaticGetter(Test.class, "ourInts", int[].class);
l.findStaticGetter(Test.class, "ourList", List.class);
l.findStaticGetter(Test.class, "ourLists", List[].class);
l.findStaticGetter(Test.class, "ourString", String.class);
l.findGetter(Test.class, <warning descr="Cannot resolve field 'doesntExist'">"doesntExist"</warning>, String.class);
l.findStaticGetter(Test.class, <warning descr="Cannot resolve field 'doesntExist'">"doesntExist"</warning>, String.class);
l.findGetter(Test.class, "myInt", <warning descr="The type of field 'myInt' is 'int'">void.class</warning>);
l.findGetter(Test.class, "myInts", <warning descr="The type of field 'myInts' is 'int[]'">int.class</warning>);
l.findGetter(Test.class, "myString", <warning descr="The type of field 'myString' is 'java.lang.String'">List.class</warning>);
l.findStaticGetter(Test.class, "ourInt", <warning descr="The type of field 'ourInt' is 'int'">void.class</warning>);
l.findStaticGetter(Test.class, "ourInts", <warning descr="The type of field 'ourInts' is 'int[]'">int.class</warning>);
l.findStaticGetter(Test.class, "ourString", <warning descr="The type of field 'ourString' is 'java.lang.String'">List.class</warning>);
}
}
class Test {
public int myInt;
public String myString;
public int[] myInts;
public List<String> myList;
@SuppressWarnings("unchecked")
public List<String>[] myLists;
public static int ourInt;
public static String ourString;
public static int[] ourInts;
public static List<String> ourList;
@SuppressWarnings("unchecked")
public static List<String>[] ourLists;
}
@@ -0,0 +1,41 @@
import java.lang.invoke.*;
class Main {
void foo() throws Exception {
MethodHandles.Lookup l = MethodHandles.lookup();
l.findVirtual(Test.class, "method", MethodType.methodType(void.class));
l.findVirtual(Test.class, "method", MethodType.methodType(int.class, int.class));
l.findVirtual(Test.class, "method", MethodType.methodType(boolean.class, short.class, char.class));
l.findVirtual(Test.class, "method", MethodType.methodType(int.class, int.class, int[].class));
l.findVirtual(Test.class, "method", MethodType.methodType(int.class, long.class, int[][].class));
l.findVirtual(Test.class, "method", MethodType.methodType(int.class, long.class, int[][][].class));
l.findVirtual(Test.class, "method", MethodType.methodType(Object.class, Object.class));
l.findVirtual(Test.class, "method", MethodType.methodType(Object.class, Object[].class));
l.findVirtual(Test.class, "method", MethodType.methodType(Object.class, Object[][].class));
l.findVirtual(Test.class, "method", MethodType.genericMethodType(1));
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'void method(void)'">MethodType.methodType(void.class, void.class)</warning>);
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'void method(int)'">MethodType.methodType(void.class, int.class)</warning>);
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'short method(char)'">MethodType.methodType(short.class, char.class)</warning>);
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'int method(int[])'">MethodType.methodType(int.class, int[].class)</warning>);
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'int method(int[][])'">MethodType.methodType(int.class, int[][].class)</warning>);
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'java.lang.Object[] method(java.lang.Object[])'">MethodType.methodType(Object[].class, Object[].class)</warning>);
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'java.lang.Object[][] method(java.lang.Object[][])'">MethodType.methodType(Object[][].class, Object[][].class)</warning>);
l.<warning descr="Method 'method' is 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));
}
}
class Test {
public void method() {}
public int method(int n) {return n;}
public boolean method(short a, char b) {return true;}
public int method(int n, int... a) {return n;}
public int method(long n, int[]... a) {return a.length;}
public int method(long n, int[][][] a) {return a.length;}
public Object method(Object o) {return o;}
public Object method(Object[] o) {return o;}
public Object method(Object[][] o) {return o;}
}
@@ -0,0 +1,47 @@
import java.lang.invoke.*;
import java.util.*;
class Main {
void foo() throws Exception {
MethodHandles.Lookup l = MethodHandles.lookup();
l.findSetter(Test.class, "myInt", int.class);
l.findSetter(Test.class, "myInts", int[].class);
l.findSetter(Test.class, "myList", List.class);
l.findSetter(Test.class, "myLists", List[].class);
l.findSetter(Test.class, "myString", String.class);
l.findStaticSetter(Test.class, "ourInt", int.class);
l.findStaticSetter(Test.class, "ourInts", int[].class);
l.findStaticSetter(Test.class, "ourList", List.class);
l.findStaticSetter(Test.class, "ourLists", List[].class);
l.findStaticSetter(Test.class, "ourString", String.class);
l.findSetter(Test.class, <warning descr="Cannot resolve field 'doesntExist'">"doesntExist"</warning>, String.class);
l.findStaticSetter(Test.class, <warning descr="Cannot resolve field 'doesntExist'">"doesntExist"</warning>, String.class);
l.findSetter(Test.class, "myInt", <warning descr="The type of field 'myInt' is 'int'">void.class</warning>);
l.findSetter(Test.class, "myInts", <warning descr="The type of field 'myInts' is 'int[]'">int.class</warning>);
l.findSetter(Test.class, "myString", <warning descr="The type of field 'myString' is 'java.lang.String'">List.class</warning>);
l.findStaticSetter(Test.class, "ourInt", <warning descr="The type of field 'ourInt' is 'int'">void.class</warning>);
l.findStaticSetter(Test.class, "ourInts", <warning descr="The type of field 'ourInts' is 'int[]'">int.class</warning>);
l.findStaticSetter(Test.class, "ourString", <warning descr="The type of field 'ourString' is 'java.lang.String'">List.class</warning>);
}
}
class Test {
public int myInt;
public String myString;
public int[] myInts;
public List<String> myList;
@SuppressWarnings("unchecked")
public List<String>[] myLists;
public static int ourInt;
public static String ourString;
public static int[] ourInts;
public static List<String> ourList;
@SuppressWarnings("unchecked")
public static List<String>[] ourLists;
}
@@ -0,0 +1,24 @@
import java.lang.invoke.*;
class Main {
void foo() throws Exception {
MethodHandles.Lookup l = MethodHandles.lookup();
l.findStatic(Test.class, "method1", MethodType.methodType(void.class));
l.findStatic(Test.class, "method2", MethodType.methodType(String.class, String.class));
l.findStatic(Test.class, "method3", MethodType.methodType(String.class, String.class, String[].class));
l.findStatic(Test.class, "method1", <warning descr="Cannot resolve method 'Test method1()'">MethodType.methodType(Test.class)</warning>);
l.findStatic(Test.class, "method2", <warning descr="Cannot resolve method 'int method2(java.lang.String)'">MethodType.methodType(int.class, String.class)</warning>);
l.findStatic(Test.class, "method3", <warning descr="Cannot resolve method 'java.lang.String method3()'">MethodType.methodType(String.class)</warning>);
l.<warning descr="Method 'method1' is not 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));
}
}
class Test {
public static void method1() {}
public static String method2(String a) {return a;}
public static String method3(String a, String... b) {return a;}
}
@@ -0,0 +1,47 @@
import java.lang.invoke.*;
import java.util.*;
class Main {
void foo() throws Exception {
MethodHandles.Lookup l = MethodHandles.lookup();
l.findVarHandle(Test.class, "myInt", int.class);
l.findVarHandle(Test.class, "myInts", int[].class);
l.findVarHandle(Test.class, "myList", List.class);
l.findVarHandle(Test.class, "myLists", List[].class);
l.findVarHandle(Test.class, "myString", String.class);
l.findStaticVarHandle(Test.class, "ourInt", int.class);
l.findStaticVarHandle(Test.class, "ourInts", int[].class);
l.findStaticVarHandle(Test.class, "ourList", List.class);
l.findStaticVarHandle(Test.class, "ourLists", List[].class);
l.findStaticVarHandle(Test.class, "ourString", String.class);
l.findVarHandle(Test.class, <warning descr="Cannot resolve field 'doesntExist'">"doesntExist"</warning>, String.class);
l.findStaticVarHandle(Test.class, <warning descr="Cannot resolve field 'doesntExist'">"doesntExist"</warning>, String.class);
l.findVarHandle(Test.class, "myInt", <warning descr="The type of field 'myInt' is 'int'">void.class</warning>);
l.findVarHandle(Test.class, "myInts", <warning descr="The type of field 'myInts' is 'int[]'">int.class</warning>);
l.findVarHandle(Test.class, "myString", <warning descr="The type of field 'myString' is 'java.lang.String'">List.class</warning>);
l.findStaticVarHandle(Test.class, "ourInt", <warning descr="The type of field 'ourInt' is 'int'">void.class</warning>);
l.findStaticVarHandle(Test.class, "ourInts", <warning descr="The type of field 'ourInts' is 'int[]'">int.class</warning>);
l.findStaticVarHandle(Test.class, "ourString", <warning descr="The type of field 'ourString' is 'java.lang.String'">List.class</warning>);
}
}
class Test {
public int myInt;
public String myString;
public int[] myInts;
public List<String> myList;
@SuppressWarnings("unchecked")
public List<String>[] myLists;
public static int ourInt;
public static String ourString;
public static int[] ourInts;
public static List<String> ourList;
@SuppressWarnings("unchecked")
public static List<String>[] ourLists;
}
@@ -0,0 +1,56 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection
import com.intellij.JavaTestUtil
import com.intellij.codeInspection.reflectiveAccess.JavaLangInvokeHandleSignatureInspection
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
/**
* @author Pavel.Dolgov
*/
class JavaLangInvokeHandleSignatureTest : LightCodeInsightFixtureTestCase() {
override fun getBasePath() = JavaTestUtil.getRelativeJavaTestDataPath() + "/inspection/invokeHandleSignature"
override fun getProjectDescriptor(): LightProjectDescriptor {
return LightCodeInsightFixtureTestCase.JAVA_9
}
override fun setUp() {
super.setUp()
myFixture.enableInspections(JavaLangInvokeHandleSignatureInspection())
}
fun testGenericMethod() = doTest()
fun testOverloadedMethod() = doTest()
fun testStaticMethod() = doTest()
fun testConstructor() = doTest()
fun testVarHandle() = doTest()
fun testGetter() = doTest()
fun testSetter() = doTest()
private fun doTest() {
myFixture.testHighlighting(getTestName(false) + ".java")
}
}
@@ -557,6 +557,7 @@ group.names.javaee.issues=Java EE issues
group.names.properties.files=Properties Files
group.names.xml=XML
group.names.toString.issues=toString() issues
group.names.reflective.access.issues=Reflective access issues
duplicate.property.display.name=Duplicate Property
@@ -798,3 +799,17 @@ inspection.null.value.for.optional.context.parameter=parameter
inspection.null.value.for.optional.context.lambda=lambda expression
inspection.null.value.for.optional.context.return=return statement
inspection.null.value.for.optional.context.declaration=declaration
inspection.handle.signature.name=MethodHandle/VarHandle type mismatch
inspection.handle.signature.field.static=Field ''{0}'' is static
inspection.handle.signature.field.not.static=Field ''{0}'' is not static
inspection.handle.signature.field.type=The type of field ''{0}'' is ''{1}''
inspection.handle.signature.field.cannot.resolve=Cannot resolve field ''{0}''
inspection.handle.signature.method.static=Method ''{0}'' is static
inspection.handle.signature.method.not.static=Method ''{0}'' is not static
inspection.handle.signature.change.type.fix.name=Change type to ''{0}''
inspection.handle.signature.replace.with.fix.name=Replace with ''{0}''
inspection.handle.signature.use.method.fix.name=Use method ''{0}''
inspection.handle.signature.use.constructor.fix.name=Use constructor ''{0}''
@@ -0,0 +1,6 @@
<html>
<body>
This inspection detects the case where the type of a VarHandle or the signature of a MethodHandle doesn't match the actual field or method.
<p>It also detects if a static field/method is accessed in non-static way and vice versa.
</body>
</html>
+4
View File
@@ -923,6 +923,10 @@
groupPath="Java" groupBundle="messages.InspectionsBundle" groupKey="group.names.naming.conventions"
bundle="messages.InspectionsBundle" key="inspection.java.module.naming"
implementationClass="com.intellij.codeInspection.java19modules.JavaModuleNamingInspection"/>
<localInspection language="JAVA" shortName="JavaLangInvokeHandleSignature" enabledByDefault="true" level="WARNING"
groupPath="Java" groupBundle="messages.InspectionsBundle" groupKey="group.names.reflective.access.issues"
bundle="messages.InspectionsBundle" key="inspection.handle.signature.name"
implementationClass="com.intellij.codeInspection.reflectiveAccess.JavaLangInvokeHandleSignatureInspection"/>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.SplitIfAction</className>