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