From 99e998cc33d4cdbb13ad5ecbed72e998db6f9e85 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Mon, 28 May 2018 20:22:34 +0300 Subject: [PATCH] Fix bug when generic types were lost after reflection access --- .../ConstructorReflectionAccessor.java | 2 +- .../reflect/FieldReflectionAccessor.java | 2 +- .../reflect/MethodReflectionAccessor.java | 2 +- .../reflect/PsiReflectionAccessUtil.java | 63 ++++++++++++++----- 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java index ea09d8835cbc..c8ce7e9552ad 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java @@ -106,7 +106,7 @@ public class ConstructorReflectionAccessor extends ReflectionAccessorBase 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));