001/*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.jetbrains.jet.lang.resolve;
018
019import com.google.common.collect.Lists;
020import com.google.common.collect.Maps;
021import org.jetbrains.annotations.NotNull;
022import org.jetbrains.jet.lang.PlatformToKotlinClassMap;
023import org.jetbrains.jet.lang.descriptors.*;
024import org.jetbrains.jet.lang.psi.*;
025import org.jetbrains.jet.lang.resolve.name.Name;
026import org.jetbrains.jet.lang.resolve.scopes.JetScope;
027import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
028
029import javax.inject.Inject;
030import java.util.Collection;
031import java.util.List;
032import java.util.Map;
033
034import static org.jetbrains.jet.lang.diagnostics.Errors.*;
035import static org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode;
036
037public class ImportsResolver {
038    @NotNull
039    private TopDownAnalysisContext context;
040    @NotNull
041    private ModuleDescriptor moduleDescriptor;
042    @NotNull
043    private QualifiedExpressionResolver qualifiedExpressionResolver;
044    @NotNull
045    private BindingTrace trace;
046    @NotNull
047    private JetImportsFactory importsFactory;
048
049    @Inject
050    public void setContext(@NotNull TopDownAnalysisContext context) {
051        this.context = context;
052    }
053
054    @Inject
055    public void setModuleDescriptor(@NotNull ModuleDescriptor moduleDescriptor) {
056        this.moduleDescriptor = moduleDescriptor;
057    }
058
059    @Inject
060    public void setTrace(@NotNull BindingTrace trace) {
061        this.trace = trace;
062    }
063
064    @Inject
065    public void setQualifiedExpressionResolver(@NotNull QualifiedExpressionResolver qualifiedExpressionResolver) {
066        this.qualifiedExpressionResolver = qualifiedExpressionResolver;
067    }
068
069    @Inject
070    public void setImportsFactory(@NotNull JetImportsFactory importsFactory) {
071        this.importsFactory = importsFactory;
072    }
073
074    public void processTypeImports(@NotNull JetScope rootScope) {
075        processImports(LookupMode.ONLY_CLASSES, rootScope);
076    }
077
078    public void processMembersImports(@NotNull JetScope rootScope) {
079        processImports(LookupMode.EVERYTHING, rootScope);
080    }
081
082    private void processImports(@NotNull LookupMode lookupMode, @NotNull JetScope rootScope) {
083        for (JetFile file : context.getNamespaceDescriptors().keySet()) {
084            WritableScope namespaceScope = context.getNamespaceScopes().get(file);
085            processImportsInFile(lookupMode, namespaceScope, Lists.newArrayList(file.getImportDirectives()), rootScope);
086        }
087        for (JetScript script : context.getScripts().keySet()) {
088            WritableScope scriptScope = context.getScriptScopes().get(script);
089            processImportsInFile(lookupMode, scriptScope, script.getImportDirectives(), rootScope);
090        }
091    }
092
093    private void processImportsInFile(@NotNull LookupMode lookupMode, WritableScope scope, List<JetImportDirective> directives, JetScope rootScope) {
094        processImportsInFile(lookupMode, scope, directives, rootScope, moduleDescriptor, trace, qualifiedExpressionResolver, importsFactory);
095    }
096
097    public static void processImportsInFile(
098            LookupMode lookupMode,
099            @NotNull WritableScope namespaceScope,
100            @NotNull List<JetImportDirective> importDirectives,
101            @NotNull JetScope rootScope,
102            @NotNull ModuleDescriptor module,
103            @NotNull BindingTrace trace,
104            @NotNull QualifiedExpressionResolver qualifiedExpressionResolver,
105            @NotNull JetImportsFactory importsFactory
106    ) {
107
108        Importer.DelayedImporter delayedImporter = new Importer.DelayedImporter(namespaceScope);
109        if (lookupMode == LookupMode.EVERYTHING) {
110            namespaceScope.clearImports();
111        }
112
113        for (ImportPath defaultImportPath : module.getDefaultImports()) {
114            TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(
115                    trace, "transient trace to resolve default imports"); //not to trace errors of default imports
116
117            JetImportDirective defaultImportDirective = importsFactory.createImportDirective(defaultImportPath);
118            qualifiedExpressionResolver.processImportReference(defaultImportDirective, rootScope, namespaceScope, delayedImporter,
119                                                               temporaryTrace, module, lookupMode);
120        }
121
122        Map<JetImportDirective, Collection<? extends DeclarationDescriptor>> resolvedDirectives = Maps.newHashMap();
123
124        for (JetImportDirective importDirective : importDirectives) {
125            Collection<? extends DeclarationDescriptor> descriptors =
126                qualifiedExpressionResolver.processImportReference(importDirective, rootScope, namespaceScope, delayedImporter,
127                                                                   trace, module, lookupMode);
128            if (!descriptors.isEmpty()) {
129                resolvedDirectives.put(importDirective, descriptors);
130            }
131
132            JetExpression importedReference = importDirective.getImportedReference();
133            if (lookupMode != LookupMode.ONLY_CLASSES && importedReference != null) {
134                for (DeclarationDescriptor descriptor : descriptors) {
135                    reportPlatformClassMappedToKotlin(module, trace, importedReference, descriptor);
136                }
137            }
138        }
139        delayedImporter.processImports();
140
141        if (lookupMode == LookupMode.EVERYTHING) {
142            for (JetImportDirective importDirective : importDirectives) {
143                reportUselessImport(importDirective, namespaceScope, resolvedDirectives, trace);
144            }
145        }
146    }
147
148    public static void reportPlatformClassMappedToKotlin(
149            @NotNull ModuleDescriptor module,
150            @NotNull BindingTrace trace,
151            @NotNull JetElement element,
152            @NotNull DeclarationDescriptor descriptor
153    ) {
154        if (!(descriptor instanceof ClassDescriptor)) return;
155
156        PlatformToKotlinClassMap platformToKotlinMap = module.getPlatformToKotlinClassMap();
157        Collection<ClassDescriptor> kotlinAnalogsForClass = platformToKotlinMap.mapPlatformClass((ClassDescriptor) descriptor);
158        if (!kotlinAnalogsForClass.isEmpty()) {
159            trace.report(PLATFORM_CLASS_MAPPED_TO_KOTLIN.on(element, kotlinAnalogsForClass));
160        }
161    }
162
163    private static void reportUselessImport(
164        @NotNull JetImportDirective importDirective,
165        @NotNull WritableScope namespaceScope,
166        @NotNull Map<JetImportDirective, Collection<? extends DeclarationDescriptor>> resolvedDirectives,
167        @NotNull BindingTrace trace
168    ) {
169
170        JetExpression importedReference = importDirective.getImportedReference();
171        if (importedReference == null || !resolvedDirectives.containsKey(importDirective)) {
172            return;
173        }
174        Name aliasName = JetPsiUtil.getAliasName(importDirective);
175        if (aliasName == null) {
176            return;
177        }
178
179        boolean uselessHiddenImport = true;
180        for (DeclarationDescriptor wasResolved : resolvedDirectives.get(importDirective)) {
181            DeclarationDescriptor isResolved = null;
182            if (wasResolved instanceof ClassDescriptor) {
183                isResolved = namespaceScope.getClassifier(aliasName);
184            }
185            else if (wasResolved instanceof VariableDescriptor) {
186                isResolved = namespaceScope.getLocalVariable(aliasName);
187            }
188            else if (wasResolved instanceof NamespaceDescriptor) {
189                isResolved = namespaceScope.getNamespace(aliasName);
190            }
191            if (isResolved == null || isResolved == wasResolved) {
192                uselessHiddenImport = false;
193            }
194        }
195        if (uselessHiddenImport) {
196            trace.report(USELESS_HIDDEN_IMPORT.on(importedReference));
197        }
198
199        if (!importDirective.isAllUnder() &&
200            importedReference instanceof JetSimpleNameExpression &&
201            importDirective.getAliasName() == null) {
202            trace.report(USELESS_SIMPLE_IMPORT.on(importedReference));
203        }
204    }
205}