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    
017    package org.jetbrains.jet.lang.resolve;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Maps;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.jet.lang.PlatformToKotlinClassMap;
023    import org.jetbrains.jet.lang.descriptors.*;
024    import org.jetbrains.jet.lang.psi.*;
025    import org.jetbrains.jet.lang.resolve.name.Name;
026    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
027    import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
028    
029    import javax.inject.Inject;
030    import java.util.Collection;
031    import java.util.List;
032    import java.util.Map;
033    
034    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
035    import static org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode;
036    
037    public 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    }