Fix bug when generic types were lost after reflection access

This commit is contained in:
Vitaliy.Bibaev
2018-05-30 08:21:04 +03:00
parent d048869de3
commit 99e998cc33
4 changed files with 51 additions and 18 deletions
@@ -106,7 +106,7 @@ public class ConstructorReflectionAccessor extends ReflectionAccessorBase<Constr
return null;
}
public ConstructorDescriptor(@NotNull PsiNewExpression expression, @Nullable PsiMethod constructor, PsiClass psiClass) {
public ConstructorDescriptor(@NotNull PsiNewExpression expression, @Nullable PsiMethod constructor, @NotNull PsiClass psiClass) {
newExpression = expression;
this.constructor = constructor;
this.psiClass = psiClass;
@@ -96,7 +96,7 @@ public class FieldReflectionAccessor extends ReferenceReflectionAccessorBase<Fie
String methodName = PsiReflectionAccessUtil.getUniqueMethodName(outerClass, "accessToField" + StringUtil.capitalize(fieldName));
ReflectionAccessMethodBuilder methodBuilder = new ReflectionAccessMethodBuilder(methodName);
if (FieldAccessType.GET.equals(accessType)) {
String returnType = PsiReflectionAccessUtil.getAccessibleReturnType(resolveFieldType(descriptor));
String returnType = PsiReflectionAccessUtil.getAccessibleReturnType(descriptor.expression, resolveFieldType(descriptor));
if (returnType == null) {
LOG.warn("Could not resolve field type");
return null;
@@ -47,7 +47,7 @@ public class MethodReflectionAccessor extends ReflectionAccessorBase<MethodRefle
@Override
protected void grantAccess(@NotNull MethodCallDescriptor descriptor) {
PsiClass outerClass = getOuterClass();
String returnType = PsiReflectionAccessUtil.getAccessibleReturnType(resolveMethodReturnType(descriptor));
String returnType = PsiReflectionAccessUtil.getAccessibleReturnType(descriptor.callExpression, resolveMethodReturnType(descriptor));
PsiClass containingClass = descriptor.method.getContainingClass();
String containingClassName = containingClass == null ? null : ClassUtil.getJVMClassName(containingClass);
String name = descriptor.method.getName();
@@ -5,8 +5,11 @@ import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ArrayUtil;
import com.siyeh.ig.psiutils.ExpectedTypeUtils;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -55,30 +58,37 @@ class PsiReflectionAccessUtil {
return psiClass == null || isAccessible(psiClass);
}
@Contract("null -> null")
@Nullable
public static PsiClass nearestAccessedClass(@Nullable PsiClass psiClass) {
while (psiClass != null && !psiClass.hasModifierProperty(PsiModifier.PUBLIC)) {
psiClass = psiClass.getSuperClass();
}
public static String getAccessibleReturnType(@NotNull PsiExpression expression, @Nullable PsiType type) {
String expectedType = tryGetExpectedType(expression);
if (expectedType != null) return expectedType;
return psiClass;
PsiType nearestAccessibleBaseType = nearestAccessedType(type);
if (nearestAccessibleBaseType != null) return nearestAccessibleBaseType.getCanonicalText();
return nearestAccessibleBaseClass(PsiTypesUtil.getPsiClass(type));
}
@Nullable
public static String getAccessibleReturnType(@Nullable PsiType type) {
PsiClass psiClass = nearestAccessedClass(PsiUtil.resolveClassInType(type));
if (psiClass != null) {
return psiClass.getQualifiedName();
}
public static String getAccessibleReturnType(@NotNull PsiExpression expression, @Nullable PsiClass psiClass) {
String expectedType = tryGetExpectedType(expression);
if (expectedType != null) return expectedType;
return type != null ? type.getCanonicalText() : null;
return nearestAccessibleBaseClass(psiClass);
}
@Nullable
public static String getAccessibleReturnType(@Nullable PsiClass psiClass) {
psiClass = nearestAccessedClass(psiClass);
return psiClass == null ? null : psiClass.getQualifiedName();
private static String tryGetExpectedType(@NotNull PsiExpression expression) {
PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, true);
if (expectedType != null && isAccessible(expectedType)) {
// java allows implicit conversions to java.lang.String. In this case we cannot use java.lang.String as a return type because
// it will produce ClassCastException
if (!CommonClassNames.JAVA_LANG_STRING.equals(expectedType.getCanonicalText())) {
return expectedType.getCanonicalText();
}
}
return null;
}
@NotNull
@@ -101,6 +111,29 @@ class PsiReflectionAccessUtil {
return name;
}
private static boolean isAccessible(@NotNull PsiType type) {
return TypeConversionUtil.isPrimitiveAndNotNull(type) || isAccessible(PsiTypesUtil.getPsiClass(type));
}
@Nullable
private static PsiType nearestAccessedType(@Nullable PsiType type) {
while (type != null && !isAccessible(type)) {
type = ArrayUtil.getFirstElement(type.getSuperTypes());
}
return type;
}
@Contract("null -> null")
@Nullable
private static String nearestAccessibleBaseClass(@Nullable PsiClass psiClass) {
while (psiClass != null && !psiClass.hasModifierProperty(PsiModifier.PUBLIC)) {
psiClass = psiClass.getSuperClass();
}
return psiClass == null ? null : psiClass.getQualifiedName();
}
private static boolean isArrayClass(@NotNull PsiClass psiClass) {
Project project = psiClass.getProject();
PsiClass arrayClass = JavaPsiFacade.getElementFactory(project).getArrayClass(PsiUtil.getLanguageLevel(psiClass));