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.annotations.Nullable;
023    import org.jetbrains.jet.lang.PlatformToKotlinClassMap;
024    import org.jetbrains.jet.lang.descriptors.*;
025    import org.jetbrains.jet.lang.psi.*;
026    import org.jetbrains.jet.lang.resolve.name.Name;
027    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
028    import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
029    
030    import javax.inject.Inject;
031    import java.util.Collection;
032    import java.util.List;
033    import java.util.Map;
034    
035    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
036    import static org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode;
037    
038    public class ImportsResolver {
039        @NotNull
040        private ModuleDescriptor moduleDescriptor;
041        @NotNull
042        private QualifiedExpressionResolver qualifiedExpressionResolver;
043        @NotNull
044        private BindingTrace trace;
045        @NotNull
046        private JetImportsFactory importsFactory;
047    
048        @Inject
049        public void setModuleDescriptor(@NotNull ModuleDescriptor moduleDescriptor) {
050            this.moduleDescriptor = moduleDescriptor;
051        }
052    
053        @Inject
054        public void setTrace(@NotNull BindingTrace trace) {
055            this.trace = trace;
056        }
057    
058        @Inject
059        public void setQualifiedExpressionResolver(@NotNull QualifiedExpressionResolver qualifiedExpressionResolver) {
060            this.qualifiedExpressionResolver = qualifiedExpressionResolver;
061        }
062    
063        @Inject
064        public void setImportsFactory(@NotNull JetImportsFactory importsFactory) {
065            this.importsFactory = importsFactory;
066        }
067    
068        public void processTypeImports(@NotNull TopDownAnalysisContext c) {
069            processImports(c, LookupMode.ONLY_CLASSES);
070        }
071    
072        public void processMembersImports(@NotNull TopDownAnalysisContext c) {
073            processImports(c, LookupMode.EVERYTHING);
074        }
075    
076        private void processImports(@NotNull TopDownAnalysisContext c, @NotNull LookupMode lookupMode) {
077            for (JetFile file : c.getFiles()) {
078                WritableScope fileScope = c.getFileScopes().get(file);
079                processImportsInFile(lookupMode, fileScope, Lists.newArrayList(file.getImportDirectives()), JetPsiUtil.getFQName(file).isRoot());
080            }
081            for (JetScript script : c.getScripts().keySet()) {
082                WritableScope scriptScope = c.getScriptScopes().get(script);
083                processImportsInFile(lookupMode, scriptScope, script.getImportDirectives(), true);
084            }
085        }
086    
087        private void processImportsInFile(@NotNull LookupMode lookupMode, WritableScope scope, List<JetImportDirective> directives, boolean inRootPackage) {
088            processImportsInFile(lookupMode, scope, directives, moduleDescriptor, trace, qualifiedExpressionResolver, importsFactory, inRootPackage);
089        }
090    
091        private static void processImportsInFile(
092                LookupMode lookupMode,
093                @NotNull WritableScope fileScope,
094                @NotNull List<JetImportDirective> importDirectives,
095                @NotNull ModuleDescriptor module,
096                @NotNull BindingTrace trace,
097                @NotNull QualifiedExpressionResolver qualifiedExpressionResolver,
098                @NotNull JetImportsFactory importsFactory,
099                boolean inRootPackage
100        ) {
101            @NotNull JetScope rootScope = JetModuleUtil.getSubpackagesOfRootScope(module);
102    
103            Importer.DelayedImporter delayedImporter = new Importer.DelayedImporter(fileScope);
104            if (lookupMode == LookupMode.EVERYTHING) {
105                fileScope.clearImports();
106            }
107    
108            for (ImportPath defaultImportPath : module.getDefaultImports()) {
109                TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(
110                        trace, "transient trace to resolve default imports"); //not to trace errors of default imports
111    
112                JetImportDirective defaultImportDirective = importsFactory.createImportDirective(defaultImportPath);
113                qualifiedExpressionResolver.processImportReference(defaultImportDirective, rootScope, fileScope, delayedImporter,
114                                                                   temporaryTrace, module, lookupMode);
115            }
116    
117            Map<JetImportDirective, Collection<? extends DeclarationDescriptor>> resolvedDirectives = Maps.newHashMap();
118    
119            JetScope rootScopeForFile = JetModuleUtil.getImportsResolutionScope(module, inRootPackage);
120    
121            for (JetImportDirective importDirective : importDirectives) {
122                Collection<? extends DeclarationDescriptor> descriptors =
123                        qualifiedExpressionResolver.processImportReference(importDirective, rootScopeForFile, fileScope, delayedImporter,
124                                                                           trace, module, lookupMode);
125                if (!descriptors.isEmpty()) {
126                    resolvedDirectives.put(importDirective, descriptors);
127                }
128    
129                if (lookupMode != LookupMode.ONLY_CLASSES) {
130                    checkPlatformTypesMappedToKotlin(module, trace, importDirective, descriptors);
131                }
132            }
133            delayedImporter.processImports();
134    
135            if (lookupMode == LookupMode.EVERYTHING) {
136                for (JetImportDirective importDirective : importDirectives) {
137                    reportUselessImport(importDirective, fileScope, resolvedDirectives.get(importDirective), trace);
138                }
139            }
140        }
141    
142        public static void checkPlatformTypesMappedToKotlin(
143                @NotNull ModuleDescriptor module,
144                @NotNull BindingTrace trace,
145                @NotNull JetImportDirective importDirective,
146                @NotNull Collection<? extends DeclarationDescriptor> descriptors
147        ) {
148            JetExpression importedReference = importDirective.getImportedReference();
149            if (importedReference != null) {
150                for (DeclarationDescriptor descriptor : descriptors) {
151                    reportPlatformClassMappedToKotlin(module, trace, importedReference, descriptor);
152                }
153            }
154        }
155    
156        public static void reportPlatformClassMappedToKotlin(
157                @NotNull ModuleDescriptor module,
158                @NotNull BindingTrace trace,
159                @NotNull JetElement element,
160                @NotNull DeclarationDescriptor descriptor
161        ) {
162            if (!(descriptor instanceof ClassDescriptor)) return;
163    
164            PlatformToKotlinClassMap platformToKotlinMap = module.getPlatformToKotlinClassMap();
165            Collection<ClassDescriptor> kotlinAnalogsForClass = platformToKotlinMap.mapPlatformClass((ClassDescriptor) descriptor);
166            if (!kotlinAnalogsForClass.isEmpty()) {
167                trace.report(PLATFORM_CLASS_MAPPED_TO_KOTLIN.on(element, kotlinAnalogsForClass));
168            }
169        }
170    
171        public static void reportUselessImport(
172            @NotNull JetImportDirective importDirective,
173            @NotNull JetScope fileScope,
174            @Nullable Collection<? extends DeclarationDescriptor> resolvedDirectives,
175            @NotNull BindingTrace trace
176        ) {
177    
178            JetExpression importedReference = importDirective.getImportedReference();
179            if (importedReference == null || resolvedDirectives == null) {
180                return;
181            }
182            Name aliasName = JetPsiUtil.getAliasName(importDirective);
183            if (aliasName == null) {
184                return;
185            }
186    
187            boolean uselessHiddenImport = true;
188            for (DeclarationDescriptor wasResolved : resolvedDirectives) {
189                DeclarationDescriptor isResolved = null;
190                if (wasResolved instanceof ClassDescriptor) {
191                    isResolved = fileScope.getClassifier(aliasName);
192                }
193                else if (wasResolved instanceof VariableDescriptor) {
194                    isResolved = fileScope.getLocalVariable(aliasName);
195                }
196                else if (wasResolved instanceof PackageViewDescriptor) {
197                    isResolved = fileScope.getPackage(aliasName);
198                }
199                if (isResolved == null || isResolved.equals(wasResolved)) {
200                    uselessHiddenImport = false;
201                }
202            }
203            if (uselessHiddenImport) {
204                trace.report(USELESS_HIDDEN_IMPORT.on(importedReference));
205            }
206    
207            if (!importDirective.isAllUnder() &&
208                importedReference instanceof JetSimpleNameExpression &&
209                importDirective.getAliasName() == null) {
210                trace.report(USELESS_SIMPLE_IMPORT.on(importedReference));
211            }
212        }
213    }