Java: Implemented inspection "Reflective invocation arguments mismatch" (IDEA-167270)

This commit is contained in:
Pavel Dolgov
2017-03-29 12:54:08 +03:00
parent 8ec767e2d9
commit 8405b6ccb5
10 changed files with 768 additions and 7 deletions
@@ -0,0 +1,228 @@
/*
* 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.codeInspection.BaseJavaBatchLocalInspectionTool;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.siyeh.ig.psiutils.MethodCallUtils;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import static com.intellij.psi.CommonClassNames.JAVA_LANG_CLASS;
import static com.intellij.psi.CommonClassNames.JAVA_LANG_OBJECT;
import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*;
/**
* @author Pavel.Dolgov
*/
public class JavaReflectionInvocationInspection extends BaseJavaBatchLocalInspectionTool {
private static final String JAVA_LANG_REFLECT_METHOD = "java.lang.reflect.Method";
private static final String JAVA_LANG_REFLECT_CONSTRUCTOR = "java.lang.reflect.Constructor";
private static final String INVOKE = "invoke";
private static final String NEW_INSTANCE = "newInstance";
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression methodCall) {
super.visitMethodCallExpression(methodCall);
if (isCallToMethod(methodCall, JAVA_LANG_REFLECT_METHOD, INVOKE)) {
checkReflectionCall(methodCall, 1, holder, JavaReflectionInvocationInspection::isReflectionMethod);
}
else if (isCallToMethod(methodCall, JAVA_LANG_REFLECT_CONSTRUCTOR, NEW_INSTANCE)) {
checkReflectionCall(methodCall, 0, holder, JavaReflectionInvocationInspection::isReflectionConstructor);
}
}
};
}
private static boolean isReflectionMethod(PsiMethodCallExpression callExpression) {
return isCallToMethod(callExpression, JAVA_LANG_CLASS, GET_METHOD) ||
isCallToMethod(callExpression, JAVA_LANG_CLASS, GET_DECLARED_METHOD);
}
private static boolean isReflectionConstructor(PsiMethodCallExpression callExpression) {
return isCallToMethod(callExpression, JAVA_LANG_CLASS, GET_CONSTRUCTOR) ||
isCallToMethod(callExpression, JAVA_LANG_CLASS, GET_DECLARED_CONSTRUCTOR);
}
private static void checkReflectionCall(@NotNull PsiMethodCallExpression methodCall,
int argumentOffset,
@NotNull ProblemsHolder holder,
@NotNull Predicate<PsiMethodCallExpression> methodPredicate) {
final List<PsiExpression> requiredTypes =
getRequiredMethodArguments(methodCall.getMethodExpression().getQualifierExpression(), argumentOffset, methodPredicate);
if (requiredTypes != null) {
final Arguments actualArguments = getActualMethodArguments(methodCall, argumentOffset);
if (actualArguments != null) {
final PsiExpressionList argumentList = methodCall.getArgumentList();
if (requiredTypes.size() != actualArguments.expressions.length) {
if (actualArguments.varargAsArray) {
final PsiExpression[] expressions = argumentList.getExpressions();
final PsiElement element = expressions.length == argumentOffset + 1 ? expressions[argumentOffset] : argumentList;
holder.registerProblem(element, InspectionsBundle.message(
"inspection.reflection.invocation.item.count", requiredTypes.size()));
}
else {
holder.registerProblem(argumentList, InspectionsBundle.message(
"inspection.reflection.invocation.argument.count", requiredTypes.size() + argumentOffset));
}
return;
}
for (int i = 0; i < requiredTypes.size(); i++) {
final ReflectiveType requiredType = getReflectiveType(requiredTypes.get(i));
if (requiredType != null) {
final PsiExpression argument = actualArguments.expressions[i];
final PsiType actualType = argument.getType();
if (actualType != null && !requiredType.isAssignableFrom(actualType)) {
if (PsiTreeUtil.isAncestor(argumentList, argument, false)) {
// either varargs or in-place arguments array
holder.registerProblem(argument, InspectionsBundle.message(actualArguments.varargAsArray
? "inspection.reflection.invocation.item.not.assignable"
: "inspection.reflection.invocation.argument.not.assignable",
requiredType.getQualifiedName()));
}
else {
// arguments array in a variable
final PsiExpression[] expressions = argumentList.getExpressions();
final PsiElement element = expressions.length == argumentOffset + 1 ? expressions[argumentOffset] : argumentList;
holder.registerProblem(element, InspectionsBundle.message(
"inspection.reflection.invocation.item.number.not.assignable", i, requiredType.getQualifiedName()));
}
}
}
}
}
}
}
@Nullable
private static List<PsiExpression> getRequiredMethodArguments(@Nullable PsiExpression qualifier,
int argumentOffset,
@NotNull Predicate<PsiMethodCallExpression> methodPredicate) {
final PsiExpression definition = findDefinition(ParenthesesUtils.stripParentheses(qualifier));
if (definition instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression definitionCall = (PsiMethodCallExpression)definition;
if (methodPredicate.test(definitionCall)) {
final PsiExpression[] arguments = definitionCall.getArgumentList().getExpressions();
if (arguments.length == argumentOffset + 1) {
final PsiExpression[] arrayElements = getVarargAsArray(arguments[argumentOffset]);
if (arrayElements != null) {
return Arrays.asList(arrayElements);
}
}
if (arguments.length >= argumentOffset) {
return Arrays.asList(arguments).subList(argumentOffset, arguments.length);
}
}
}
return null;
}
@Nullable
private static Arguments getActualMethodArguments(PsiMethodCallExpression methodCall, int argumentOffset) {
final PsiExpression[] arguments = methodCall.getArgumentList().getExpressions();
if (arguments.length == argumentOffset + 1) {
final PsiExpression[] expressions = getVarargAsArray(arguments[argumentOffset]);
if (expressions != null) {
return new Arguments(expressions, true);
}
}
if (arguments.length >= argumentOffset) {
final PsiExpression[] expressions = argumentOffset != 0 ? Arrays.copyOfRange(arguments, argumentOffset, arguments.length) : arguments;
for (int i = 0; i < expressions.length; i++) {
final PsiExpression castOperand = unwrapDisambiguatingCastToObject(expressions[i]);
if (castOperand != null) {
expressions[i] = castOperand;
}
}
return new Arguments(expressions, false);
}
return null;
}
@Nullable
private static PsiExpression[] getVarargAsArray(@Nullable PsiExpression maybeArray) {
if (maybeArray != null) {
final PsiType type = maybeArray.getType();
if (type instanceof PsiArrayType &&
type.getArrayDimensions() == 1 &&
type.getDeepComponentType() instanceof PsiClassType) {
final PsiExpression argumentsDefinition = findDefinition(maybeArray);
if (argumentsDefinition instanceof PsiArrayInitializerExpression) {
return ((PsiArrayInitializerExpression)argumentsDefinition).getInitializers();
}
if (argumentsDefinition instanceof PsiNewExpression) {
final PsiArrayInitializerExpression arrayInitializer = ((PsiNewExpression)argumentsDefinition).getArrayInitializer();
if (arrayInitializer != null) {
return arrayInitializer.getInitializers();
}
final PsiExpression[] dimensions = ((PsiNewExpression)argumentsDefinition).getArrayDimensions();
if (dimensions.length == 1) { // special case: new Object[0]
final Integer itemCount = computeConstantExpression(findDefinition(dimensions[0]), Integer.class);
if (itemCount != null && itemCount == 0) {
return PsiExpression.EMPTY_ARRAY;
}
}
}
}
}
return null;
}
@Nullable
private static PsiExpression unwrapDisambiguatingCastToObject(@Nullable PsiExpression expression) {
if (expression instanceof PsiTypeCastExpression) {
final PsiTypeCastExpression typeCast = (PsiTypeCastExpression)expression;
final PsiTypeElement castElement = typeCast.getCastType();
if (castElement != null && castElement.getType().equalsToText(JAVA_LANG_OBJECT)) {
return typeCast.getOperand();
}
}
return null;
}
private static boolean isCallToMethod(PsiMethodCallExpression methodCall, String className, String methodName) {
return MethodCallUtils.isCallToMethod(methodCall, className, null, methodName, (PsiType[])null);
}
private static class Arguments {
final PsiExpression[] expressions;
final boolean varargAsArray;
public Arguments(PsiExpression[] expressions, boolean varargAsArray) {
this.expressions = expressions;
this.varargAsArray = varargAsArray;
}
}
}
@@ -27,10 +27,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.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.PlatformIcons;
@@ -80,6 +77,8 @@ public class JavaReflectionReferenceUtil {
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 GET_CONSTRUCTOR = "getConstructor";
public static final String GET_DECLARED_CONSTRUCTOR = "getDeclaredConstructor";
public static final String JAVA_LANG_CLASS_LOADER = "java.lang.ClassLoader";
public static final String FOR_NAME = "forName";
@@ -176,8 +175,10 @@ public class JavaReflectionReferenceUtil {
@Nullable
public static PsiExpression findDefinition(@Nullable PsiExpression expression) {
if (expression instanceof PsiReferenceExpression) {
return findVariableDefinition((PsiReferenceExpression)expression);
int preventEndlessLoop = 5;
while (expression instanceof PsiReferenceExpression) {
if (--preventEndlessLoop == 0) return null;
expression = findVariableDefinition((PsiReferenceExpression)expression);
}
return expression;
}
@@ -369,6 +370,21 @@ public class JavaReflectionReferenceUtil {
return false;
}
public boolean isAssignableFrom(@NotNull PsiType type) {
if (type.equals(PsiType.NULL)) {
return myPsiClass != null || myArrayDimensions != 0;
}
if (type.getArrayDimensions() != myArrayDimensions) {
return false;
}
final PsiType otherType = type.getDeepComponentType();
if (myPrimitiveType != null) {
return myPrimitiveType.isAssignableFrom(otherType) || otherType.equalsToText(myPrimitiveType.getBoxedTypeName());
}
final PsiElementFactory factory = JavaPsiFacade.getInstance(myPsiClass.getProject()).getElementFactory();
return factory.createType(myPsiClass).isAssignableFrom(otherType);
}
@Nullable
public static ReflectiveType create(@Nullable PsiType originalType, @NotNull PsiElement context) {
if (originalType == null) {
@@ -0,0 +1,88 @@
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
class ConstructorParamCount {
void varargSignature() throws Exception {
Class<?> cls = Test.class;
Constructor c1 = cls.getConstructor(int.class);
Constructor c2 = cls.getConstructor(int.class, String.class);
Constructor c3 = cls.getConstructor(int.class, String.class, String.class);
c1.newInstance<warning descr="One argument is expected">(42, "abc")</warning>;
c2.newInstance(42, "abc");
c3.newInstance<warning descr="3 arguments are expected">(42, "abc")</warning>;
c1.newInstance(<warning descr="Single-item array is expected">new Object[]{42, "abc"}</warning>);
c2.newInstance(new Object[]{42, "abc"});
c3.newInstance(<warning descr="3 array items are expected">new Object[]{42, "abc"}</warning>);
c1.newInstance<warning descr="One argument is expected">()</warning>;
c2.newInstance<warning descr="2 arguments are expected">()</warning>;
c3.newInstance<warning descr="3 arguments are expected">()</warning>;
cls.getConstructor(String.class).newInstance(new String[] {"abc"});
}
void arraySignatutre() throws Exception {
Class<?> cls = Test.class;
Constructor c1 = cls.getConstructor(new Class[]{int.class});
Constructor c2 = cls.getConstructor(new Class[]{int.class, String.class});
Constructor c3 = cls.getConstructor(new Class[]{int.class, String.class, String.class});
c1.newInstance<warning descr="One argument is expected">(42, "abc")</warning>;
c2.newInstance(42, "abc");
c3.newInstance<warning descr="3 arguments are expected">(42, "abc")</warning>;
c1.newInstance(<warning descr="Single-item array is expected">new Object[]{42, "abc"}</warning>);
c2.newInstance(new Object[]{42, "abc"});
c3.newInstance(<warning descr="3 array items are expected">new Object[]{42, "abc"}</warning>);
c1.newInstance<warning descr="One argument is expected">()</warning>;
c2.newInstance<warning descr="2 arguments are expected">()</warning>;
c3.newInstance<warning descr="3 arguments are expected">()</warning>;
cls.getConstructor(new Class[]{String.class}).newInstance(new String[] {"abc"});
}
void manyArguments() throws Exception {
Class<?> cls = M.class;
Constructor m0 = cls.getConstructor();
Constructor m1 = cls.getConstructor(int.class);
Constructor m2 = cls.getConstructor(int.class, short.class);
Constructor m3 = cls.getConstructor(int.class, short.class, long.class);
Constructor m4 = cls.getConstructor(int.class, short.class, long.class, float.class);
Constructor m5 = cls.getConstructor(int.class, short.class, long.class, float.class, double.class);
m0.newInstance<warning descr="No arguments are expected">(0, 0)</warning>;
m1.newInstance(<warning descr="Argument is not assignable to 'int'">"abc"</warning>);
m2.newInstance(0, <warning descr="Argument is not assignable to 'short'">"abc"</warning>);
m3.newInstance(0, <warning descr="Argument is not assignable to 'short'">0</warning>, <warning descr="Argument is not assignable to 'long'">"abc"</warning>);
m4.newInstance(0, <warning descr="Argument is not assignable to 'short'">0</warning>, 0, <warning descr="Argument is not assignable to 'float'">"abc"</warning>);
m5.newInstance(0, <warning descr="Argument is not assignable to 'short'">0</warning>, 0, 0, <warning descr="Argument is not assignable to 'double'">"abc"</warning>);
Constructor m = cls.getConstructor(new Class[0]);
m.newInstance(new Object[0]);
m.newInstance(<warning descr="Empty array is expected">new Object[] {"abc"}</warning>);
}
class Test {
public Test(int n) {}
public Test(int n, String s) {}
public Test(int n, String s, String t) {}
public Test(String s) {}
}
class M{
public M() {}
public M(int a1) {}
public M(int a1, short a2) {}
public M(int a1, short a2, long a3) {}
public M(int a1, short a2, long a3, float a4) {}
public M(int a1, short a2, long a3, float a4, double a5) {}
}
}
@@ -0,0 +1,131 @@
import java.lang.reflect.Constructor;
import java.util.*;
class ConstructorParamTypes {
void varargSignature() throws Exception {
Class<?> cls = Test.class;
Constructor c1 = cls.getConstructor(int.class, String.class);
Constructor c2 = cls.getConstructor(String.class, Integer.class);
Constructor c3 = cls.getConstructor(int[].class, List.class);
c1.newInstance(42, "abc");
c2.newInstance("abc", 42);
c3.newInstance(new int[]{42, 23}, Arrays.asList("x", "y"));
c1.newInstance(<warning descr="Argument is not assignable to 'int'">"42"</warning>, "abc");
c1.newInstance(<warning descr="Argument is not assignable to 'int'">42.5</warning>, "abc");
c2.newInstance(<warning descr="Argument is not assignable to 'java.lang.String'">42</warning>, 23);
c2.newInstance("abc", <warning descr="Argument is not assignable to 'java.lang.Integer'">"def"</warning>);
c3.newInstance(<warning descr="Argument is not assignable to 'int[]'">Arrays.asList("x", "y")</warning>, <warning descr="Argument is not assignable to 'java.util.List'">new int[]{42, 23}</warning>);
c3.newInstance(<warning descr="Argument is not assignable to 'int[]'">42</warning>, <warning descr="Argument is not assignable to 'java.util.List'">"def"</warning>);
final Object[] a1 = {new Integer(42), "abc"};
final Object[] a2 = {"abc", Integer.valueOf(42)};
final Object[] a3 = {new int[]{42, 23}, Arrays.asList("x", "y")};
c1.newInstance(a1);
c2.newInstance(a2);
c3.newInstance(a3);
c1.newInstance(new Object[] {new Integer(42), "abc"});
c2.newInstance(new Object[] {"abc", Integer.valueOf(42)});
c3.newInstance(new Object[] {new int[]{42, 23}, Arrays.asList("x", "y")});
c1.newInstance(<warning descr="Array item 0 is not assignable to 'int'"><warning descr="Array item 1 is not assignable to 'java.lang.String'">a2</warning></warning>);
c2.newInstance(<warning descr="Array item 0 is not assignable to 'java.lang.String'"><warning descr="Array item 1 is not assignable to 'java.lang.Integer'">a3</warning></warning>);
c3.newInstance(<warning descr="Array item 0 is not assignable to 'int[]'"><warning descr="Array item 1 is not assignable to 'java.util.List'">a1</warning></warning>);
c1.newInstance(new Object[]{<warning descr="Array item is not assignable to 'int'">"42"</warning>, "abc"});
c2.newInstance(new Object[]{<warning descr="Array item is not assignable to 'java.lang.String'">42</warning>, 23});
c3.newInstance(new Object[]{<warning descr="Array item is not assignable to 'int[]'">"x"</warning>, <warning descr="Array item is not assignable to 'java.util.List'">"y"</warning>});
c1.newInstance(<warning descr="Argument is not assignable to 'int'">null</warning>, null);
c2.newInstance(null, null);
c3.newInstance(null, null);
cls.getConstructor(String[].class).newInstance((Object)new String[] {"abc"});
cls.getConstructor(String[].class).newInstance(new String[] {<warning descr="Array item is not assignable to 'java.lang.String[]'">"abc"</warning>});
}
void arraySignatutre() throws Exception {
Class<?> cls = Test.class;
Constructor c1 = cls.getConstructor(new Class[]{int.class, String.class});
Constructor c2 = cls.getConstructor(new Class[]{String.class, Integer.class});
Constructor c3 = cls.getConstructor(new Class[]{int[].class, List.class});
c1.newInstance(<warning descr="Argument is not assignable to 'int'">"42"</warning>, "abc");
c1.newInstance(<warning descr="Argument is not assignable to 'int'">42.5</warning>, "abc");
c2.newInstance(<warning descr="Argument is not assignable to 'java.lang.String'">42</warning>, 23);
c2.newInstance("abc", <warning descr="Argument is not assignable to 'java.lang.Integer'">"def"</warning>);
c3.newInstance(<warning descr="Argument is not assignable to 'int[]'">Arrays.asList("x", "y")</warning>, <warning descr="Argument is not assignable to 'java.util.List'">new int[]{42, 23}</warning>);
c3.newInstance(<warning descr="Argument is not assignable to 'int[]'">42</warning>, <warning descr="Argument is not assignable to 'java.util.List'">"def"</warning>);
final Object[] a1 = {new Integer(42), "abc"};
final Object[] a2 = {"abc", Integer.valueOf(42)};
final Object[] a3 = {new int[]{42, 23}, Arrays.asList("x", "y")};
c1.newInstance(a1);
c2.newInstance(a2);
c3.newInstance(a3);
c1.newInstance(new Object[] {new Integer(42), "abc"});
c2.newInstance(new Object[] {"abc", Integer.valueOf(42)});
c3.newInstance(new Object[] {new int[]{42, 23}, Arrays.asList("x", "y")});
c1.newInstance(<warning descr="Array item 0 is not assignable to 'int'"><warning descr="Array item 1 is not assignable to 'java.lang.String'">a2</warning></warning>);
c2.newInstance(<warning descr="Array item 0 is not assignable to 'java.lang.String'"><warning descr="Array item 1 is not assignable to 'java.lang.Integer'">a3</warning></warning>);
c3.newInstance(<warning descr="Array item 0 is not assignable to 'int[]'"><warning descr="Array item 1 is not assignable to 'java.util.List'">a1</warning></warning>);
c1.newInstance(new Object[]{<warning descr="Array item is not assignable to 'int'">"42"</warning>, "abc"});
c2.newInstance(new Object[]{<warning descr="Array item is not assignable to 'java.lang.String'">42</warning>, 23});
c3.newInstance(new Object[]{<warning descr="Array item is not assignable to 'int[]'">"x"</warning>, <warning descr="Array item is not assignable to 'java.util.List'">"y"</warning>});
c1.newInstance(<warning descr="Argument is not assignable to 'int'">null</warning>, null);
c2.newInstance(null, null);
c3.newInstance(null, null);
cls.getConstructor(new Class[]{String[].class}).newInstance((Object)new String[] {"abc"});
cls.getConstructor(new Class[]{String[].class}).newInstance(new String[] {<warning descr="Array item is not assignable to 'java.lang.String[]'">"abc"</warning>});
}
void manyArguments() throws Exception {
Class<?> cls = M.class;
Constructor m0 = cls.getConstructor();
Constructor m1 = cls.getConstructor(int.class);
Constructor m2 = cls.getConstructor(int.class, short.class);
Constructor m3 = cls.getConstructor(int.class, short.class, long.class);
Constructor m4 = cls.getConstructor(int.class, short.class, long.class, float.class);
Constructor m5 = cls.getConstructor(int.class, short.class, long.class, float.class, double.class);
Object[] a0 = {};
Object[] a1 = {"abc"};
Object[] a2 = {0, "abc"};
Object[] a3 = {0, (short)0, "abc"};
Object[] a4 = {0, (short)0, 0, "abc"};
Object[] a5 = {0, (short)0, 0, 0.0f, "abc"};
m0.newInstance(a0);
m1.newInstance(<warning descr="Array item 0 is not assignable to 'int'">a1</warning>);
m2.newInstance(<warning descr="Array item 1 is not assignable to 'short'">a2</warning>);
m3.newInstance(<warning descr="Array item 2 is not assignable to 'long'">a3</warning>);
m4.newInstance(<warning descr="Array item 3 is not assignable to 'float'">a4</warning>);
m5.newInstance(<warning descr="Array item 4 is not assignable to 'double'">a5</warning>);
}
class Test {
public Test(int n, String s) {}
public Test(String s, Integer n) {}
public Test(int[] n, List<String> s) {}
public Test(String[] s) {}
}
class M {
public String m1(int a1) {return "";}
public String m2(int a1, short a2) {return "";}
public String m3(int a1, short a2, long a3) {return "";}
public String m4(int a1, short a2, long a3, float a4) {return "";}
public String m5(int a1, short a2, long a3, float a4, double a5) {return "";}
}
}
@@ -0,0 +1,89 @@
import java.lang.reflect.Method;
class MethodParamCount {
void varargSignature() throws Exception {
Class<?> cls = Test.class;
Object obj = new Test();
Method m1 = cls.getMethod("bar", int.class);
Method m2 = cls.getMethod("bar", int.class, String.class);
Method m3 = cls.getMethod("bar", int.class, String.class, String.class);
m1.invoke<warning descr="2 arguments are expected">(obj, 42, "abc")</warning>;
m2.invoke(obj, 42, "abc");
m3.invoke<warning descr="4 arguments are expected">(obj, 42, "abc")</warning>;
m1.invoke(obj, <warning descr="Single-item array is expected">new Object[]{42, "abc"}</warning>);
m2.invoke(obj, new Object[]{42, "abc"});
m3.invoke(obj, <warning descr="3 array items are expected">new Object[]{42, "abc"}</warning>);
m1.invoke<warning descr="2 arguments are expected">(obj)</warning>;
m2.invoke<warning descr="3 arguments are expected">(obj)</warning>;
m3.invoke<warning descr="4 arguments are expected">(obj)</warning>;
cls.getMethod("str", String.class).invoke(null, new String[] {"abc"});
}
void arraySignatutre() throws Exception {
Class<?> cls = Test.class;
Object obj = new Test();
Method m1 = cls.getMethod("bar", new Class[]{int.class});
Method m2 = cls.getMethod("bar", new Class[]{int.class, String.class});
Method m3 = cls.getMethod("bar", new Class[]{int.class, String.class, String.class});
m1.invoke<warning descr="2 arguments are expected">(obj, 42, "abc")</warning>;
m2.invoke(obj, 42, "abc");
m3.invoke<warning descr="4 arguments are expected">(obj, 42, "abc")</warning>;
m1.invoke(obj, <warning descr="Single-item array is expected">new Object[]{42, "abc"}</warning>);
m2.invoke(obj, new Object[]{42, "abc"});
m3.invoke(obj, <warning descr="3 array items are expected">new Object[]{42, "abc"}</warning>);
m1.invoke<warning descr="2 arguments are expected">(obj)</warning>;
m2.invoke<warning descr="3 arguments are expected">(obj)</warning>;
m3.invoke<warning descr="4 arguments are expected">(obj)</warning>;
cls.getMethod("str", new Class[]{String.class}).invoke(null, new String[] {"abc"});
}
void manyArguments() throws Exception {
Class<?> cls = M.class;
Object obj = new M();
Method m0 = cls.getMethod("m0");
Method m1 = cls.getMethod("m1", int.class);
Method m2 = cls.getMethod("m2", int.class, short.class);
Method m3 = cls.getMethod("m3", int.class, short.class, long.class);
Method m4 = cls.getMethod("m4", int.class, short.class, long.class, float.class);
Method m5 = cls.getMethod("m5", int.class, short.class, long.class, float.class, double.class);
m0.invoke<warning descr="One argument is expected">(0, 0)</warning>;
m1.invoke<warning descr="2 arguments are expected">("abc")</warning>;
m2.invoke<warning descr="3 arguments are expected">(0, "abc")</warning>;
m3.invoke<warning descr="4 arguments are expected">(0, 0, "abc")</warning>;
m4.invoke<warning descr="5 arguments are expected">(0, 0, 0, "abc")</warning>;
m5.invoke<warning descr="6 arguments are expected">(0, 0, 0, 0, "abc")</warning>;
Method m = cls.getMethod("m0", new Class[0]);
m.invoke(obj, new Object[0]);
m.invoke(obj, <warning descr="Empty array is expected">new Object[] {"abc"}</warning>);
}
static class Test {
public void bar(int n) {}
public void bar(int n, String s) {}
public void bar(int n, String s, String t) {}
public static void str(String s) {}
}
class M {
public String m0() {return "";}
public String m1(int a1) {return "";}
public String m2(int a1, short a2) {return "";}
public String m3(int a1, short a2, long a3) {return "";}
public String m4(int a1, short a2, long a3, float a4) {return "";}
public String m5(int a1, short a2, long a3, float a4, double a5) {return "";}
}
}
@@ -0,0 +1,135 @@
import java.lang.reflect.Method;
import java.util.*;
class MethodParamTypes {
void varargSignature() throws Exception {
Class<?> cls = Test.class;
Object obj = new Test();
Method m1 = cls.getMethod("bar", int.class, String.class);
Method m2 = cls.getMethod("bar", String.class, Integer.class);
Method m3 = cls.getMethod("bar", int[].class, List.class);
m1.invoke(obj, 42, "abc");
m2.invoke(obj, "abc", 42);
m3.invoke(obj, new int[]{42, 23}, Arrays.asList("x", "y"));
m1.invoke(obj, <warning descr="Argument is not assignable to 'int'">"42"</warning>, "abc");
m1.invoke(obj, <warning descr="Argument is not assignable to 'int'">42.5</warning>, "abc");
m2.invoke(obj, <warning descr="Argument is not assignable to 'java.lang.String'">42</warning>, 23);
m2.invoke(obj, "abc", <warning descr="Argument is not assignable to 'java.lang.Integer'">"def"</warning>);
m3.invoke(obj, <warning descr="Argument is not assignable to 'int[]'">Arrays.asList("x", "y")</warning>, <warning descr="Argument is not assignable to 'java.util.List'">new int[]{42, 23}</warning>);
m3.invoke(obj, <warning descr="Argument is not assignable to 'int[]'">42</warning>, <warning descr="Argument is not assignable to 'java.util.List'">"def"</warning>);
final Object[] a1 = {new Integer(42), "abc"};
final Object[] a2 = {"abc", Integer.valueOf(42)};
final Object[] a3 = {new int[]{42, 23}, Arrays.asList("x", "y")};
m1.invoke(obj, a1);
m2.invoke(obj, a2);
m3.invoke(obj, a3);
m1.invoke(obj, new Object[] {new Integer(42), "abc"});
m2.invoke(obj, new Object[] {"abc", Integer.valueOf(42)});
m3.invoke(obj, new Object[] {new int[]{42, 23}, Arrays.asList("x", "y")});
m1.invoke(obj, <warning descr="Array item 0 is not assignable to 'int'"><warning descr="Array item 1 is not assignable to 'java.lang.String'">a2</warning></warning>);
m2.invoke(obj, <warning descr="Array item 0 is not assignable to 'java.lang.String'"><warning descr="Array item 1 is not assignable to 'java.lang.Integer'">a3</warning></warning>);
m3.invoke(obj, <warning descr="Array item 0 is not assignable to 'int[]'"><warning descr="Array item 1 is not assignable to 'java.util.List'">a1</warning></warning>);
m1.invoke(obj, new Object[]{<warning descr="Array item is not assignable to 'int'">"42"</warning>, "abc"});
m2.invoke(obj, new Object[]{<warning descr="Array item is not assignable to 'java.lang.String'">42</warning>, 23});
m3.invoke(obj, new Object[]{<warning descr="Array item is not assignable to 'int[]'">"x"</warning>, <warning descr="Array item is not assignable to 'java.util.List'">"y"</warning>});
m1.invoke(obj, <warning descr="Argument is not assignable to 'int'">null</warning>, null);
m2.invoke(obj, null, null);
m3.invoke(obj, null, null);
cls.getMethod("str", String[].class).invoke(null, (Object)new String[] {"abc"});
cls.getMethod("str", String[].class).invoke(null, new String[] {<warning descr="Array item is not assignable to 'java.lang.String[]'">"abc"</warning>});
}
void arraySignatutre() throws Exception {
Class<?> cls = Test.class;
Object obj = new Test();
Method m1 = cls.getMethod("bar", new Class[]{int.class, String.class});
Method m2 = cls.getMethod("bar", new Class[]{String.class, Integer.class});
Method m3 = cls.getMethod("bar", new Class[]{int[].class, List.class});
m1.invoke(obj, <warning descr="Argument is not assignable to 'int'">"42"</warning>, "abc");
m1.invoke(obj, <warning descr="Argument is not assignable to 'int'">42.5</warning>, "abc");
m2.invoke(obj, <warning descr="Argument is not assignable to 'java.lang.String'">42</warning>, 23);
m2.invoke(obj, "abc", <warning descr="Argument is not assignable to 'java.lang.Integer'">"def"</warning>);
m3.invoke(obj, <warning descr="Argument is not assignable to 'int[]'">Arrays.asList("x", "y")</warning>, <warning descr="Argument is not assignable to 'java.util.List'">new int[]{42, 23}</warning>);
m3.invoke(obj, <warning descr="Argument is not assignable to 'int[]'">42</warning>, <warning descr="Argument is not assignable to 'java.util.List'">"def"</warning>);
final Object[] a1 = {new Integer(42), "abc"};
final Object[] a2 = {"abc", Integer.valueOf(42)};
final Object[] a3 = {new int[]{42, 23}, Arrays.asList("x", "y")};
m1.invoke(obj, a1);
m2.invoke(obj, a2);
m3.invoke(obj, a3);
m1.invoke(obj, new Object[] {new Integer(42), "abc"});
m2.invoke(obj, new Object[] {"abc", Integer.valueOf(42)});
m3.invoke(obj, new Object[] {new int[]{42, 23}, Arrays.asList("x", "y")});
m1.invoke(obj, <warning descr="Array item 0 is not assignable to 'int'"><warning descr="Array item 1 is not assignable to 'java.lang.String'">a2</warning></warning>);
m2.invoke(obj, <warning descr="Array item 0 is not assignable to 'java.lang.String'"><warning descr="Array item 1 is not assignable to 'java.lang.Integer'">a3</warning></warning>);
m3.invoke(obj, <warning descr="Array item 0 is not assignable to 'int[]'"><warning descr="Array item 1 is not assignable to 'java.util.List'">a1</warning></warning>);
m1.invoke(obj, new Object[]{<warning descr="Array item is not assignable to 'int'">"42"</warning>, "abc"});
m2.invoke(obj, new Object[]{<warning descr="Array item is not assignable to 'java.lang.String'">42</warning>, 23});
m3.invoke(obj, new Object[]{<warning descr="Array item is not assignable to 'int[]'">"x"</warning>, <warning descr="Array item is not assignable to 'java.util.List'">"y"</warning>});
m1.invoke(obj, <warning descr="Argument is not assignable to 'int'">null</warning>, null);
m2.invoke(obj, null, null);
m3.invoke(obj, null, null);
cls.getMethod("str", new Class[]{String[].class}).invoke(null, (Object)new String[] {"abc"});
cls.getMethod("str", new Class[]{String[].class}).invoke(null, new String[] {<warning descr="Array item is not assignable to 'java.lang.String[]'">"abc"</warning>});
}
void manyArguments() throws Exception {
Class<?> cls = M.class;
Object obj = new M();
Method m0 = cls.getMethod("m0");
Method m1 = cls.getMethod("m1", int.class);
Method m2 = cls.getMethod("m2", int.class, short.class);
Method m3 = cls.getMethod("m3", int.class, short.class, long.class);
Method m4 = cls.getMethod("m4", int.class, short.class, long.class, float.class);
Method m5 = cls.getMethod("m5", int.class, short.class, long.class, float.class, double.class);
Object[] a0 = {};
Object[] a1 = {"abc"};
Object[] a2 = {0, "abc"};
Object[] a3 = {0, (short)0, "abc"};
Object[] a4 = {0, (short)0, 0, "abc"};
Object[] a5 = {0, (short)0, 0, 0.0f, "abc"};
m0.invoke(obj, a0);
m1.invoke(obj, <warning descr="Array item 0 is not assignable to 'int'">a1</warning>);
m2.invoke(obj, <warning descr="Array item 1 is not assignable to 'short'">a2</warning>);
m3.invoke(obj, <warning descr="Array item 2 is not assignable to 'long'">a3</warning>);
m4.invoke(obj, <warning descr="Array item 3 is not assignable to 'float'">a4</warning>);
m5.invoke(obj, <warning descr="Array item 4 is not assignable to 'double'">a5</warning>);
}
static class Test {
public void bar(int n, String s) {}
public void bar(String s, Integer n) {}
public void bar(int[] n, List<String> s) {}
public static void str(String[] s) {}
}
class M {
public String m1(int a1) {return "";}
public String m2(int a1, short a2) {return "";}
public String m3(int a1, short a2, long a3) {return "";}
public String m4(int a1, short a2, long a3, float a4) {return "";}
public String m5(int a1, short a2, long a3, float a4, double a5) {return "";}
}
}
@@ -0,0 +1,48 @@
/*
* 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.JavaReflectionInvocationInspection
import com.intellij.openapi.roots.LanguageLevelProjectExtension
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
/**
* @author Pavel.Dolgov
*/
class JavaReflectionInvocationTest : LightCodeInsightFixtureTestCase() {
override fun setUp() {
super.setUp()
LanguageLevelProjectExtension.getInstance(project).languageLevel = LanguageLevel.JDK_1_5
myFixture.enableInspections(JavaReflectionInvocationInspection())
}
override fun getProjectDescriptor(): LightProjectDescriptor = LightCodeInsightFixtureTestCase.JAVA_8
override fun getBasePath() = JavaTestUtil.getRelativeJavaTestDataPath() + "/inspection/javaReflectionInvocation"
fun testMethodParamCount() = doTest()
fun testMethodParamTypes() = doTest()
fun testConstructorParamCount() = doTest()
fun testConstructorParamTypes() = doTest()
private fun doTest() {
myFixture.testHighlighting("${getTestName(false)}.java")
}
}
@@ -812,4 +812,11 @@ inspection.handle.signature.replace.with.fix.name=Replace with ''{0}''
inspection.handle.signature.use.method.fix.family.name=Use one of method overloads
inspection.handle.signature.use.method.fix.name=Use method ''{0}''
inspection.handle.signature.use.constructor.fix.family.name=Use one of constructor overloads
inspection.handle.signature.use.constructor.fix.name=Use constructor ''{0}''
inspection.handle.signature.use.constructor.fix.name=Use constructor ''{0}''
inspection.reflection.invocation.name=Reflective invocation arguments mismatch
inspection.reflection.invocation.argument.count={0,choice,0#No arguments are|1#One argument is|1<{0} arguments are} expected
inspection.reflection.invocation.item.count={0,choice,0#Empty array is|1#Single-item array is|1<{0} array items are} expected
inspection.reflection.invocation.argument.not.assignable=Argument is not assignable to ''{0}''
inspection.reflection.invocation.item.not.assignable=Array item is not assignable to ''{0}''
inspection.reflection.invocation.item.number.not.assignable=Array item {0} is not assignable to ''{1}''
@@ -0,0 +1,15 @@
<html>
<body>
The inspection checks that the arguments provided to Method.invoke() and Constructor.newInstance()
match the signature specified in Class.getMethod() and Class.getConstructor()
<!-- tooltip end -->
<p>Example:</p>
<pre><code>
Method m = myObj.getClass().getMethod("myMethod", <b>int</b>.class);
<i>// the argument should be an <b>int</b> value</i>
m.invoke(myObj, "<b>abc</b>");
</code></pre>
<p><small>New in 2017.2</small></p>
</body>
</html>
+4
View File
@@ -922,6 +922,10 @@
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"/>
<localInspection language="JAVA" shortName="JavaReflectionInvocation" enabledByDefault="true" level="WARNING"
groupPath="Java" groupBundle="messages.InspectionsBundle" groupKey="group.names.reflective.access.issues"
bundle="messages.InspectionsBundle" key="inspection.reflection.invocation.name"
implementationClass="com.intellij.codeInspection.reflectiveAccess.JavaReflectionInvocationInspection"/>
<localInspection language="JAVA" shortName="MoveFieldAssignmentToInitializer" enabledByDefault="true" level="INFORMATION"
groupPath="Java" groupBundle="messages.InspectionsBundle" groupKey="group.names.code.style.issues"
displayName="Move field assignment to initializer"