From 6cc0a36607d0d343931fd87caae1e5f0e16be5e1 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Thu, 5 May 2016 00:10:12 +0300 Subject: [PATCH] [groovy] fix direct inheritors search when aliased import is used --- .../groovy/lang/psi/stubs/GrStubUtils.java | 50 ++++++++++++-- .../elements/GrReferenceListElementType.java | 4 +- .../GrAliasedImportInheritorsTest.groovy | 69 +++++++++++++++++++ 3 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/lang/overriding/GrAliasedImportInheritorsTest.groovy diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrStubUtils.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrStubUtils.java index e838d37d181a..05a240b68416 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrStubUtils.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrStubUtils.java @@ -18,27 +18,32 @@ package org.jetbrains.plugins.groovy.lang.psi.stubs; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiModifier; -import com.intellij.psi.PsiModifierList; -import com.intellij.psi.PsiModifierListOwner; -import com.intellij.psi.PsiNameHelper; +import com.intellij.psi.*; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.stubs.StubInputStream; import com.intellij.psi.stubs.StubOutputStream; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; +import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrReferenceElementImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.modifiers.GrModifierListImpl; import java.io.IOException; +import java.util.Collections; import java.util.List; +import java.util.Map; /** * User: Dmitry.Krasilschikov @@ -92,10 +97,45 @@ public class GrStubUtils { return typeElement == null ? null : typeElement.getText(); } + @NotNull + private static Map getAliasMapping(@Nullable PsiFile file) { + if (!(file instanceof GroovyFile)) return Collections.emptyMap(); + return CachedValuesManager.getCachedValue(file, () -> { + Map mapping = ContainerUtil.newHashMap(); + for (GrImportStatement importStatement : ((GroovyFile)file).getImportStatements()) { + if (importStatement.getImportReference() != null && !importStatement.isStatic() && importStatement.isAliasedImport()) { + String importName = importStatement.getImportReference().getClassNameText(); + String importedName = importStatement.getImportedName(); + if (importedName != null) { + mapping.put(importedName, importName); + } + } + } + return CachedValueProvider.Result.create(mapping, file); + }); + } + + @Nullable + public static String getReferenceName(@NotNull GrReferenceElement element) { + final String referenceName = element.getReferenceName(); + if (referenceName == null) return null; + + // Foo -> java.util.List + final String mappedFqn = getAliasMapping(element.getContainingFile()).get(referenceName); + final String fullText = element instanceof GrReferenceElementImpl + ? ((GrReferenceElementImpl)element).getTextSkipWhiteSpaceAndComments() + : element.getText(); + + // alias: Foo -> java.util.List + // unqualified ref: List -> List + // qualified ref: java.util.List -> java.util.List + return mappedFqn == null ? fullText : fullText.replace(referenceName, mappedFqn); + } + @Nullable public static String getBaseClassName(@NotNull GrTypeDefinition psi) { if (!(psi instanceof GrAnonymousClassDefinition)) return null; - return ((GrAnonymousClassDefinition)psi).getBaseClassReferenceGroovy().getReferenceName(); + return getReferenceName(((GrAnonymousClassDefinition)psi).getBaseClassReferenceGroovy()); } public static String[] getAnnotationNames(PsiModifierListOwner psi) { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrReferenceListElementType.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrReferenceListElementType.java index bb1200be1e4d..d69c2a2b4c2c 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrReferenceListElementType.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrReferenceListElementType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,7 +46,7 @@ public abstract class GrReferenceListElementType exte public GrReferenceListStub createStub(@NotNull T psi, StubElement parentStub) { List refNames = new ArrayList(); for (GrCodeReferenceElement element : psi.getReferenceElementsGroovy()) { - final String name = element.getText(); + final String name = GrStubUtils.getReferenceName(element); if (StringUtil.isNotEmpty(name)) { refNames.add(name); } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/overriding/GrAliasedImportInheritorsTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/overriding/GrAliasedImportInheritorsTest.groovy new file mode 100644 index 000000000000..ef227006ec27 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/overriding/GrAliasedImportInheritorsTest.groovy @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.groovy.lang.overriding + +import com.intellij.psi.search.searches.DirectClassInheritorsSearch +import groovy.transform.CompileStatic +import org.jetbrains.plugins.groovy.LightGroovyTestCase +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition + +@CompileStatic +class GrAliasedImportInheritorsTest extends LightGroovyTestCase { + + final String basePath = null + + void 'test aliased import'() { + def iface = myFixture.addClass ''' +package pckg; + +public interface MyInterface {} +''' + myFixture.addFileToProject 'a.groovy', """\ +import pckg.MyInterface as Roo + +class MyClass implements Roo {} +enum MyEnum implements Roo {} +trait MyTrait implements Roo {} +new Roo() {} +""" + def inheritors = DirectClassInheritorsSearch.search(iface).findAll() + assert inheritors.size() == 4 + } + + void 'test aliased import with generics'() { + def iface = myFixture.addClass ''' +package pckg; + +public interface MyInterface {} +''' + myFixture.addFileToProject 'a.groovy', """\ +import pckg.MyInterface as Roo + +class MyClass implements Roo {} +enum MyEnum implements Roo {} +trait MyTrait implements Roo {} +new Roo() {} +""" + def inheritors = DirectClassInheritorsSearch.search(iface).findAll() + assert inheritors.size() == 4 + inheritors.each { + def type = (it as GrTypeDefinition).getImplementsListTypes(false).first() + def resolveResult = type.resolveGenerics() + assert resolveResult.element == iface + assert resolveResult.substitutor.substitute(iface.typeParameters.first()) + } + } +}