From 5899792ff257b7e7e63df14b32b55dcf912c071c Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 28 Jan 2016 16:25:07 +0100 Subject: [PATCH] IDEA-96099 Smart completion fails with local inner classes --- .../intellij/codeInsight/CodeInsightUtil.java | 197 +++++++----------- ...LocalClassInExpectedTypeArguments-out.java | 8 + .../LocalClassInExpectedTypeArguments.java | 6 + .../completion/SmartTypeCompletionTest.java | 2 + 4 files changed, 94 insertions(+), 119 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/LocalClassInExpectedTypeArguments-out.java create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/LocalClassInExpectedTypeArguments.java diff --git a/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java b/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java index d842830abdb6..0fc7d26ba879 100644 --- a/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java @@ -21,14 +21,11 @@ import com.intellij.codeInsight.completion.JavaCompletionUtil; import com.intellij.codeInsight.completion.PrefixMatcher; import com.intellij.lang.Language; import com.intellij.lang.StdLanguages; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; @@ -276,33 +273,15 @@ public class CodeInsightUtil { psiType = psiType.getDeepComponentType(); if (!(psiType instanceof PsiClassType)) return; - final Condition shortNameCondition = new Condition() { - @Override - public boolean value(String s) { - return matcher.prefixMatches(s); - } - }; - - final PsiClassType baseType = (PsiClassType)psiType; - final PsiClassType.ClassResolveResult baseResult = - ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public PsiClassType.ClassResolveResult compute() { - return JavaCompletionUtil.originalize(baseType).resolveGenerics(); - } - }); - final PsiClass baseClass = baseResult.getElement(); - final PsiSubstitutor baseSubstitutor = baseResult.getSubstitutor(); + PsiClassType baseType = JavaCompletionUtil.originalize((PsiClassType)psiType); + PsiClassType.ClassResolveResult baseResult = baseType.resolveGenerics(); + PsiClass baseClass = baseResult.getElement(); + PsiSubstitutor baseSubstitutor = baseResult.getSubstitutor(); if(baseClass == null) return; - final GlobalSearchScope scope = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public GlobalSearchScope compute() { - return context.getResolveScope(); - } - }); + GlobalSearchScope scope = context.getResolveScope(); - final Processor inheritorsProcessor = + Processor inheritorsProcessor = createInheritorsProcessor(context, baseType, arrayDim, getRawSubtypes, consumer, baseClass, baseSubstitutor); addContextTypeArguments(context, baseType, inheritorsProcessor); @@ -310,118 +289,98 @@ public class CodeInsightUtil { if (baseClass.hasModifierProperty(PsiModifier.FINAL)) return; if (matcher.getPrefix().length() > 2) { - AllClassesGetter.processJavaClasses(matcher, context.getProject(), scope, new Processor() { - @Override - public boolean process(PsiClass psiClass) { - if (psiClass.isInheritor(baseClass, true)) { - return inheritorsProcessor.process(psiClass); - } - return true; + AllClassesGetter.processJavaClasses(matcher, context.getProject(), scope, psiClass -> { + if (psiClass.isInheritor(baseClass, true)) { + return inheritorsProcessor.process(psiClass); } + return true; }); } else { - final Query baseQuery = ClassInheritorsSearch.search( - new ClassInheritorsSearch.SearchParameters(baseClass, scope, true, false, false, shortNameCondition)); - final Query query = new FilteredQuery(baseQuery, new Condition() { - @Override - public boolean value(final PsiClass psiClass) { - return !(psiClass instanceof PsiTypeParameter); - } - }); + Query baseQuery = ClassInheritorsSearch.search( + new ClassInheritorsSearch.SearchParameters(baseClass, scope, true, false, false, matcher::prefixMatches)); + Query query = new FilteredQuery<>(baseQuery, psiClass -> !(psiClass instanceof PsiTypeParameter)); query.forEach(inheritorsProcessor); } - } - private static void addContextTypeArguments(final PsiElement context, - final PsiClassType baseType, - final Processor inheritorsProcessor) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - Set usedNames = ContainerUtil.newHashSet(); - PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject()); - PsiElement each = context; - while (true) { - PsiTypeParameterListOwner typed = PsiTreeUtil.getParentOfType(each, PsiTypeParameterListOwner.class); - if (typed == null) break; - for (PsiTypeParameter parameter : typed.getTypeParameters()) { - if (baseType.isAssignableFrom(factory.createType(parameter)) && usedNames.add(parameter.getName())) { - inheritorsProcessor.process(CompletionUtil.getOriginalOrSelf(parameter)); - } - } - - each = typed; + private static void addContextTypeArguments(PsiElement context, PsiClassType baseType, Processor inheritorsProcessor) { + Set usedNames = ContainerUtil.newHashSet(); + PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject()); + PsiElement each = context; + while (true) { + PsiTypeParameterListOwner typed = PsiTreeUtil.getParentOfType(each, PsiTypeParameterListOwner.class); + if (typed == null) break; + for (PsiTypeParameter parameter : typed.getTypeParameters()) { + if (baseType.isAssignableFrom(factory.createType(parameter)) && usedNames.add(parameter.getName())) { + inheritorsProcessor.process(CompletionUtil.getOriginalOrSelf(parameter)); } } - }); + + each = typed; + } } - public static Processor createInheritorsProcessor(final PsiElement context, final PsiClassType baseType, - final int arrayDim, - final boolean getRawSubtypes, - final Consumer result, @NotNull final PsiClass baseClass, final PsiSubstitutor baseSubstitutor) { - final PsiManager manager = context.getManager(); - final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject()); - final PsiResolveHelper resolveHelper = facade.getResolveHelper(); + public static Processor createInheritorsProcessor(PsiElement context, + PsiClassType baseType, + int arrayDim, + boolean getRawSubtypes, + Consumer result, + @NotNull PsiClass baseClass, + PsiSubstitutor baseSubstitutor) { + PsiManager manager = context.getManager(); + JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject()); + PsiResolveHelper resolveHelper = facade.getResolveHelper(); - return new Processor() { - @Override - public boolean process(final PsiClass inheritor) { - ProgressManager.checkCanceled(); + return inheritor -> { + ProgressManager.checkCanceled(); - return ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Boolean compute() { - if (!context.isValid() || !inheritor.isValid() || !facade.getResolveHelper().isAccessible(inheritor, context, null)) - return true; + if (!facade.getResolveHelper().isAccessible(inheritor, context, null)) { + return true; + } - if (inheritor.getQualifiedName() == null && - !manager.areElementsEquivalent(inheritor.getContainingFile(), context.getContainingFile().getOriginalFile())) { - return true; - } + if (inheritor.getQualifiedName() == null && + !manager.areElementsEquivalent(inheritor.getContainingFile(), context.getContainingFile().getOriginalFile())) { + return true; + } - if (JavaCompletionUtil.isInExcludedPackage(inheritor, false)) return true; + if (JavaCompletionUtil.isInExcludedPackage(inheritor, false)) return true; - PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(baseClass, inheritor, PsiSubstitutor.EMPTY); - if (superSubstitutor == null) return true; - if (getRawSubtypes) { - result.consume(createType(inheritor, facade.getElementFactory().createRawSubstitutor(inheritor), arrayDim)); - return true; - } + PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(baseClass, inheritor, PsiSubstitutor.EMPTY); + if (superSubstitutor == null) return true; + if (getRawSubtypes) { + result.consume(createType(inheritor, facade.getElementFactory().createRawSubstitutor(inheritor), arrayDim)); + return true; + } - PsiSubstitutor inheritorSubstitutor = PsiSubstitutor.EMPTY; - for (PsiTypeParameter inheritorParameter : PsiUtil.typeParametersIterable(inheritor)) { - for (PsiTypeParameter baseParameter : PsiUtil.typeParametersIterable(baseClass)) { - final PsiType substituted = superSubstitutor.substitute(baseParameter); - PsiType arg = baseSubstitutor.substitute(baseParameter); - if (arg instanceof PsiWildcardType) { - PsiType bound = ((PsiWildcardType)arg).getBound(); - arg = bound != null ? bound : ((PsiWildcardType)arg).getExtendsBound(); - } - PsiType substitution = resolveHelper.getSubstitutionForTypeParameter(inheritorParameter, - substituted, - arg, - true, - PsiUtil.getLanguageLevel(context)); - if (PsiType.NULL.equals(substitution) || substitution instanceof PsiWildcardType) continue; - if (substitution == null) { - result.consume(createType(inheritor, facade.getElementFactory().createRawSubstitutor(inheritor), arrayDim)); - return true; - } - inheritorSubstitutor = inheritorSubstitutor.put(inheritorParameter, substitution); - break; - } - } - - PsiType toAdd = createType(inheritor, inheritorSubstitutor, arrayDim); - if (baseType.isAssignableFrom(toAdd)) { - result.consume(toAdd); - } + PsiSubstitutor inheritorSubstitutor = PsiSubstitutor.EMPTY; + for (PsiTypeParameter inheritorParameter : PsiUtil.typeParametersIterable(inheritor)) { + for (PsiTypeParameter baseParameter : PsiUtil.typeParametersIterable(baseClass)) { + final PsiType substituted = superSubstitutor.substitute(baseParameter); + PsiType arg = baseSubstitutor.substitute(baseParameter); + if (arg instanceof PsiWildcardType) { + PsiType bound = ((PsiWildcardType)arg).getBound(); + arg = bound != null ? bound : ((PsiWildcardType)arg).getExtendsBound(); + } + PsiType substitution = resolveHelper.getSubstitutionForTypeParameter(inheritorParameter, + substituted, + arg, + true, + PsiUtil.getLanguageLevel(context)); + if (PsiType.NULL.equals(substitution) || substitution instanceof PsiWildcardType) continue; + if (substitution == null) { + result.consume(createType(inheritor, facade.getElementFactory().createRawSubstitutor(inheritor), arrayDim)); return true; } - }).booleanValue(); + inheritorSubstitutor = inheritorSubstitutor.put(inheritorParameter, substitution); + break; + } } + + PsiType toAdd = createType(inheritor, inheritorSubstitutor, arrayDim); + if (baseType.isAssignableFrom(toAdd)) { + result.consume(toAdd); + } + return true; }; } diff --git a/java/java-tests/testData/codeInsight/completion/smartType/LocalClassInExpectedTypeArguments-out.java b/java/java-tests/testData/codeInsight/completion/smartType/LocalClassInExpectedTypeArguments-out.java new file mode 100644 index 000000000000..03533d7d3e17 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/LocalClassInExpectedTypeArguments-out.java @@ -0,0 +1,8 @@ +import java.util.ArrayList; + +class A { + void m() { + class B {} + java.util.List l = new ArrayList(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/LocalClassInExpectedTypeArguments.java b/java/java-tests/testData/codeInsight/completion/smartType/LocalClassInExpectedTypeArguments.java new file mode 100644 index 000000000000..9ea67f35de7e --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/LocalClassInExpectedTypeArguments.java @@ -0,0 +1,6 @@ +class A { + void m() { + class B {} + java.util.List l = new ArrayLi + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java index 0b2c868a147b..b31d13bacb5b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java @@ -1163,6 +1163,8 @@ public class SmartTypeCompletionTest extends LightFixtureCompletionTestCase { public void testNonStaticField() throws Exception { doAntiTest(); } + public void testLocalClassInExpectedTypeArguments() { doTest(); } + private void doActionTest() throws Exception { configureByTestName(); checkResultByTestName();