[groovy] bring default imports processing into import contributor extension

This commit is contained in:
Daniil Ovchinnikov
2016-06-08 23:26:10 +03:00
parent f12a5bec99
commit bf4ed9c5db
7 changed files with 224 additions and 62 deletions
@@ -53,6 +53,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
import org.jetbrains.plugins.groovy.lang.psi.stubs.GrFileStub;
import org.jetbrains.plugins.groovy.lang.psi.stubs.GrPackageDefinitionStub;
import org.jetbrains.plugins.groovy.lang.resolve.ImplicitImportsKt;
import org.jetbrains.plugins.groovy.lang.resolve.MethodTypeInferencer;
import org.jetbrains.plugins.groovy.lang.resolve.PackageSkippingProcessor;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
@@ -165,7 +166,7 @@ public class GroovyFileImpl extends GroovyFileBaseImpl implements GroovyFile {
if (!processImports(processor, state, lastParent, place, importStatements, ImportKind.SIMPLE, null)) return false;
if (!processDeclarationsInPackage(processor, state, lastParent, place)) return false;
if (!processImports(processor, state, lastParent, place, importStatements, ImportKind.ON_DEMAND, null)) return false;
if (!GroovyImportHelper.processImplicitImports(processor, state, lastParent, place, this)) return false;
if (!ImplicitImportsKt.processImplicitImports(processor, state, lastParent, place, this)) return false;
if (ResolveUtil.shouldProcessPackages(classHint)) {
@@ -15,13 +15,10 @@
*/
package org.jetbrains.plugins.groovy.lang.psi.impl;
import com.intellij.psi.*;
import com.intellij.psi.scope.ElementClassHint;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -29,11 +26,9 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
import org.jetbrains.plugins.groovy.lang.resolve.GrImportContributor;
import org.jetbrains.plugins.groovy.lang.resolve.PackageSkippingProcessor;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import org.jetbrains.plugins.groovy.lang.resolve.ImportType;
import java.util.LinkedHashSet;
import java.util.List;
/**
* @author Max Medvedev
@@ -75,7 +70,7 @@ public class GroovyImportHelper {
for (GrImportContributor contributor : GrImportContributor.EP_NAME.getExtensions()) {
result.addAll(ContainerUtil.mapNotNull(
contributor.getImports(file),
i -> i.getStar() && !i.getStatic() ? i.getName() : null
i -> i.getType() == ImportType.STAR ? i.getName() : null
));
}
@@ -98,38 +93,6 @@ public class GroovyImportHelper {
return true;
}
public static boolean processImplicitImports(@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
@Nullable PsiElement lastParent,
@NotNull PsiElement place,
@NotNull GroovyFile file) {
if (!ResolveUtil.shouldProcessClasses(processor.getHint(ElementClassHint.KEY))) return true;
JavaPsiFacade facade = JavaPsiFacade.getInstance(file.getProject());
final PsiScopeProcessor packageSkipper = new PackageSkippingProcessor(processor);
for (final String implicitlyImported : getImplicitlyImportedPackages(file)) {
PsiPackage aPackage = facade.findPackage(implicitlyImported);
if (aPackage == null) continue;
if (!aPackage.processDeclarations(packageSkipper, state, lastParent, place)) {
return false;
}
}
List<PsiClass> implicitlyImportedClasses = CachedValuesManager.getCachedValue(file, () -> {
GlobalSearchScope scope = file.getResolveScope();
List<PsiClass> classes = ContainerUtil.mapNotNull(GroovyFileBase.IMPLICITLY_IMPORTED_CLASSES, s -> facade.findClass(s, scope));
return CachedValueProvider.Result.create(classes, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
});
for (PsiClass clazz : implicitlyImportedClasses) {
if (!ResolveUtil.processElement(processor, clazz, state)) return false;
}
return true;
}
@Nullable
private static ImportKind getImportKind(GrImportStatement statement) {
if (statement.isOnDemand() && !statement.isAliasedImport()) return ImportKind.ON_DEMAND;
@@ -25,7 +25,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.GroovyLanguage;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyImportHelper;
import org.jetbrains.plugins.groovy.lang.resolve.ImplicitImportsKt;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import java.util.List;
@@ -96,7 +96,7 @@ class FromStringLightElement extends LightElement {
}
}
if (!GroovyImportHelper.processImplicitImports(processor, state, lastParent, place, myFile)) {
if (!ImplicitImportsKt.processImplicitImports(processor, state, lastParent, place, myFile)) {
return false;
}
@@ -17,25 +17,23 @@ package org.jetbrains.plugins.groovy.lang.resolve
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
/**
* regular: class
* static: class member
* star: classes in package
* static star: members of class
*/
class Import(
val name: String,
val static: Boolean = false,
val star: Boolean = false
private val classes = listOf(
Import("java.math.BigInteger"),
Import("java.math.BigDecimal")
)
abstract class GrImportContributorBase : GrImportContributor {
private val packages = listOf(
Import("java.lang", ImportType.STAR),
Import("java.util", ImportType.STAR),
Import("java.io", ImportType.STAR),
Import("java.net", ImportType.STAR),
Import("groovy.lang", ImportType.STAR),
Import("groovy.util", ImportType.STAR)
)
abstract fun appendImplicitlyImportedPackages(file: GroovyFile): List<String>
private val imports = classes + packages
final override fun getImports(file: GroovyFile): Collection<Import> {
return appendImplicitlyImportedPackages(file).map {
Import(name = it, star = true)
}
}
class DefaultImportContributor : GrImportContributor {
override fun getImports(file: GroovyFile): Collection<Import> = imports
}
@@ -0,0 +1,107 @@
/*
* 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.resolve
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiElement
import com.intellij.psi.ResolveState
import com.intellij.psi.scope.PsiScopeProcessor
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
open class Import(
val name: String,
val type: ImportType = ImportType.REGULAR
)
enum class ImportType {
/**
* Class
*/
REGULAR,
/**
* Class member
*/
STATIC,
/**
* Classes of package
*/
STAR,
/**
* Members of class
*/
STATIC_STAR
}
abstract class GrImportContributorBase : GrImportContributor {
abstract fun appendImplicitlyImportedPackages(file: GroovyFile): List<String>
final override fun getImports(file: GroovyFile): Collection<Import> {
return appendImplicitlyImportedPackages(file).map {
Import(it, ImportType.STAR)
}
}
}
fun getImplicitImports(file: GroovyFile): Collection<Import> = GrImportContributor.EP_NAME.extensions.fold(emptyList()) { acc, ext ->
acc + ext.getImports(file)
}
fun processImplicitImports(processor: PsiScopeProcessor,
state: ResolveState,
lastParent: PsiElement?,
place: PsiElement,
file: GroovyFile): Boolean {
val hint = processor.getHint(com.intellij.psi.scope.ElementClassHint.KEY)
val facade = JavaPsiFacade.getInstance(file.project)
val packageSkipper = PackageSkippingProcessor(processor)
loop@for (implicitImport in getImplicitImports(file)) {
when (implicitImport.type) {
ImportType.REGULAR -> {
if (!ResolveUtil.shouldProcessClasses(hint)) continue@loop
val clazz = facade.findClass(implicitImport.name, file.resolveScope) ?: continue@loop
if (!ResolveUtil.processElement(processor, clazz, state)) return false
}
ImportType.STATIC -> {
val className = StringUtil.getPackageName(implicitImport.name)
val memberName = StringUtil.getShortName(implicitImport.name)
if (StringUtil.isEmptyOrSpaces(className) || StringUtil.isEmptyOrSpaces(memberName)) continue@loop
val clazz = facade.findClass(className, file.resolveScope) ?: continue@loop
if (ResolveUtil.shouldProcessMethods (hint)) {
for (method in clazz.findMethodsByName(memberName, true)) {
if (!ResolveUtil.processElement(processor, method, state)) return false
}
}
if (ResolveUtil.shouldProcessProperties(hint)) {
val field = clazz.findFieldByName(memberName, true) ?: continue@loop
if (!ResolveUtil.processElement(processor, field, state)) return false
}
}
ImportType.STAR -> {
val pckg = facade.findPackage(implicitImport.name) ?: continue@loop
if (!pckg.processDeclarations(packageSkipper, state, lastParent, place)) return false
}
ImportType.STATIC_STAR -> {
val clazz = facade.findClass(implicitImport.name, file.resolveScope) ?: continue@loop
if (!clazz.processDeclarations(processor, state, lastParent, place)) return false
}
}
}
return true
}
+1
View File
@@ -247,6 +247,7 @@
<methodComparator implementation="org.jetbrains.plugins.groovy.lang.resolve.GrDefaultMethodComparator" order="last"/>
<expectedPackageNameProvider implementation="org.jetbrains.plugins.groovy.lang.resolve.DefaultExpectedPackageNameProvider"
order="last"/>
<importContributor implementation="org.jetbrains.plugins.groovy.lang.resolve.DefaultImportContributor"/>
</extensions>
<extensions defaultExtensionNs="com.intellij.properties">
@@ -0,0 +1,92 @@
/*
* 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.resolve
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.PlatformTestUtil
import groovy.transform.CompileStatic
import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor
import org.jetbrains.plugins.groovy.LightGroovyTestCase
import org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess.GrUnresolvedAccessInspection
@CompileStatic
class GrImportContributorTest extends LightGroovyTestCase {
LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST
@Override
public void setUp() throws Exception {
super.setUp();
fixture.enableInspections(GrUnresolvedAccessInspection)
fixture.addClass '''
package foo.bar;
public class MyClass {
public static Object BAR = null;
public static void foo() {}
}
'''
}
void 'test regular import'() {
PlatformTestUtil.registerExtension(GrImportContributor.EP_NAME, {
[new Import("foo.bar.MyClass", ImportType.REGULAR)]
} as GrImportContributor, testRootDisposable)
fixture.with {
configureByText('a.groovy', 'new MyClass()')
checkHighlighting()
}
}
void 'test static import method'() {
PlatformTestUtil.registerExtension(GrImportContributor.EP_NAME, {
[new Import("foo.bar.MyClass.foo", ImportType.STATIC)]
} as GrImportContributor, testRootDisposable)
fixture.with {
configureByText('a.groovy', 'foo()')
checkHighlighting()
}
}
void 'test static import field'() {
PlatformTestUtil.registerExtension(GrImportContributor.EP_NAME, {
[new Import("foo.bar.MyClass.BAR", ImportType.STATIC)]
} as GrImportContributor, testRootDisposable)
fixture.with {
configureByText('a.groovy', 'println BAR')
checkHighlighting()
}
}
void 'test star import'() {
PlatformTestUtil.registerExtension(GrImportContributor.EP_NAME, {
[new Import("foo.bar", ImportType.STAR)]
} as GrImportContributor, testRootDisposable)
fixture.with {
configureByText('a.groovy', 'new MyClass()')
checkHighlighting()
}
}
void 'test static star import'() {
PlatformTestUtil.registerExtension(GrImportContributor.EP_NAME, {
[new Import("foo.bar.MyClass", ImportType.STATIC_STAR)]
} as GrImportContributor, testRootDisposable)
fixture.with {
configureByText('a.groovy', 'println foo()\n println BAR')
checkHighlighting()
}
}
}