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 }