From e72a28c3e693e4ae260b2cc0f465ff7cb372b250 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 21 Feb 2019 12:58:25 +0700 Subject: [PATCH] Detect recursive type parameter definition when checking for assignability Fixes EA-137370 - SOE: TypeConversionUtil.isNarrowingReferenceConversionAllowed --- .../impl/analysis/HighlightClassUtil.java | 27 +------------- .../intellij/psi/util/InheritanceUtil.java | 35 +++++++++++++++++++ .../intellij/psi/util/TypeConversionUtil.java | 6 +++- .../WideningCastToTypeParam.java | 13 +++++++ .../groovy/annotator/GroovyAnnotator.java | 3 +- 5 files changed, 55 insertions(+), 29 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java index 31292960b2ea..44997886cdd9 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java @@ -606,7 +606,7 @@ public class HighlightClassUtil { @Nullable static HighlightInfo checkCyclicInheritance(@NotNull PsiClass aClass) { - PsiClass circularClass = getCircularClass(aClass, new HashSet<>()); + PsiClass circularClass = InheritanceUtil.getCircularClass(aClass); if (circularClass != null) { String description = JavaErrorMessages.message("cyclic.inheritance", HighlightUtil.formatClass(circularClass)); TextRange range = HighlightNamesUtil.getClassDeclarationTextRange(aClass); @@ -615,31 +615,6 @@ public class HighlightClassUtil { return null; } - @Nullable - public static PsiClass getCircularClass(@NotNull PsiClass aClass, @NotNull Collection usedClasses) { - if (usedClasses.contains(aClass)) { - return aClass; - } - try { - usedClasses.add(aClass); - PsiClass[] superTypes = aClass.getSupers(); - for (PsiElement superType : superTypes) { - while (superType instanceof PsiClass) { - if (!CommonClassNames.JAVA_LANG_OBJECT.equals(((PsiClass)superType).getQualifiedName())) { - PsiClass circularClass = getCircularClass((PsiClass)superType, usedClasses); - if (circularClass != null) return circularClass; - } - // check class qualifier - superType = superType.getParent(); - } - } - } - finally { - usedClasses.remove(aClass); - } - return null; - } - @Nullable static HighlightInfo checkExtendsDuplicate(@NotNull PsiJavaCodeReferenceElement element, @Nullable PsiElement resolved, @NotNull PsiFile containingFile) { if (!(element.getParent() instanceof PsiReferenceList)) return null; diff --git a/java/java-psi-api/src/com/intellij/psi/util/InheritanceUtil.java b/java/java-psi-api/src/com/intellij/psi/util/InheritanceUtil.java index 7d66e5454dfa..71aa4cad904c 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/InheritanceUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/InheritanceUtil.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; @@ -188,4 +189,38 @@ public class InheritanceUtil { return true; } + @Nullable + private static PsiClass getCircularClass(@NotNull PsiClass aClass, @NotNull Collection usedClasses) { + if (usedClasses.contains(aClass)) { + return aClass; + } + try { + usedClasses.add(aClass); + PsiClass[] superTypes = aClass.getSupers(); + for (PsiElement superType : superTypes) { + while (superType instanceof PsiClass) { + if (!CommonClassNames.JAVA_LANG_OBJECT.equals(((PsiClass)superType).getQualifiedName())) { + PsiClass circularClass = getCircularClass((PsiClass)superType, usedClasses); + if (circularClass != null) return circularClass; + } + // check class qualifier + superType = superType.getParent(); + } + } + } + finally { + usedClasses.remove(aClass); + } + return null; + } + + /** + * Detects a circular inheritance + * @param aClass a class to check + * @return a class which is a part of the inheritance loop; null if no circular inheritance was detected + */ + @Nullable + public static PsiClass getCircularClass(@NotNull PsiClass aClass) { + return getCircularClass(aClass, new HashSet<>()); + } } diff --git a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java index ba0a40489ae6..c847acc4f38c 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java @@ -320,8 +320,12 @@ public class TypeConversionUtil { final PsiClassType.ClassResolveResult result = superType.resolveGenerics(); final PsiClass superClass = result.getElement(); if (superClass != null) { + PsiElementFactory factory = JavaPsiFacade.getElementFactory(typeParameter.getProject()); + if (superClass instanceof PsiTypeParameter && InheritanceUtil.getCircularClass(superClass) != null) { + return factory.createTypeByFQClassName(JAVA_LANG_OBJECT, typeParameter.getResolveScope()); + } final PsiSubstitutor substitutor = result.getSubstitutor().put(typeParameter, null); - return JavaPsiFacade.getElementFactory(typeParameter.getProject()).createType(superClass, substitutor); + return factory.createType(superClass, substitutor); } return superType; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/WideningCastToTypeParam.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/WideningCastToTypeParam.java index 06bd3cc863b1..5eda165a9a3b 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/WideningCastToTypeParam.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/WideningCastToTypeParam.java @@ -49,3 +49,16 @@ class TestNonNarrowingConversion { return 1; } } +class TestRecursiveTypeParameter { + static <T extends T> void test(T t) { + String x = t; + t = x; + } + + static <A extends B, B extends A> void test(A a, B b) { + a = b; + b = a; + String x = a; + a = x; + } +} \ No newline at end of file diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java index 82706a2067fc..a8e7104fcb51 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java @@ -2,7 +2,6 @@ package org.jetbrains.plugins.groovy.annotator; import com.intellij.codeInsight.ClassUtil; -import com.intellij.codeInsight.daemon.impl.analysis.HighlightClassUtil; import com.intellij.codeInsight.generation.OverrideImplementExploreUtil; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.QuickFixFactory; @@ -1959,7 +1958,7 @@ public class GroovyAnnotator extends GroovyElementVisitor { private static void checkCyclicInheritance(AnnotationHolder holder, @NotNull GrTypeDefinition typeDefinition) { - final PsiClass psiClass = HighlightClassUtil.getCircularClass(typeDefinition, new HashSet<>()); + final PsiClass psiClass = InheritanceUtil.getCircularClass(typeDefinition); if (psiClass != null) { String qname = psiClass.getQualifiedName(); assert qname != null;