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 }