Java: Don't show warnings at reflection access to members of the class if a subclass could be passed at that place (IDEA-177983, IDEA-173505)

This commit is contained in:
Pavel Dolgov
2017-08-28 14:43:10 +03:00
parent ecf65d0924
commit 032fd07e23
12 changed files with 208 additions and 130 deletions
@@ -100,7 +100,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
@NotNull ProblemsHolder holder) {
if (arguments.length == 2) {
if (FIND_CONSTRUCTOR.equals(factoryMethodName)) {
final PsiClass ownerClass = getReflectiveClass(arguments[0]);
final ReflectiveClass ownerClass = getReflectiveClass(arguments[0]);
if (ownerClass != null) {
final PsiExpression typeExpression = ParenthesesUtils.stripParentheses(arguments[1]);
checkConstructor(ownerClass, typeExpression, holder);
@@ -108,7 +108,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
}
}
else if (arguments.length >= 3) {
final PsiClass ownerClass = getReflectiveClass(arguments[0]);
final ReflectiveClass ownerClass = getReflectiveClass(arguments[0]);
if (ownerClass != null) {
final PsiExpression nameExpression = ParenthesesUtils.stripParentheses(arguments[1]);
final PsiExpression nameDefinition = findDefinition(nameExpression);
@@ -146,12 +146,13 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
}
}
private static void checkConstructor(@NotNull PsiClass ownerClass,
private static void checkConstructor(@NotNull ReflectiveClass ownerClass,
@NotNull PsiExpression constructorTypeExpression,
@NotNull ProblemsHolder holder) {
if (!ownerClass.isExact()) return;
final ReflectiveSignature constructorSignature = composeMethodSignature(constructorTypeExpression);
if (constructorSignature != null) {
final List<PsiMethod> constructors = ContainerUtil.filter(ownerClass.getMethods(), PsiMethod::isConstructor);
final List<PsiMethod> constructors = ContainerUtil.filter(ownerClass.getPsiClass().getMethods(), PsiMethod::isConstructor);
List<ReflectiveSignature> validSignatures = null;
if (constructors.isEmpty()) {
if (!constructorSignature.equals(ReflectiveSignature.NO_ARGUMENT_CONSTRUCTOR_SIGNATURE)) {
@@ -168,7 +169,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
final String declarationText = getConstructorDeclarationText(ownerClass, constructorSignature);
if (declarationText != null) {
LocalQuickFix fix = null;
final String ownerClassName = ownerClass.getName();
final String ownerClassName = ownerClass.getPsiClass().getName();
if (ownerClassName != null) {
fix = ReplaceSignatureQuickFix
.createFix(constructorTypeExpression, ownerClassName, validSignatures, true, holder.isOnTheFly());
@@ -179,14 +180,15 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
}
}
private static void checkField(@NotNull PsiClass ownerClass,
private static void checkField(@NotNull ReflectiveClass ownerClass,
@NotNull String fieldName,
@NotNull PsiExpression fieldNameExpression,
@NotNull PsiExpression fieldTypeExpression,
boolean isStaticExpected,
@NotNull PsiReferenceExpression factoryMethodExpression,
@NotNull ProblemsHolder holder) {
final PsiField field = ownerClass.findFieldByName(fieldName, true);
if (!ownerClass.isExact()) return;
final PsiField field = ownerClass.getPsiClass().findFieldByName(fieldName, true);
if (field == null) {
holder.registerProblem(fieldNameExpression, InspectionsBundle.message("inspection.handle.signature.field.cannot.resolve", fieldName));
return;
@@ -212,15 +214,15 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
}
}
private static void checkMethod(@NotNull PsiClass ownerClass,
private static void checkMethod(@NotNull ReflectiveClass ownerClass,
@NotNull String methodName,
@NotNull PsiExpression methodNameExpression,
@NotNull PsiExpression methodTypeExpression,
boolean isStaticExpected,
@NotNull PsiReferenceExpression factoryMethodExpression,
@NotNull ProblemsHolder holder) {
final PsiMethod[] methods = ownerClass.findMethodsByName(methodName, true);
if (!ownerClass.isExact()) return;
final PsiMethod[] methods = ownerClass.getPsiClass().findMethodsByName(methodName, true);
if (methods.length == 0) {
holder.registerProblem(methodNameExpression, JavaErrorMessages.message("cannot.resolve.method", methodName));
return;
@@ -264,7 +266,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
if (argumentType == null || argumentType.getType() instanceof PsiArrayType) {
return;
}
if (!argumentType.isPrimitive()) {
if (!argumentType.isPrimitive() && !argumentType.isExact()) {
final String name = argumentType.getQualifiedName();
if (JAVA_LANG_OBJECT.equals(name) || "java.io.Serializable".equals(name) || "java.lang.Cloneable".equals(name)) {
return;
@@ -280,8 +282,8 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
}
@Nullable
private static String getConstructorDeclarationText(@NotNull PsiClass ownerClass, @NotNull ReflectiveSignature methodSignature) {
final String className = ownerClass.getName();
private static String getConstructorDeclarationText(@NotNull ReflectiveClass ownerClass, @NotNull ReflectiveSignature methodSignature) {
final String className = ownerClass.getPsiClass().getName();
if (className != null) {
return getConstructorDeclarationText(className, methodSignature);
}
@@ -280,7 +280,7 @@ class JavaLangReflectHandleInvocationChecker {
}
final PsiClassType javaLangObject =
PsiType.getJavaLangObject(methodTypeExpression.getManager(), methodTypeExpression.getResolveScope());
final ReflectiveType objectType = ReflectiveType.create(javaLangObject);
final ReflectiveType objectType = ReflectiveType.create(javaLangObject, false);
final List<ReflectiveType> argumentTypes = new ArrayList<>();
argumentTypes.add(objectType); // return type
for (int i = 0; i < objectArgCount; i++) {
@@ -135,9 +135,8 @@ class JavaLangReflectVarHandleInvocationChecker {
if (arrayType != null) {
checkCallReceiver(methodCall, arrayType, holder);
final PsiType rawArrayType = arrayType.getType();
if (rawArrayType instanceof PsiArrayType) {
final ReflectiveType valueType = ReflectiveType.create(((PsiArrayType)rawArrayType).getComponentType());
final ReflectiveType valueType = arrayType.getArrayComponentType();
if (valueType != null) {
checkVarHandleAccessSignature(methodCall, valueType, 2, holder);
}
}
@@ -163,19 +163,19 @@ public class JavaReflectionMemberAccessInspection extends BaseJavaBatchLocalInsp
final PsiExpression nameExpression = arguments[0];
final String fieldName = getMemberName(nameExpression);
if (fieldName != null) {
final PsiClass psiClass = getPsiClass(callExpression);
if (psiClass != null) {
final PsiField field = psiClass.findFieldByName(fieldName, true);
final ReflectiveClass ownerClass = getOwnerClass(callExpression);
if (ownerClass != null && ownerClass.isExact()) {
final PsiField field = ownerClass.getPsiClass().findFieldByName(fieldName, true);
if (field == null) {
if (reportUnresolvedMembersOf(psiClass)) {
if (reportUnresolvedMembersOf(ownerClass)) {
holder.registerProblem(nameExpression, InspectionsBundle.message(
"inspection.reflection.member.access.cannot.resolve.field", fieldName));
}
return;
}
if (isDeclared && field.getContainingClass() != psiClass) {
if (isDeclared && field.getContainingClass() != ownerClass.getPsiClass()) {
holder.registerProblem(nameExpression, InspectionsBundle.message(
"inspection.reflection.member.access.field.not.in.class", fieldName, psiClass.getQualifiedName()));
"inspection.reflection.member.access.field.not.in.class", fieldName, ownerClass.getPsiClass().getQualifiedName()));
return;
}
if (!isDeclared && !field.hasModifierProperty(PsiModifier.PUBLIC)) {
@@ -193,11 +193,11 @@ public class JavaReflectionMemberAccessInspection extends BaseJavaBatchLocalInsp
final PsiExpression nameExpression = arguments[0];
final String methodName = getMemberName(nameExpression);
if (methodName != null) {
final PsiClass psiClass = getPsiClass(callExpression);
if (psiClass != null) {
final PsiMethod[] methods = psiClass.findMethodsByName(methodName, true);
final ReflectiveClass ownerClass = getOwnerClass(callExpression);
if (ownerClass != null && ownerClass.isExact()) {
final PsiMethod[] methods = ownerClass.getPsiClass().findMethodsByName(methodName, true);
if (methods.length == 0) {
if (reportUnresolvedMembersOf(psiClass)) {
if (reportUnresolvedMembersOf(ownerClass)) {
holder.registerProblem(nameExpression, InspectionsBundle.message(
"inspection.reflection.member.access.cannot.resolve.method", methodName));
}
@@ -205,15 +205,15 @@ public class JavaReflectionMemberAccessInspection extends BaseJavaBatchLocalInsp
}
final PsiMethod matchingMethod = matchMethod(methods, arguments, 1);
if (matchingMethod == null) {
if (reportUnresolvedMembersOf(psiClass)) {
if (reportUnresolvedMembersOf(ownerClass)) {
holder.registerProblem(nameExpression, InspectionsBundle.message(
"inspection.reflection.member.access.cannot.resolve.method.arguments", methodName));
}
return;
}
if (isDeclared && matchingMethod.getContainingClass() != psiClass) {
if (isDeclared && matchingMethod.getContainingClass() != ownerClass.getPsiClass()) {
holder.registerProblem(nameExpression, InspectionsBundle.message(
"inspection.reflection.member.access.method.not.in.class", methodName, psiClass.getQualifiedName()));
"inspection.reflection.member.access.method.not.in.class", methodName, ownerClass.getPsiClass().getQualifiedName()));
return;
}
if (!isDeclared && !matchingMethod.hasModifierProperty(PsiModifier.PUBLIC)) {
@@ -228,9 +228,9 @@ public class JavaReflectionMemberAccessInspection extends BaseJavaBatchLocalInsp
private void checkConstructor(@NotNull PsiMethodCallExpression callExpression,
boolean isDeclared,
@NotNull ProblemsHolder holder) {
final PsiClass psiClass = getPsiClass(callExpression);
if (psiClass != null) {
final PsiMethod[] methods = psiClass.getConstructors();
final ReflectiveClass ownerClass = getOwnerClass(callExpression);
if (ownerClass != null && ownerClass.isExact()) {
final PsiMethod[] methods = ownerClass.getPsiClass().getConstructors();
final PsiExpression[] arguments = callExpression.getArgumentList().getExpressions();
final PsiModifierListOwner constructorOrClass;
if (methods.length != 0) {
@@ -238,10 +238,10 @@ public class JavaReflectionMemberAccessInspection extends BaseJavaBatchLocalInsp
}
else {
// implicit constructor
constructorOrClass = arguments.length == 0 ? psiClass : null;
constructorOrClass = arguments.length == 0 ? ownerClass.getPsiClass() : null;
}
if (constructorOrClass == null) {
if (reportUnresolvedMembersOf(psiClass)) {
if (reportUnresolvedMembersOf(ownerClass)) {
holder.registerProblem(callExpression.getArgumentList(), InspectionsBundle.message(
"inspection.reflection.member.access.cannot.resolve.constructor.arguments"));
}
@@ -254,9 +254,9 @@ public class JavaReflectionMemberAccessInspection extends BaseJavaBatchLocalInsp
}
}
private boolean reportUnresolvedMembersOf(@NotNull PsiClass psiClass) {
return (checkMemberExistsInNonFinalClasses || psiClass.hasModifierProperty(PsiModifier.FINAL)) &&
!ignoredClassNames.contains(psiClass.getQualifiedName());
private boolean reportUnresolvedMembersOf(@NotNull ReflectiveClass ownerClass) {
return (checkMemberExistsInNonFinalClasses || ownerClass.getPsiClass().hasModifierProperty(PsiModifier.FINAL)) &&
!ignoredClassNames.contains(ownerClass.getPsiClass().getQualifiedName());
}
@Contract("null->null")
@@ -266,7 +266,7 @@ public class JavaReflectionMemberAccessInspection extends BaseJavaBatchLocalInsp
}
@Nullable
private static PsiClass getPsiClass(@NotNull PsiMethodCallExpression callExpression) {
private static ReflectiveClass getOwnerClass(@NotNull PsiMethodCallExpression callExpression) {
return getReflectiveClass(callExpression.getMethodExpression().getQualifierExpression());
}
@@ -56,21 +56,21 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
final String type = getMemberType(myElement);
if (type != null) {
final PsiClass psiClass = getPsiClass();
if (psiClass != null) {
final ReflectiveClass ownerClass = getOwnerClass();
if (ownerClass != null) {
switch (type) {
case GET_FIELD: {
return psiClass.findFieldByName(name, true);
return ownerClass.getPsiClass().findFieldByName(name, true);
}
case GET_DECLARED_FIELD: {
final PsiField field = psiClass.findFieldByName(name, false);
return isPotentiallyAccessible(field, psiClass) ? field : null;
final PsiField field = ownerClass.getPsiClass().findFieldByName(name, false);
return isPotentiallyAccessible(field, ownerClass) ? field : null;
}
case GET_METHOD: {
PsiMethod[] methods = psiClass.findMethodsByName(name, true);
PsiMethod[] methods = ownerClass.getPsiClass().findMethodsByName(name, true);
if (methods.length > 1) {
methods =
ContainerUtil.filter(methods, method -> isRegularMethod(method) && isPublic(method))
@@ -83,10 +83,10 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
}
case GET_DECLARED_METHOD: {
PsiMethod[] methods = psiClass.findMethodsByName(name, false);
PsiMethod[] methods = ownerClass.getPsiClass().findMethodsByName(name, false);
if (methods.length > 1) {
methods =
ContainerUtil.filter(methods, method -> isRegularMethod(method) && isPotentiallyAccessible(method, psiClass))
ContainerUtil.filter(methods, method -> isRegularMethod(method) && isPotentiallyAccessible(method, ownerClass))
.toArray(PsiMethod.EMPTY_ARRAY);
if (methods.length > 1) {
return findOverloadedMethod(methods);
@@ -102,7 +102,7 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
}
@Nullable
private PsiClass getPsiClass() {
private ReflectiveClass getOwnerClass() {
return getReflectiveClass(myContext);
}
@@ -111,12 +111,12 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
public Object[] getVariants() {
final String type = getMemberType(myElement);
if (type != null) {
final PsiClass psiClass = getPsiClass();
if (psiClass != null) {
final ReflectiveClass ownerClass = getOwnerClass();
if (ownerClass != null) {
switch (type) {
case GET_DECLARED_FIELD:
return Arrays.stream(psiClass.getFields())
return Arrays.stream(ownerClass.getPsiClass().getFields())
.filter(field -> field.getName() != null)
.sorted(Comparator.comparing(PsiField::getName))
.map(field -> lookupField(field))
@@ -124,15 +124,15 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
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()))
return Arrays.stream(ownerClass.getPsiClass().getAllFields())
.filter(field -> isPotentiallyAccessible(field, ownerClass) && field.getName() != null && uniqueNames.add(field.getName()))
.sorted(Comparator.comparingInt((PsiField field) -> isPublic(field) ? 0 : 1).thenComparing(PsiField::getName))
.map(field -> withPriority(lookupField(field), isPublic(field)))
.toArray();
}
case GET_DECLARED_METHOD:
return Arrays.stream(psiClass.getMethods())
return Arrays.stream(ownerClass.getPsiClass().getMethods())
.filter(method -> isRegularMethod(method))
.sorted(Comparator.comparing(PsiMethod::getName))
.map(method -> lookupMethod(method, this))
@@ -140,10 +140,10 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
.toArray();
case GET_METHOD: {
return psiClass.getVisibleSignatures()
return ownerClass.getPsiClass().getVisibleSignatures()
.stream()
.map(MethodSignatureBackedByPsiMethod::getMethod)
.filter(method -> isRegularMethod(method) && isPotentiallyAccessible(method, psiClass))
.filter(method -> isRegularMethod(method) && isPotentiallyAccessible(method, ownerClass))
.sorted(Comparator.comparingInt((PsiMethod method) -> getMethodSortOrder(method)).thenComparing(PsiMethod::getName))
.map(method -> withPriority(lookupMethod(method, this), -getMethodSortOrder(method)))
.filter(Objects::nonNull)
@@ -160,8 +160,8 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
* Non-public members of superclass/superinterface can't be obtained via reflection, they need to be filtered out.
*/
@Contract("null, _ -> false")
private static boolean isPotentiallyAccessible(PsiMember member, PsiClass psiClass) {
return member != null && (member.getContainingClass() == psiClass || isPublic(member));
private static boolean isPotentiallyAccessible(PsiMember member, ReflectiveClass psiClass) {
return member != null && (member.getContainingClass() == psiClass.getPsiClass() || isPublic(member));
}
@Nullable
@@ -63,27 +63,27 @@ public class JavaLangInvokeHandleReference extends PsiReferenceBase<PsiLiteralEx
final String type = getMemberType(myElement);
if (type != null) {
final PsiClass psiClass = getReflectiveClass(myContext);
if (psiClass != null) {
final ReflectiveClass ownerClass = getReflectiveClass(myContext);
if (ownerClass != null) {
switch (type) {
case FIND_GETTER:
case FIND_SETTER:
return resolveField(name, psiClass, JavaLangInvokeHandleReference::isNonStaticField);
return resolveField(name, ownerClass, JavaLangInvokeHandleReference::isNonStaticField);
case FIND_STATIC_GETTER:
case FIND_STATIC_SETTER:
return resolveField(name, psiClass, JavaLangInvokeHandleReference::isStaticField);
return resolveField(name, ownerClass, JavaLangInvokeHandleReference::isStaticField);
case FIND_VIRTUAL:
return resolveMethod(name, psiClass, JavaLangInvokeHandleReference::isNonStaticMethod);
return resolveMethod(name, ownerClass, JavaLangInvokeHandleReference::isNonStaticMethod);
case FIND_STATIC:
return resolveMethod(name, psiClass, JavaLangInvokeHandleReference::isStaticMethod);
return resolveMethod(name, ownerClass, JavaLangInvokeHandleReference::isStaticMethod);
case FIND_VAR_HANDLE:
return resolveField(name, psiClass, JavaLangInvokeHandleReference::isNonStaticField);
return resolveField(name, ownerClass, JavaLangInvokeHandleReference::isNonStaticField);
case FIND_STATIC_VAR_HANDLE:
return resolveField(name, psiClass, JavaLangInvokeHandleReference::isStaticField);
return resolveField(name, ownerClass, JavaLangInvokeHandleReference::isStaticField);
}
}
}
@@ -91,13 +91,13 @@ public class JavaLangInvokeHandleReference extends PsiReferenceBase<PsiLiteralEx
return null;
}
private static PsiElement resolveField(@NotNull String name, @NotNull PsiClass psiClass, Condition<? super PsiField> filter) {
final PsiField field = psiClass.findFieldByName(name, true);
private static PsiElement resolveField(@NotNull String name, @NotNull ReflectiveClass ownerClass, Condition<? super PsiField> filter) {
final PsiField field = ownerClass.getPsiClass().findFieldByName(name, true);
return field != null && filter.value(field) ? field : null;
}
private PsiElement resolveMethod(@NotNull String name, @NotNull PsiClass psiClass, Condition<? super PsiMethod> filter) {
PsiMethod[] methods = psiClass.findMethodsByName(name, true);
private PsiElement resolveMethod(@NotNull String name, @NotNull ReflectiveClass ownerClass, Condition<? super PsiMethod> filter) {
PsiMethod[] methods = ownerClass.getPsiClass().findMethodsByName(name, true);
if (methods.length != 0) {
methods = ContainerUtil.filter(methods, filter).toArray(PsiMethod.EMPTY_ARRAY);
if (methods.length > 1) {
@@ -125,25 +125,25 @@ public class JavaLangInvokeHandleReference extends PsiReferenceBase<PsiLiteralEx
final String type = getMemberType(myElement);
if (type != null) {
final PsiClass psiClass = getReflectiveClass(myContext);
if (psiClass != null) {
final ReflectiveClass ownerClass = getReflectiveClass(myContext);
if (ownerClass != null) {
switch (type) {
case FIND_GETTER:
case FIND_SETTER:
return lookupFields(psiClass, JavaLangInvokeHandleReference::isNonStaticField);
return lookupFields(ownerClass, JavaLangInvokeHandleReference::isNonStaticField);
case FIND_STATIC_GETTER:
case FIND_STATIC_SETTER:
return lookupFields(psiClass, JavaLangInvokeHandleReference::isStaticField);
return lookupFields(ownerClass, JavaLangInvokeHandleReference::isStaticField);
case FIND_VIRTUAL:
return lookupMethods(psiClass, JavaLangInvokeHandleReference::isNonStaticMethod);
return lookupMethods(ownerClass, JavaLangInvokeHandleReference::isNonStaticMethod);
case FIND_STATIC:
return lookupMethods(psiClass, JavaLangInvokeHandleReference::isStaticMethod);
return lookupMethods(ownerClass, JavaLangInvokeHandleReference::isStaticMethod);
case FIND_VAR_HANDLE:
return lookupFields(psiClass, JavaLangInvokeHandleReference::isNonStaticField);
return lookupFields(ownerClass, JavaLangInvokeHandleReference::isNonStaticField);
case FIND_STATIC_VAR_HANDLE:
return lookupFields(psiClass, JavaLangInvokeHandleReference::isStaticField);
return lookupFields(ownerClass, JavaLangInvokeHandleReference::isStaticField);
}
}
}
@@ -151,8 +151,8 @@ public class JavaLangInvokeHandleReference extends PsiReferenceBase<PsiLiteralEx
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
private Object[] lookupMethods(@NotNull PsiClass psiClass, Predicate<? super PsiMethod> filter) {
return psiClass.getVisibleSignatures()
private Object[] lookupMethods(@NotNull ReflectiveClass ownerClass, Predicate<? super PsiMethod> filter) {
return ownerClass.getPsiClass().getVisibleSignatures()
.stream()
.map(MethodSignatureBackedByPsiMethod::getMethod)
.filter(filter)
@@ -163,11 +163,11 @@ public class JavaLangInvokeHandleReference extends PsiReferenceBase<PsiLiteralEx
.toArray();
}
private Object[] lookupFields(@NotNull PsiClass psiClass, Predicate<? super PsiField> filter) {
private Object[] lookupFields(@NotNull ReflectiveClass ownerClass, Predicate<? super PsiField> filter) {
final Set<String> uniqueNames = new THashSet<>();
return Arrays.stream(psiClass.getAllFields())
return Arrays.stream(ownerClass.getPsiClass().getAllFields())
.filter(field -> field != null &&
(field.getContainingClass() == psiClass || !field.hasModifierProperty(PsiModifier.PRIVATE)) &&
(field.getContainingClass() == ownerClass.getPsiClass() || !field.hasModifierProperty(PsiModifier.PRIVATE)) &&
field.getName() != null && uniqueNames.add(field.getName()))
.filter(filter)
.sorted(Comparator.comparing((PsiField field) -> isPublic(field) ? 0 : 1).thenComparing(PsiField::getName))
@@ -91,12 +91,12 @@ public class JavaMethodHandleCompletionContributor extends CompletionContributor
final String methodName = methodCall.getMethodExpression().getReferenceName();
if (methodName != null && METHOD_HANDLE_FACTORY_NAMES.contains(methodName)) {
final PsiExpression[] arguments = methodCall.getArgumentList().getExpressions();
final PsiClass psiClass = arguments.length != 0 ? getReflectiveClass(arguments[0]) : null;
if (psiClass != null) {
final ReflectiveClass ownerClass = arguments.length != 0 ? getReflectiveClass(arguments[0]) : null;
if (ownerClass != null) {
switch (methodName) {
case FIND_CONSTRUCTOR:
addConstructorSignatures(psiClass, position, result);
addConstructorSignatures(ownerClass, position, result);
break;
case FIND_VIRTUAL:
@@ -104,7 +104,7 @@ public class JavaMethodHandleCompletionContributor extends CompletionContributor
case FIND_SPECIAL:
final String name = arguments.length > 1 ? computeConstantExpression(arguments[1], String.class) : null;
if (!StringUtil.isEmpty(name)) {
addMethodSignatures(psiClass, name, FIND_STATIC.equals(methodName), position, result);
addMethodSignatures(ownerClass, name, FIND_STATIC.equals(methodName), position, result);
}
break;
}
@@ -113,27 +113,24 @@ public class JavaMethodHandleCompletionContributor extends CompletionContributor
}
}
private static void addConstructorSignatures(@NotNull PsiClass psiClass,
private static void addConstructorSignatures(@NotNull ReflectiveClass ownerClass,
@NotNull PsiElement context,
@NotNull Consumer<LookupElement> result) {
final String className = psiClass.getName();
if (className != null) {
final PsiMethod[] constructors = psiClass.getConstructors();
if (constructors.length != 0) {
lookupMethodTypes(Arrays.stream(constructors), context, result);
}
else {
result.consume(lookupSignature(ReflectiveSignature.NO_ARGUMENT_CONSTRUCTOR_SIGNATURE, context));
}
final PsiMethod[] constructors = ownerClass.getPsiClass().getConstructors();
if (constructors.length != 0) {
lookupMethodTypes(Arrays.stream(constructors), context, result);
}
else {
result.consume(lookupSignature(ReflectiveSignature.NO_ARGUMENT_CONSTRUCTOR_SIGNATURE, context));
}
}
private static void addMethodSignatures(@NotNull PsiClass psiClass,
private static void addMethodSignatures(@NotNull ReflectiveClass psiClass,
@NotNull String methodName,
boolean isStaticExpected,
@NotNull PsiElement context,
@NotNull Consumer<LookupElement> result) {
final PsiMethod[] methods = psiClass.findMethodsByName(methodName, false);
final PsiMethod[] methods = psiClass.getPsiClass().findMethodsByName(methodName, false);
if (methods.length != 0) {
final Stream<PsiMethod> methodStream = Arrays.stream(methods)
.filter(method -> method.hasModifierProperty(PsiModifier.STATIC) == isStaticExpected);
@@ -174,9 +171,9 @@ public class JavaMethodHandleCompletionContributor extends CompletionContributor
if (arguments.length > 2) {
final String fieldName = computeConstantExpression(arguments[1], String.class);
if (!StringUtil.isEmpty(fieldName)) {
final PsiClass psiClass = getReflectiveClass(arguments[0]);
if (psiClass != null) {
addFieldType(psiClass, fieldName, position, result);
final ReflectiveClass ownerClass = getReflectiveClass(arguments[0]);
if (ownerClass != null) {
addFieldType(ownerClass, fieldName, position, result);
}
}
}
@@ -184,14 +181,14 @@ public class JavaMethodHandleCompletionContributor extends CompletionContributor
}
}
private static void addFieldType(@NotNull PsiClass psiClass,
private static void addFieldType(@NotNull ReflectiveClass ownerClass,
@NotNull String fieldName,
@NotNull PsiElement context,
@NotNull Consumer<LookupElement> result) {
final PsiField field = psiClass.findFieldByName(fieldName, false);
final PsiField field = ownerClass.getPsiClass().findFieldByName(fieldName, false);
if (field != null) {
final String typeText = getTypeText(field.getType());
final PsiElementFactory factory = JavaPsiFacade.getInstance(psiClass.getProject()).getElementFactory();
final PsiElementFactory factory = JavaPsiFacade.getInstance(ownerClass.getPsiClass().getProject()).getElementFactory();
final PsiExpression expression = factory.createExpressionFromText(typeText + ".class", context);
final String shortType = PsiNameHelper.getShortClassName(typeText);
@@ -99,11 +99,11 @@ public class JavaReflectionCompletionContributor extends CompletionContributor {
private static void addVariants(PsiElement position, BiConsumer<PsiClass, Boolean> variantAdder) {
PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(position, PsiMethodCallExpression.class);
if (methodCall != null) {
PsiClass psiClass = getReflectiveClass(methodCall.getMethodExpression().getQualifierExpression());
if (psiClass != null) {
ReflectiveClass ownerClass = getReflectiveClass(methodCall.getMethodExpression().getQualifierExpression());
if (ownerClass != null) {
String methodName = methodCall.getMethodExpression().getReferenceName();
if (methodName != null) {
variantAdder.accept(psiClass, DECLARED_NAMES.contains(methodName));
variantAdder.accept(ownerClass.getPsiClass(), DECLARED_NAMES.contains(methodName));
}
}
}
@@ -102,9 +102,9 @@ public class JavaReflectionReferenceUtil {
if (context == null) {
return null;
}
if (context instanceof PsiClassObjectAccessExpression) { // special case for JDK 1.4
if (context instanceof PsiClassObjectAccessExpression) {
final PsiTypeElement operand = ((PsiClassObjectAccessExpression)context).getOperand();
return ReflectiveType.create(operand.getType());
return ReflectiveType.create(operand.getType(), true);
}
if (context instanceof PsiMethodCallExpression) {
@@ -118,7 +118,7 @@ public class JavaReflectionReferenceUtil {
final PsiExpression argument = findDefinition(ParenthesesUtils.stripParentheses(expressions[0]));
final String className = computeConstantExpression(argument, String.class);
if (className != null) {
return ReflectiveType.create(findClass(className, context));
return ReflectiveType.create(findClass(className, context), true);
}
}
}
@@ -154,7 +154,7 @@ public class JavaReflectionReferenceUtil {
if (field.hasModifierProperty(PsiModifier.FINAL) && field.hasModifierProperty(PsiModifier.STATIC)) {
final PsiPrimitiveType unboxedType = tryUnbox(field.getContainingClass(), (PsiClassType)type);
if (unboxedType != null) {
return ReflectiveType.create(unboxedType);
return ReflectiveType.create(unboxedType, true);
}
}
}
@@ -165,7 +165,7 @@ public class JavaReflectionReferenceUtil {
final PsiType erasure = TypeConversionUtil.erasure(typeArgument);
final PsiClass argumentClass = PsiTypesUtil.getPsiClass(erasure);
if (argumentClass != null && !isJavaLangObject(argumentClass)) {
return ReflectiveType.create(argumentClass);
return ReflectiveType.create(argumentClass, false);
}
}
}
@@ -212,7 +212,7 @@ public class JavaReflectionReferenceUtil {
}
}
}
return ReflectiveType.create(expression.getType());
return ReflectiveType.create(expression.getType(), false);
}
@Contract("null,_->null")
@@ -224,9 +224,9 @@ public class JavaReflectionReferenceUtil {
}
@Nullable
public static PsiClass getReflectiveClass(PsiExpression context) {
public static ReflectiveClass getReflectiveClass(PsiExpression context) {
final ReflectiveType reflectiveType = getReflectiveType(context);
return reflectiveType != null ? reflectiveType.getPsiClass() : null;
return reflectiveType != null ? reflectiveType.getReflectiveClass() : null;
}
@Nullable
@@ -405,7 +405,7 @@ public class JavaReflectionReferenceUtil {
@NotNull
public static String getTypeText(@NotNull PsiType type) {
final ReflectiveType reflectiveType = ReflectiveType.create(type);
final ReflectiveType reflectiveType = ReflectiveType.create(type, false);
return reflectiveType.getQualifiedName();
}
@@ -557,9 +557,11 @@ public class JavaReflectionReferenceUtil {
public static class ReflectiveType {
final PsiType myType;
final boolean myIsExact;
private ReflectiveType(@NotNull PsiType erasedType) {
private ReflectiveType(@NotNull PsiType erasedType, boolean isExact) {
myType = erasedType;
myIsExact = isExact;
}
@NotNull
@@ -589,26 +591,48 @@ public class JavaReflectionReferenceUtil {
return myType;
}
public boolean isExact() {
return myIsExact;
}
@Nullable
public ReflectiveClass getReflectiveClass() {
PsiClass psiClass = getPsiClass();
if (psiClass != null) {
return new ReflectiveClass(psiClass, myIsExact);
}
return null;
}
@Nullable
public ReflectiveType getArrayComponentType() {
if (myType instanceof PsiArrayType) {
PsiType componentType = ((PsiArrayType)myType).getComponentType();
return new ReflectiveType(componentType, myIsExact);
}
return null;
}
@Nullable
public PsiClass getPsiClass() {
return PsiTypesUtil.getPsiClass(myType);
}
@Contract("!null -> !null; null -> null")
@Contract("!null,_ -> !null; null,_ -> null")
@Nullable
public static ReflectiveType create(@Nullable PsiType originalType) {
public static ReflectiveType create(@Nullable PsiType originalType, boolean isExact) {
if (originalType != null) {
return new ReflectiveType(erasure(originalType));
return new ReflectiveType(erasure(originalType), isExact);
}
return null;
}
@Contract("!null -> !null; null -> null")
@Contract("!null,_ -> !null; null,_ -> null")
@Nullable
public static ReflectiveType create(@Nullable PsiClass psiClass) {
public static ReflectiveType create(@Nullable PsiClass psiClass, boolean isExact) {
if (psiClass != null) {
final PsiElementFactory factory = JavaPsiFacade.getInstance(psiClass.getProject()).getElementFactory();
return new ReflectiveType(factory.createType(psiClass));
return new ReflectiveType(factory.createType(psiClass), isExact);
}
return null;
}
@@ -617,7 +641,7 @@ public class JavaReflectionReferenceUtil {
@Nullable
public static ReflectiveType arrayOf(@Nullable ReflectiveType itemType) {
if (itemType != null) {
return new ReflectiveType(itemType.myType.createArrayType());
return new ReflectiveType(itemType.myType.createArrayType(), itemType.myIsExact);
}
return null;
}
@@ -632,6 +656,25 @@ public class JavaReflectionReferenceUtil {
}
}
public static class ReflectiveClass {
final PsiClass myPsiClass;
final boolean myIsExact;
public ReflectiveClass(@NotNull PsiClass psiClass, boolean isExact) {
myPsiClass = psiClass;
myIsExact = isExact;
}
@NotNull
public PsiClass getPsiClass() {
return myPsiClass;
}
public boolean isExact() {
return myIsExact || myPsiClass.hasModifierProperty(PsiModifier.FINAL);
}
}
public static class ReflectiveSignature implements Comparable<ReflectiveSignature> {
public static final ReflectiveSignature NO_ARGUMENT_CONSTRUCTOR_SIGNATURE =
new ReflectiveSignature(null, PsiKeyword.VOID, ArrayUtil.EMPTY_STRING_ARRAY);
@@ -0,0 +1,38 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class Bugs {
static class EnumMethod {
public static <T extends Enum<T>> boolean isEnum(final Class<T> enumClass, final String candidate) {
try {
final Method method = enumClass.getMethod("valueOf", String.class);
Object o = method.invoke(null, candidate);
System.out.println(o.getClass() + "." + o);
return true;
}
catch (final IllegalArgumentException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
return false;
}
}
}
static class ConstructorInSubclass {
static abstract class X {
}
public static class Y extends X {
public Y(int i) {
System.out.println("ok");
}
}
static X test(Class<? extends X> clazz) throws Exception {
return clazz.getConstructor(int.class)
.newInstance(1);
}
public static void main(String[] args) throws Exception {
test(Y.class);
}
}
}
@@ -53,7 +53,7 @@ class Vararg {
myB1 = B.class;
}
void foo1() {
void foo1() throws Exception {
ourA.getMethod("bar", myB1);
ourA.getMethod(<warning descr="Cannot resolve method 'bar' with specified argument types">"bar"</warning>, myB1, String.class);
}
@@ -17,8 +17,6 @@ package com.intellij.java.codeInspection
import com.intellij.JavaTestUtil
import com.intellij.codeInspection.reflectiveAccess.JavaReflectionMemberAccessInspection
import com.intellij.openapi.roots.LanguageLevelProjectExtension
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
@@ -31,11 +29,11 @@ class JavaReflectionMemberAccessTest : LightCodeInsightFixtureTestCase() {
override fun setUp() {
super.setUp()
LanguageLevelProjectExtension.getInstance(project).languageLevel = LanguageLevel.JDK_1_5
myFixture.enableInspections(inspection)
}
override fun getProjectDescriptor(): LightProjectDescriptor = LightCodeInsightFixtureTestCase.JAVA_1_5
override fun getProjectDescriptor(): LightProjectDescriptor =
LightCodeInsightFixtureTestCase.JAVA_8 // older mock JREs are missing some bits
override fun getBasePath() = JavaTestUtil.getRelativeJavaTestDataPath() + "/inspection/javaReflectionMemberAccess"
@@ -48,6 +46,7 @@ class JavaReflectionMemberAccessTest : LightCodeInsightFixtureTestCase() {
fun testConstructorExists() = doTest(true)
fun testNewInstance() = doTest(true)
fun testBugs() = doTest(true)
private fun doTest(checkExists: Boolean = false) {