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