Big refactoring of imports resolve - preparing to change current package members priority

This commit is contained in:
Valentin Kipyatkov
2015-01-15 19:19:52 +03:00
parent 45a7c3bcdc
commit 3caef82184
6 changed files with 150 additions and 191 deletions
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer;
import org.jetbrains.kotlin.resolve.lazy.LazyImportScope;
import org.jetbrains.kotlin.resolve.lazy.LazyFileScope;
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor;
import org.jetbrains.kotlin.resolve.resolveUtil.ResolveUtilPackage;
import org.jetbrains.kotlin.resolve.varianceChecker.VarianceChecker;
@@ -193,8 +193,9 @@ public class LazyTopDownAnalyzer {
@Override
public void visitImportDirective(@NotNull JetImportDirective importDirective) {
LazyImportScope importScope = resolveSession.getScopeProvider().getExplicitImportsScopeForFile(importDirective.getContainingJetFile());
importScope.forceResolveImportDirective(importDirective);
LazyFileScope fileScope = resolveSession.getScopeProvider().getScopeForFile(
importDirective.getContainingJetFile());
fileScope.forceResolveImportDirective(importDirective);
}
private void visitClassOrObject(@NotNull JetClassOrObject classOrObject) {
@@ -363,7 +364,7 @@ public class LazyTopDownAnalyzer {
}
private static void resolveAndCheckImports(@NotNull JetFile file, @NotNull KotlinCodeAnalyzer resolveSession) {
LazyImportScope fileScope = resolveSession.getScopeProvider().getExplicitImportsScopeForFile(file);
LazyFileScope fileScope = resolveSession.getScopeProvider().getScopeForFile(file);
fileScope.forceResolveAllContents();
}
@@ -1,112 +0,0 @@
/*
* Copyright 2010-2015 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.kotlin.resolve.lazy;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import kotlin.Function0;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.JetImportDirective;
import org.jetbrains.kotlin.resolve.ImportPath;
import org.jetbrains.kotlin.storage.NotNullLazyValue;
import org.jetbrains.kotlin.storage.StorageManager;
import java.util.Collections;
import java.util.List;
class ImportsProvider {
private final List<JetImportDirective> importDirectives;
private final NotNullLazyValue<NameToImportsCache> importsCacheValue;
public ImportsProvider(StorageManager storageManager, final List<JetImportDirective> importDirectives) {
this.importDirectives = importDirectives;
this.importsCacheValue = storageManager.createLazyValue(new Function0<NameToImportsCache>() {
@Override
public NameToImportsCache invoke() {
return NameToImportsCache.createIndex(importDirectives);
}
});
}
@NotNull
public List<JetImportDirective> getImports(@NotNull Name name, @NotNull LookupMode mode) {
return importsCacheValue.invoke().getImports(name, mode);
}
@NotNull
public List<JetImportDirective> getAllImports() {
return importDirectives;
}
public enum LookupMode {
CLASS,
PACKAGE,
FUNCTION_OR_PROPERTY
}
private static class NameToImportsCache {
private final ListMultimap<Name, JetImportDirective> nameToDirectives;
private final List<JetImportDirective> allUnderImports;
private NameToImportsCache(ListMultimap<Name, JetImportDirective> directives, List<JetImportDirective> imports) {
nameToDirectives = directives;
allUnderImports = imports;
}
private List<JetImportDirective> getImports(@NotNull Name name, @NotNull LookupMode mode) {
switch (mode) {
case CLASS:
return nameToDirectives.containsKey(name) ? nameToDirectives.get(name) : allUnderImports; // for class lookup explicit imports have priority
case PACKAGE:
return nameToDirectives.containsKey(name) ? nameToDirectives.get(name) : Collections.<JetImportDirective>emptyList(); // no packages import by all under imports
case FUNCTION_OR_PROPERTY:
return nameToDirectives.containsKey(name) ? KotlinPackage.plus(nameToDirectives.get(name), allUnderImports) : allUnderImports;
default:
throw new IllegalArgumentException();
}
}
private static NameToImportsCache createIndex(List<JetImportDirective> importDirectives) {
ImmutableListMultimap.Builder<Name, JetImportDirective> namesToRelativeImportsBuilder = ImmutableListMultimap.builder();
List<JetImportDirective> allUnderImports = Lists.newArrayList();
for (JetImportDirective anImport : importDirectives) {
ImportPath path = anImport.getImportPath();
if (path == null) continue; // Could be some parse errors
if (path.isAllUnder()) {
allUnderImports.add(anImport);
}
else {
Name aliasName = path.getImportedName();
assert aliasName != null;
namesToRelativeImportsBuilder.put(aliasName, anImport);
}
}
return new NameToImportsCache(namesToRelativeImportsBuilder.build(), ImmutableList.copyOf(allUnderImports));
}
}
}
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2015 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.kotlin.resolve.lazy
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.resolve.scopes.ChainedScope
import org.jetbrains.kotlin.psi.JetImportDirective
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.psi.JetCodeFragment
class LazyFileScope private(
private val aliasImportsScope: LazyImportScope,
private val allUnderImportsScope: LazyImportScope,
private val defaultAliasImportsScope: LazyImportScope,
private val defaultAllUnderImportsScope: LazyImportScope,
containingDeclaration: PackageViewDescriptor,
debugName: String
) : ChainedScope(containingDeclaration, debugName, aliasImportsScope, allUnderImportsScope, defaultAliasImportsScope, defaultAllUnderImportsScope) {
public fun forceResolveAllContents() {
aliasImportsScope.forceResolveAllContents()
allUnderImportsScope.forceResolveAllContents()
}
public fun forceResolveImportDirective(importDirective: JetImportDirective) {
if (importDirective.isAllUnder()) {
allUnderImportsScope.forceResolveImportDirective(importDirective)
}
else {
aliasImportsScope.forceResolveImportDirective(importDirective)
}
}
class object {
public fun create(
resolveSession: ResolveSession,
packageDescriptor: PackageViewDescriptor,
jetFile: JetFile,
defaultImports: Collection<JetImportDirective>,
traceForImportResolve: BindingTrace,
traceForDefaultImportResolve: BindingTrace,
debugName: String
): LazyFileScope {
val imports = if (jetFile is JetCodeFragment)
jetFile.importsAsImportList()?.getImports() ?: listOf()
else
jetFile.getImportDirectives()
val aliasImportsScope = LazyImportScope(resolveSession, packageDescriptor, AliasImportsIndexed(imports), traceForImportResolve, "Alias imports in $debugName")
val allUnderImportsScope = LazyImportScope(resolveSession, packageDescriptor, AllUnderImportsIndexed(imports), traceForImportResolve, "All under imports in $debugName")
val defaultAliasImportsScope = LazyImportScope(resolveSession, packageDescriptor, AliasImportsIndexed(defaultImports), traceForDefaultImportResolve, "Default alias imports in $debugName")
val defaultAllUnderImportsScope = LazyImportScope(resolveSession, packageDescriptor, AllUnderImportsIndexed(defaultImports), traceForDefaultImportResolve, "Default all under imports in $debugName")
return LazyFileScope(aliasImportsScope, allUnderImportsScope, defaultAliasImportsScope, defaultAllUnderImportsScope, packageDescriptor, debugName)
}
}
}
@@ -17,8 +17,6 @@
package org.jetbrains.kotlin.resolve.lazy
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.psi.JetCodeFragment
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetImportDirective
import org.jetbrains.kotlin.psi.debugText.*
import org.jetbrains.kotlin.resolve.BindingTrace
@@ -32,18 +30,49 @@ import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver.LookupMode
import java.util.LinkedHashSet
import java.util.HashSet
import com.google.common.collect.ListMultimap
import kotlin.properties.Delegates
import com.google.common.collect.ImmutableListMultimap
public class LazyImportScope(private val resolveSession: ResolveSession,
private val containingDeclaration: PackageViewDescriptor,
imports: List<JetImportDirective>,
private val traceForImportResolve: BindingTrace,
private val debugName: String,
inRootPackage: Boolean) : JetScope {
trait IndexedImports {
val imports: List<JetImportDirective>
fun importsForName(name: Name): Collection<JetImportDirective>
}
class AllUnderImportsIndexed(allImports: Collection<JetImportDirective>) : IndexedImports {
override val imports = allImports.filter { it.isAllUnder() }
override fun importsForName(name: Name) = imports
}
class AliasImportsIndexed(allImports: Collection<JetImportDirective>) : IndexedImports {
override val imports = allImports.filter { !it.isAllUnder() }
private val nameToDirectives: ListMultimap<Name, JetImportDirective> by Delegates.lazy {
val builder = ImmutableListMultimap.builder<Name, JetImportDirective>()
for (directive in imports) {
val path = directive.getImportPath() ?: continue // can be some parse errors
builder.put(path.getImportedName()!!, directive)
}
builder.build()
}
override fun importsForName(name: Name) = nameToDirectives.get(name)
}
class LazyImportScope(
private val resolveSession: ResolveSession,
private val containingDeclaration: PackageViewDescriptor,
private val indexedImports: IndexedImports,
private val traceForImportResolve: BindingTrace,
private val debugName: String
) : JetScope {
private val importsProvider = ImportsProvider(resolveSession.getStorageManager(), imports)
private val importedScopesProvider = resolveSession.getStorageManager().createMemoizedFunction {
(directive: JetImportDirective) -> ImportDirectiveResolveCache(directive)
}
private val inRootPackage = containingDeclaration.getFqName().isRoot()
private val rootScope = JetModuleUtil.getImportsResolutionScope(resolveSession.getModuleDescriptor(), inRootPackage)
private var directiveUnderResolve: JetImportDirective? = null
@@ -95,7 +124,7 @@ public class LazyImportScope(private val resolveSession: ResolveSession,
}
public fun forceResolveAllContents() {
for (importDirective in importsProvider.getAllImports()) {
for (importDirective in indexedImports.imports) {
forceResolveImportDirective(importDirective)
}
}
@@ -113,12 +142,10 @@ public class LazyImportScope(private val resolveSession: ResolveSession,
private fun <D : DeclarationDescriptor> selectSingleFromImports(
name: Name,
lookupMode: LookupMode,
selectImportsMode: ImportsProvider.LookupMode,
descriptorSelector: JetScopeSelectorUtil.ScopeByNameSelector<D>
): D? {
fun compute(): D? {
// for classes and packages it never returns a mix of explicit and all-under imports so they all have the same priority
val imports = importsProvider.getImports(name, selectImportsMode)
val imports = indexedImports.importsForName(name)
if (imports.contains(directiveUnderResolve)) {
// This is the recursion in imports analysis
return null
@@ -138,12 +165,11 @@ public class LazyImportScope(private val resolveSession: ResolveSession,
private fun <D : DeclarationDescriptor> collectFromImports(
name: Name,
lookupMode: LookupMode,
selectImportsMode: ImportsProvider.LookupMode,
descriptorsSelector: JetScopeSelectorUtil.ScopeByNameMultiSelector<D>
): Collection<D> {
return resolveSession.getStorageManager().compute {
val descriptors = HashSet<D>()
for (directive in importsProvider.getImports(name, selectImportsMode)) {
for (directive in indexedImports.importsForName(name)) {
if (directive == directiveUnderResolve) {
// This is the recursion in imports analysis
throw IllegalStateException("Recursion while resolving many imports: " + directive.getText())
@@ -158,22 +184,22 @@ public class LazyImportScope(private val resolveSession: ResolveSession,
private fun getImportScope(directive: JetImportDirective, lookupMode: LookupMode) = importedScopesProvider(directive).scopeForMode(lookupMode)
override fun getClassifier(name: Name) = selectSingleFromImports(name, LookupMode.ONLY_CLASSES_AND_PACKAGES, ImportsProvider.LookupMode.CLASS, JetScopeSelectorUtil.CLASSIFIER_DESCRIPTOR_SCOPE_SELECTOR)
override fun getClassifier(name: Name) = selectSingleFromImports(name, LookupMode.ONLY_CLASSES_AND_PACKAGES, JetScopeSelectorUtil.CLASSIFIER_DESCRIPTOR_SCOPE_SELECTOR)
override fun getPackage(name: Name) = selectSingleFromImports(name, LookupMode.ONLY_CLASSES_AND_PACKAGES, ImportsProvider.LookupMode.PACKAGE, JetScopeSelectorUtil.PACKAGE_SCOPE_SELECTOR)
override fun getPackage(name: Name) = selectSingleFromImports(name, LookupMode.ONLY_CLASSES_AND_PACKAGES, JetScopeSelectorUtil.PACKAGE_SCOPE_SELECTOR)
override fun getProperties(name: Name) = collectFromImports(name, LookupMode.EVERYTHING, ImportsProvider.LookupMode.FUNCTION_OR_PROPERTY, JetScopeSelectorUtil.NAMED_PROPERTIES_SCOPE_SELECTOR)
override fun getProperties(name: Name) = collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_PROPERTIES_SCOPE_SELECTOR)
override fun getLocalVariable(name: Name) = null
override fun getFunctions(name: Name) = collectFromImports(name, LookupMode.EVERYTHING, ImportsProvider.LookupMode.FUNCTION_OR_PROPERTY, JetScopeSelectorUtil.NAMED_FUNCTION_SCOPE_SELECTOR)
override fun getFunctions(name: Name) = collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_FUNCTION_SCOPE_SELECTOR)
override fun getDeclarationsByLabel(labelName: Name): Collection<DeclarationDescriptor> = listOf()
override fun getDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> {
return resolveSession.getStorageManager().compute {
val descriptors = LinkedHashSet<DeclarationDescriptor>()
for (directive in importsProvider.getAllImports()) {
for (directive in indexedImports.imports) {
if (directive == directiveUnderResolve) {
// This is the recursion in imports analysis
throw IllegalStateException("Recursion while resolving many imports: " + directive.getText())
@@ -210,21 +236,4 @@ public class LazyImportScope(private val resolveSession: ResolveSession,
p.popIndent()
p.println("}")
}
class object {
public fun createImportScopeForFile(resolveSession: ResolveSession,
packageDescriptor: PackageViewDescriptor,
jetFile: JetFile,
traceForImportResolve: BindingTrace,
debugName: String): LazyImportScope {
val importDirectives: List<JetImportDirective> = if (jetFile is JetCodeFragment) {
jetFile.importsAsImportList()?.getImports() ?: listOf()
}
else {
jetFile.getImportDirectives()
}
return LazyImportScope(resolveSession, packageDescriptor, importDirectives.reverse(), traceForImportResolve, debugName, packageDescriptor.getFqName().isRoot())
}
}
}
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.resolve.lazy;
import com.google.common.collect.Lists;
import com.intellij.psi.PsiElement;
import kotlin.Function0;
import kotlin.Function1;
@@ -50,9 +49,9 @@ public class ScopeProvider {
private final ResolveSession resolveSession;
private final MemoizedFunctionToNotNull<JetFile, LazyImportScope> explicitImportScopes;
private final NotNullLazyValue<Collection<JetImportDirective>> defaultImports;
private final NotNullLazyValue<JetScope> defaultImportsScope;
private final MemoizedFunctionToNotNull<JetFile, LazyFileScope> importScopes;
private final MemoizedFunctionToNotNull<JetFile, JetScope> fileScopes;
@@ -64,20 +63,29 @@ public class ScopeProvider {
this.additionalFileScopeProvider = additionalFileScopeProvider;
}
public ScopeProvider(@NotNull ResolveSession resolveSession) {
public ScopeProvider(@NotNull final ResolveSession resolveSession) {
this.resolveSession = resolveSession;
this.explicitImportScopes = resolveSession.getStorageManager().createMemoizedFunction(new Function1<JetFile, LazyImportScope>() {
this.defaultImports = resolveSession.getStorageManager().createLazyValue(new Function0<Collection<JetImportDirective>>() {
@Override
public LazyImportScope invoke(@NotNull JetFile file) {
return createExplicitImportScope(file);
public Collection<JetImportDirective> invoke() {
PackageViewDescriptor rootPackage = resolveSession.getModuleDescriptor().getPackage(FqName.ROOT);
if (rootPackage == null) {
throw new IllegalStateException("Root package not found");
}
JetImportsFactory importsFactory = resolveSession.getJetImportsFactory();
List<ImportPath> defaultImports = resolveSession.getModuleDescriptor().getDefaultImports();
return importsFactory.createImportDirectives(defaultImports);
}
});
this.defaultImportsScope = resolveSession.getStorageManager().createLazyValue(new Function0<JetScope>() {
this.importScopes = resolveSession.getStorageManager().createMemoizedFunction(new Function1<JetFile, LazyFileScope>() {
@Override
public JetScope invoke() {
return createScopeWithDefaultImports();
public LazyFileScope invoke(@NotNull JetFile file) {
return createImportScope(file, defaultImports.invoke());
}
});
@@ -89,13 +97,16 @@ public class ScopeProvider {
});
}
private LazyImportScope createExplicitImportScope(@NotNull JetFile file) {
return LazyImportScope.OBJECT$.createImportScopeForFile(
private LazyFileScope createImportScope(@NotNull JetFile file, @NotNull Collection<JetImportDirective> defaultImports) {
TemporaryBindingTrace tempTrace = TemporaryBindingTrace.create(resolveSession.getTrace(), "Transient trace for default imports lazy resolve");
return LazyFileScope.OBJECT$.create(
resolveSession,
getFilePackageDescriptor(file),
file,
defaultImports,
resolveSession.getTrace(),
"Lazy Imports Scope for file " + file.getName());
tempTrace,
"LazyFileScope for file " + file.getName());
}
@NotNull
@@ -114,36 +125,15 @@ public class ScopeProvider {
List<JetScope> list = new ArrayList<JetScope>();
list.add(new NoSubpackagesInPackageScope(getFilePackageDescriptor(file)));
list.add(JetModuleUtil.getSubpackagesOfRootScope(resolveSession.getModuleDescriptor()));
list.add(explicitImportScopes.invoke(file));
list.add(importScopes.invoke(file));
list.addAll(additionalFileScopeProvider.scopes(file));
list.add(defaultImportsScope.invoke());
return list.toArray(new JetScope[list.size()]);
}
@NotNull
public LazyImportScope getExplicitImportsScopeForFile(@NotNull JetFile file) {
return explicitImportScopes.invoke(file);
}
private JetScope createScopeWithDefaultImports() {
PackageViewDescriptor rootPackage = resolveSession.getModuleDescriptor().getPackage(FqName.ROOT);
if (rootPackage == null) {
throw new IllegalStateException("Root package not found");
}
JetImportsFactory importsFactory = resolveSession.getJetImportsFactory();
List<ImportPath> defaultImports = resolveSession.getModuleDescriptor().getDefaultImports();
Collection<JetImportDirective> defaultImportDirectives = importsFactory.createImportDirectives(defaultImports);
return new LazyImportScope(
resolveSession,
rootPackage,
Lists.reverse(Lists.newArrayList(defaultImportDirectives)),
TemporaryBindingTrace.create(resolveSession.getTrace(), "Transient trace for default imports lazy resolve"),
"Lazy default imports scope",
false);
public LazyFileScope getScopeForFile(@NotNull JetFile file) {
return importScopes.invoke(file);
}
@NotNull
@@ -160,7 +160,7 @@ public abstract class ElementResolver {
}
else if (resolveElement instanceof JetImportDirective) {
JetImportDirective importDirective = (JetImportDirective) resolveElement;
LazyImportScope scope = resolveSession.getScopeProvider().getExplicitImportsScopeForFile(importDirective.getContainingJetFile());
LazyFileScope scope = resolveSession.getScopeProvider().getScopeForFile(importDirective.getContainingJetFile());
scope.forceResolveAllContents();
}
else if (resolveElement instanceof JetAnnotationEntry) {