mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
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:
+15
-13
@@ -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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -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++) {
|
||||
|
||||
+2
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+22
-22
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
+19
-19
@@ -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
|
||||
|
||||
+25
-25
@@ -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))
|
||||
|
||||
+19
-22
@@ -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);
|
||||
|
||||
+3
-3
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+60
-17
@@ -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);
|
||||
}
|
||||
|
||||
+3
-4
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user