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.base.Predicate;
020 import com.google.common.collect.Collections2;
021 import com.google.common.collect.Lists;
022 import com.google.common.collect.Sets;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.annotations.Nullable;
025 import org.jetbrains.jet.lang.descriptors.*;
026 import org.jetbrains.jet.lang.diagnostics.Errors;
027 import org.jetbrains.jet.lang.psi.*;
028 import org.jetbrains.jet.lang.resolve.name.Name;
029 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
030
031 import java.util.Collection;
032 import java.util.Collections;
033 import java.util.Set;
034
035 import static org.jetbrains.jet.lang.diagnostics.Errors.*;
036
037 public class QualifiedExpressionResolver {
038 private static final Predicate<DeclarationDescriptor> CLASSIFIERS_AND_NAMESPACES = new Predicate<DeclarationDescriptor>() {
039 @Override
040 public boolean apply(@Nullable DeclarationDescriptor descriptor) {
041 return descriptor instanceof ClassifierDescriptor || descriptor instanceof NamespaceDescriptor;
042 }
043 };
044
045 public enum LookupMode {
046 // Only classifier and packages are resolved
047 ONLY_CLASSES,
048
049 // Resolve all descriptors
050 EVERYTHING
051 }
052
053 @NotNull
054 public Collection<? extends DeclarationDescriptor> analyseImportReference(
055 @NotNull JetImportDirective importDirective,
056 @NotNull JetScope scope,
057 @NotNull BindingTrace trace,
058 @NotNull ModuleDescriptor module
059 ) {
060 return processImportReference(importDirective, scope, scope, Importer.DO_NOTHING, trace, module, LookupMode.EVERYTHING);
061 }
062
063 @NotNull
064 public Collection<? extends DeclarationDescriptor> processImportReference(
065 @NotNull JetImportDirective importDirective,
066 @NotNull JetScope scope,
067 @NotNull JetScope scopeToCheckVisibility,
068 @NotNull Importer importer,
069 @NotNull BindingTrace trace,
070 @NotNull ModuleDescriptor module,
071 @NotNull LookupMode lookupMode
072 ) {
073 if (importDirective.isAbsoluteInRootNamespace()) {
074 trace.report(UNSUPPORTED.on(importDirective, "TypeHierarchyResolver")); // TODO
075 return Collections.emptyList();
076 }
077 JetExpression importedReference = importDirective.getImportedReference();
078 if (importedReference == null) {
079 return Collections.emptyList();
080 }
081
082 Collection<? extends DeclarationDescriptor> descriptors;
083 if (importedReference instanceof JetQualifiedExpression) {
084 //store result only when we find all descriptors, not only classes on the second phase
085 descriptors = lookupDescriptorsForQualifiedExpression(
086 (JetQualifiedExpression)importedReference, scope, scopeToCheckVisibility, trace,
087 lookupMode, lookupMode == LookupMode.EVERYTHING);
088 }
089 else {
090 assert importedReference instanceof JetSimpleNameExpression;
091 descriptors = lookupDescriptorsForSimpleNameReference(
092 (JetSimpleNameExpression)importedReference, scope, scopeToCheckVisibility, trace,
093 lookupMode, true, lookupMode == LookupMode.EVERYTHING);
094 }
095
096 JetSimpleNameExpression referenceExpression = JetPsiUtil.getLastReference(importedReference);
097 if (importDirective.isAllUnder()) {
098 if (referenceExpression == null || !canImportMembersFrom(descriptors, referenceExpression, trace, lookupMode)) {
099 return Collections.emptyList();
100 }
101
102 for (DeclarationDescriptor descriptor : descriptors) {
103 importer.addAllUnderImport(descriptor, module.getPlatformToKotlinClassMap());
104 }
105 return Collections.emptyList();
106 }
107
108 Name aliasName = JetPsiUtil.getAliasName(importDirective);
109 if (aliasName == null) {
110 return Collections.emptyList();
111 }
112
113 for (DeclarationDescriptor descriptor : descriptors) {
114 importer.addAliasImport(descriptor, aliasName);
115 }
116
117 return descriptors;
118 }
119
120 private boolean canImportMembersFrom(@NotNull Collection<? extends DeclarationDescriptor> descriptors,
121 @NotNull JetSimpleNameExpression reference, @NotNull BindingTrace trace, @NotNull LookupMode lookupMode
122 ) {
123
124 if (lookupMode == LookupMode.ONLY_CLASSES) {
125 return true;
126 }
127
128 if (descriptors.size() == 1) {
129 return canImportMembersFrom(descriptors.iterator().next(), reference, trace, lookupMode);
130 }
131 TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(trace, "trace to find out if members can be imported from", reference);
132 boolean canImport = false;
133 for (DeclarationDescriptor descriptor : descriptors) {
134 canImport |= canImportMembersFrom(descriptor, reference, temporaryTrace, lookupMode);
135 }
136 if (!canImport) {
137 temporaryTrace.commit();
138 }
139 return canImport;
140 }
141
142 private boolean canImportMembersFrom(@NotNull DeclarationDescriptor descriptor,
143 @NotNull JetSimpleNameExpression reference, @NotNull BindingTrace trace, @NotNull LookupMode lookupMode
144 ) {
145
146 assert lookupMode == LookupMode.EVERYTHING;
147 if (descriptor instanceof NamespaceDescriptor) {
148 return true;
149 }
150 if (descriptor instanceof ClassDescriptor && !((ClassDescriptor)descriptor).getKind().isSingleton()) {
151 return true;
152 }
153 trace.report(CANNOT_IMPORT_FROM_ELEMENT.on(reference, descriptor));
154 return false;
155 }
156
157 @NotNull
158 public Collection<? extends DeclarationDescriptor> lookupDescriptorsForUserType(@NotNull JetUserType userType,
159 @NotNull JetScope outerScope, @NotNull BindingTrace trace) {
160
161 if (userType.isAbsoluteInRootNamespace()) {
162 trace.report(Errors.UNSUPPORTED.on(userType, "package"));
163 return Collections.emptyList();
164 }
165 JetSimpleNameExpression referenceExpression = userType.getReferenceExpression();
166 if (referenceExpression == null) {
167 return Collections.emptyList();
168 }
169 JetUserType qualifier = userType.getQualifier();
170 if (qualifier == null) {
171 return lookupDescriptorsForSimpleNameReference(referenceExpression, outerScope, outerScope, trace, LookupMode.ONLY_CLASSES, false, true);
172 }
173 Collection<? extends DeclarationDescriptor> declarationDescriptors = lookupDescriptorsForUserType(qualifier, outerScope, trace);
174 return lookupSelectorDescriptors(referenceExpression, declarationDescriptors, trace, outerScope, LookupMode.ONLY_CLASSES, true);
175 }
176
177 @NotNull
178 public Collection<? extends DeclarationDescriptor> lookupDescriptorsForQualifiedExpression(@NotNull JetQualifiedExpression importedReference,
179 @NotNull JetScope outerScope, @NotNull JetScope scopeToCheckVisibility, @NotNull BindingTrace trace, @NotNull LookupMode lookupMode, boolean storeResult) {
180
181 JetExpression receiverExpression = importedReference.getReceiverExpression();
182 Collection<? extends DeclarationDescriptor> declarationDescriptors;
183 if (receiverExpression instanceof JetQualifiedExpression) {
184 declarationDescriptors = lookupDescriptorsForQualifiedExpression((JetQualifiedExpression)receiverExpression, outerScope, scopeToCheckVisibility, trace,
185 lookupMode, storeResult);
186 }
187 else {
188 assert receiverExpression instanceof JetSimpleNameExpression;
189 declarationDescriptors = lookupDescriptorsForSimpleNameReference((JetSimpleNameExpression)receiverExpression, outerScope, scopeToCheckVisibility, trace,
190 lookupMode, true, storeResult);
191 }
192
193 JetExpression selectorExpression = importedReference.getSelectorExpression();
194 if (!(selectorExpression instanceof JetSimpleNameExpression)) {
195 return Collections.emptyList();
196 }
197
198 JetSimpleNameExpression selector = (JetSimpleNameExpression)selectorExpression;
199 JetSimpleNameExpression lastReference = JetPsiUtil.getLastReference(receiverExpression);
200 if (lastReference == null || !canImportMembersFrom(declarationDescriptors, lastReference, trace, lookupMode)) {
201 return Collections.emptyList();
202 }
203
204 return lookupSelectorDescriptors(selector, declarationDescriptors, trace, scopeToCheckVisibility, lookupMode, storeResult);
205 }
206
207 @NotNull
208 private Collection<? extends DeclarationDescriptor> lookupSelectorDescriptors(@NotNull JetSimpleNameExpression selector,
209 @NotNull Collection<? extends DeclarationDescriptor> declarationDescriptors, @NotNull BindingTrace trace,
210 @NotNull JetScope scopeToCheckVisibility, @NotNull LookupMode lookupMode, boolean storeResult) {
211
212 Set<SuccessfulLookupResult> results = Sets.newHashSet();
213 for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) {
214 if (declarationDescriptor instanceof NamespaceDescriptor) {
215 addResult(results, lookupSimpleNameReference(selector, ((NamespaceDescriptor)declarationDescriptor).getMemberScope(),
216 lookupMode, true));
217 }
218 if (declarationDescriptor instanceof ClassDescriptor) {
219 addResult(results, lookupSimpleNameReference(selector, getAppropriateScope((ClassDescriptor)declarationDescriptor,
220 lookupMode), lookupMode, false));
221 ClassDescriptor classObjectDescriptor = ((ClassDescriptor)declarationDescriptor).getClassObjectDescriptor();
222 if (classObjectDescriptor != null) {
223 addResult(results, lookupSimpleNameReference(selector, getAppropriateScope(classObjectDescriptor, lookupMode),
224 lookupMode, false));
225 }
226 }
227 }
228 return filterAndStoreResolutionResult(results, selector, trace, scopeToCheckVisibility, lookupMode, storeResult);
229 }
230
231 @NotNull
232 private JetScope getAppropriateScope(@NotNull ClassDescriptor classDescriptor, @NotNull LookupMode lookupMode) {
233 return lookupMode == LookupMode.ONLY_CLASSES ? classDescriptor.getUnsubstitutedInnerClassesScope() : classDescriptor.getDefaultType().getMemberScope();
234 }
235
236 private void addResult(@NotNull Set<SuccessfulLookupResult> results, @NotNull LookupResult result) {
237 if (result == LookupResult.EMPTY) return;
238 results.add((SuccessfulLookupResult)result);
239 }
240
241
242 @NotNull
243 public Collection<? extends DeclarationDescriptor> lookupDescriptorsForSimpleNameReference(@NotNull JetSimpleNameExpression referenceExpression,
244 @NotNull JetScope outerScope, @NotNull JetScope scopeToCheckVisibility, @NotNull BindingTrace trace, @NotNull LookupMode lookupMode, boolean namespaceLevel, boolean storeResult) {
245
246 LookupResult lookupResult = lookupSimpleNameReference(referenceExpression, outerScope, lookupMode, namespaceLevel);
247 if (lookupResult == LookupResult.EMPTY) return Collections.emptyList();
248 return filterAndStoreResolutionResult(Collections.singletonList((SuccessfulLookupResult)lookupResult), referenceExpression, trace, scopeToCheckVisibility,
249 lookupMode, storeResult);
250 }
251
252 @NotNull
253 private LookupResult lookupSimpleNameReference(@NotNull JetSimpleNameExpression referenceExpression,
254 @NotNull JetScope outerScope, @NotNull LookupMode lookupMode, boolean namespaceLevel) {
255
256 Name referencedName = referenceExpression.getReferencedNameAsName();
257
258 Set<DeclarationDescriptor> descriptors = Sets.newHashSet();
259 NamespaceDescriptor namespaceDescriptor = outerScope.getNamespace(referencedName);
260 if (namespaceDescriptor != null) {
261 descriptors.add(namespaceDescriptor);
262 }
263
264 ClassifierDescriptor classifierDescriptor = outerScope.getClassifier(referencedName);
265 if (classifierDescriptor != null) {
266 descriptors.add(classifierDescriptor);
267 }
268
269 if (lookupMode == LookupMode.EVERYTHING) {
270 descriptors.addAll(outerScope.getFunctions(referencedName));
271 descriptors.addAll(outerScope.getProperties(referencedName));
272
273 VariableDescriptor localVariable = outerScope.getLocalVariable(referencedName);
274 if (localVariable != null) {
275 descriptors.add(localVariable);
276 }
277 }
278 return new SuccessfulLookupResult(descriptors, outerScope, namespaceLevel);
279 }
280
281 @NotNull
282 private Collection<? extends DeclarationDescriptor> filterAndStoreResolutionResult(@NotNull Collection<SuccessfulLookupResult> lookupResults,
283 @NotNull JetSimpleNameExpression referenceExpression, @NotNull final BindingTrace trace, @NotNull JetScope scopeToCheckVisibility,
284 @NotNull LookupMode lookupMode, boolean storeResult) {
285
286 if (lookupResults.isEmpty()) {
287 return Collections.emptyList();
288 }
289 Collection<DeclarationDescriptor> descriptors = Sets.newLinkedHashSet();
290 for (SuccessfulLookupResult lookupResult : lookupResults) {
291 descriptors.addAll(lookupResult.descriptors);
292 }
293
294 Collection<JetScope> possibleResolutionScopes = Lists.newArrayList();
295 for (SuccessfulLookupResult lookupResult : lookupResults) {
296 if (!lookupResult.descriptors.isEmpty()) {
297 possibleResolutionScopes.add(lookupResult.resolutionScope);
298 }
299 }
300 if (possibleResolutionScopes.isEmpty()) {
301 for (SuccessfulLookupResult lookupResult : lookupResults) {
302 possibleResolutionScopes.add(lookupResult.resolutionScope);
303 }
304 }
305
306 Collection<DeclarationDescriptor> filteredDescriptors;
307 if (lookupMode == LookupMode.ONLY_CLASSES) {
308 filteredDescriptors = Collections2.filter(descriptors, CLASSIFIERS_AND_NAMESPACES);
309 }
310 else {
311 filteredDescriptors = Sets.newLinkedHashSet();
312 //functions and properties can be imported if lookupResult.namespaceLevel == true
313 for (SuccessfulLookupResult lookupResult : lookupResults) {
314 if (lookupResult.namespaceLevel) {
315 filteredDescriptors.addAll(lookupResult.descriptors);
316 continue;
317 }
318 filteredDescriptors.addAll(Collections2.filter(lookupResult.descriptors, CLASSIFIERS_AND_NAMESPACES));
319 }
320 }
321 if (storeResult) {
322 storeResolutionResult(descriptors, filteredDescriptors, referenceExpression, possibleResolutionScopes, trace,
323 scopeToCheckVisibility);
324 }
325 return filteredDescriptors;
326 }
327
328 private void storeResolutionResult(@NotNull Collection<? extends DeclarationDescriptor> descriptors,
329 @NotNull Collection<? extends DeclarationDescriptor> canBeImportedDescriptors,
330 @NotNull JetSimpleNameExpression referenceExpression,
331 @NotNull Collection<JetScope> possibleResolutionScopes,
332 @NotNull BindingTrace trace,
333 @NotNull JetScope scopeToCheckVisibility) {
334
335 assert canBeImportedDescriptors.size() <= descriptors.size();
336 assert !possibleResolutionScopes.isEmpty();
337 //todo completion here needs all possible resolution scopes, if there are many
338 JetScope resolutionScope = possibleResolutionScopes.iterator().next();
339
340 // A special case - will fill all trace information
341 if (resolveClassNamespaceAmbiguity(canBeImportedDescriptors, referenceExpression, resolutionScope, trace, scopeToCheckVisibility)) {
342 return;
343 }
344
345 // Simple case of no descriptors
346 if (descriptors.isEmpty()) {
347 trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
348 trace.report(UNRESOLVED_REFERENCE.on(referenceExpression, referenceExpression));
349 return;
350 }
351
352 // Decide if expression has resolved reference
353 DeclarationDescriptor descriptor = null;
354 if (descriptors.size() == 1) {
355 descriptor = descriptors.iterator().next();
356 assert canBeImportedDescriptors.size() <= 1;
357 }
358 else if (canBeImportedDescriptors.size() == 1) {
359 descriptor = canBeImportedDescriptors.iterator().next();
360 }
361 if (descriptor != null) {
362 trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, descriptors.iterator().next());
363 trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
364
365 if (descriptor instanceof DeclarationDescriptorWithVisibility) {
366 checkVisibility((DeclarationDescriptorWithVisibility)descriptor, trace, referenceExpression, scopeToCheckVisibility);
367 }
368 }
369
370 // Check for more information and additional errors
371 if (canBeImportedDescriptors.isEmpty()) {
372 assert descriptors.size() >= 1;
373 trace.report(CANNOT_BE_IMPORTED.on(referenceExpression, descriptors.iterator().next()));
374 return;
375 }
376 if (canBeImportedDescriptors.size() > 1) {
377 trace.record(BindingContext.AMBIGUOUS_REFERENCE_TARGET, referenceExpression, descriptors);
378 }
379 }
380
381 /**
382 * This method tries to resolve descriptors ambiguity between class descriptor and namespace descriptor for the same class.
383 * It's ok choose class for expression reference resolution.
384 *
385 * @return <code>true</code> if method has successfully resolved ambiguity
386 */
387 private boolean resolveClassNamespaceAmbiguity(@NotNull Collection<? extends DeclarationDescriptor> filteredDescriptors,
388 @NotNull JetSimpleNameExpression referenceExpression, @NotNull JetScope resolutionScope, @NotNull BindingTrace trace,
389 @NotNull JetScope scopeToCheckVisibility) {
390
391 if (filteredDescriptors.size() == 2) {
392 NamespaceDescriptor namespaceDescriptor = null;
393 ClassDescriptor classDescriptor = null;
394
395 for (DeclarationDescriptor filteredDescriptor : filteredDescriptors) {
396 if (filteredDescriptor instanceof NamespaceDescriptor) {
397 namespaceDescriptor = (NamespaceDescriptor)filteredDescriptor;
398 }
399 else if (filteredDescriptor instanceof ClassDescriptor) {
400 classDescriptor = (ClassDescriptor)filteredDescriptor;
401 }
402 }
403
404 if (namespaceDescriptor != null && classDescriptor != null) {
405 if (DescriptorUtils.getFQName(namespaceDescriptor).equalsTo(DescriptorUtils.getFQName(classDescriptor))) {
406 trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, classDescriptor);
407 trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
408 checkVisibility(classDescriptor, trace, referenceExpression, scopeToCheckVisibility);
409 return true;
410 }
411 }
412 }
413
414 return false;
415 }
416
417 private void checkVisibility(@NotNull DeclarationDescriptorWithVisibility descriptor, @NotNull BindingTrace trace,
418 @NotNull JetSimpleNameExpression referenceExpression, @NotNull JetScope scopeToCheckVisibility) {
419
420 if (!Visibilities.isVisible(descriptor, scopeToCheckVisibility.getContainingDeclaration())) {
421 trace.report(INVISIBLE_REFERENCE.on(referenceExpression, descriptor, descriptor.getVisibility(), descriptor.getContainingDeclaration()));
422 }
423 }
424
425 private interface LookupResult {
426 LookupResult EMPTY = new LookupResult() {
427 };
428 }
429
430 private static class SuccessfulLookupResult implements LookupResult {
431 final Collection<? extends DeclarationDescriptor> descriptors;
432 final JetScope resolutionScope;
433 final boolean namespaceLevel;
434
435 private SuccessfulLookupResult(Collection<? extends DeclarationDescriptor> descriptors,
436 JetScope resolutionScope,
437 boolean namespaceLevel) {
438 this.descriptors = descriptors;
439 this.resolutionScope = resolutionScope;
440 this.namespaceLevel = namespaceLevel;
441 }
442 }
443 }