001 /*
002 * Copyright 2010-2015 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.kotlin.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 com.intellij.psi.util.PsiTreeUtil;
024 import kotlin.KotlinPackage;
025 import org.jetbrains.annotations.Mutable;
026 import org.jetbrains.annotations.NotNull;
027 import org.jetbrains.annotations.Nullable;
028 import org.jetbrains.kotlin.descriptors.*;
029 import org.jetbrains.kotlin.diagnostics.Errors;
030 import org.jetbrains.kotlin.name.Name;
031 import org.jetbrains.kotlin.psi.*;
032 import org.jetbrains.kotlin.resolve.scopes.AbstractScopeAdapter;
033 import org.jetbrains.kotlin.resolve.scopes.JetScope;
034 import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
035 import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator;
036
037 import javax.inject.Inject;
038 import java.util.Collection;
039 import java.util.Collections;
040 import java.util.Set;
041
042 import static org.jetbrains.kotlin.diagnostics.Errors.*;
043
044 public class QualifiedExpressionResolver {
045 private SymbolUsageValidator symbolUsageValidator;
046
047 /**
048 * @deprecated Instance of this class should be obtained from the Injector
049 */
050 @Deprecated
051 public QualifiedExpressionResolver() {
052 }
053
054 @Inject
055 public void setSymbolUsageValidator(@NotNull SymbolUsageValidator symbolUsageValidator) {
056 this.symbolUsageValidator = symbolUsageValidator;
057 }
058
059 private static final Predicate<DeclarationDescriptor> CLASSIFIERS_AND_PACKAGE_VIEWS = new Predicate<DeclarationDescriptor>() {
060 @Override
061 public boolean apply(@Nullable DeclarationDescriptor descriptor) {
062 return descriptor instanceof ClassifierDescriptor || descriptor instanceof PackageViewDescriptor;
063 }
064 };
065
066 public enum LookupMode {
067 // Only classifier and packages are resolved
068 ONLY_CLASSES_AND_PACKAGES,
069
070 // Resolve all descriptors
071 EVERYTHING
072 }
073
074 public static boolean canAllUnderImportFrom(@NotNull Collection<DeclarationDescriptor> descriptors) {
075 if (descriptors.isEmpty()) {
076 return true;
077 }
078 for (DeclarationDescriptor descriptor : descriptors) {
079 if (!(descriptor instanceof ClassDescriptor)) {
080 return true;
081 }
082 if (canAllUnderImportFromClass((ClassDescriptor) descriptor)) {
083 return true;
084 }
085 }
086 return false;
087 }
088
089 public static boolean canAllUnderImportFromClass(@NotNull ClassDescriptor descriptor) {
090 return !descriptor.getKind().isSingleton();
091 }
092
093 @NotNull
094 public JetScope processImportReference(
095 @NotNull JetImportDirective importDirective,
096 @NotNull JetScope scope,
097 @NotNull JetScope scopeToCheckVisibility,
098 @NotNull BindingTrace trace,
099 @NotNull LookupMode lookupMode
100 ) {
101 if (importDirective.isAbsoluteInRootPackage()) {
102 trace.report(UNSUPPORTED.on(importDirective, "TypeHierarchyResolver")); // TODO
103 return JetScope.Empty.INSTANCE$;
104 }
105 JetExpression importedReference = importDirective.getImportedReference();
106 if (importedReference == null) {
107 return JetScope.Empty.INSTANCE$;
108 }
109
110 Collection<DeclarationDescriptor> descriptors;
111 if (importedReference instanceof JetQualifiedExpression) {
112 //store result only when we find all descriptors, not only classes on the second phase
113 descriptors = lookupDescriptorsForQualifiedExpression(
114 (JetQualifiedExpression) importedReference, scope, scopeToCheckVisibility, trace,
115 lookupMode, lookupMode == LookupMode.EVERYTHING);
116 }
117 else {
118 assert importedReference instanceof JetSimpleNameExpression;
119 descriptors = lookupDescriptorsForSimpleNameReference(
120 (JetSimpleNameExpression) importedReference, scope, scopeToCheckVisibility, trace,
121 lookupMode, true, lookupMode == LookupMode.EVERYTHING);
122 }
123
124 JetSimpleNameExpression referenceExpression = JetPsiUtil.getLastReference(importedReference);
125 if (importDirective.isAllUnder()) {
126 if (!canAllUnderImportFrom(descriptors) && referenceExpression != null) {
127 ClassDescriptor toReportOn = KotlinPackage.filterIsInstance(descriptors, ClassDescriptor.class).iterator().next();
128 trace.report(CANNOT_IMPORT_ON_DEMAND_FROM_SINGLETON.on(referenceExpression, toReportOn));
129 }
130
131 if (referenceExpression == null || !canImportMembersFrom(descriptors, referenceExpression, trace, lookupMode)) {
132 return JetScope.Empty.INSTANCE$;
133 }
134
135 AllUnderImportsScope importsScope = new AllUnderImportsScope();
136 for (DeclarationDescriptor descriptor : descriptors) {
137 importsScope.addAllUnderImport(descriptor);
138 }
139 return importsScope;
140 }
141 else {
142 Name aliasName = JetPsiUtil.getAliasName(importDirective);
143 if (aliasName == null) return JetScope.Empty.INSTANCE$;
144 return new SingleImportScope(aliasName, descriptors);
145 }
146 }
147
148 private static boolean canImportMembersFrom(
149 @NotNull Collection<DeclarationDescriptor> descriptors,
150 @NotNull JetSimpleNameExpression reference,
151 @NotNull BindingTrace trace,
152 @NotNull LookupMode lookupMode
153 ) {
154 if (lookupMode == LookupMode.ONLY_CLASSES_AND_PACKAGES) {
155 return true;
156 }
157
158 if (descriptors.size() == 1) {
159 return canImportMembersFrom(descriptors.iterator().next(), reference, trace, lookupMode);
160 }
161
162 TemporaryBindingTrace temporaryTrace =
163 TemporaryBindingTrace.create(trace, "trace to find out if members can be imported from", reference);
164 boolean canImport = false;
165 for (DeclarationDescriptor descriptor : descriptors) {
166 canImport |= canImportMembersFrom(descriptor, reference, temporaryTrace, lookupMode);
167 }
168 if (!canImport) {
169 temporaryTrace.commit();
170 }
171 return canImport;
172 }
173
174 private static boolean canImportMembersFrom(
175 @NotNull DeclarationDescriptor descriptor,
176 @NotNull JetSimpleNameExpression reference,
177 @NotNull BindingTrace trace,
178 @NotNull LookupMode lookupMode
179 ) {
180 assert lookupMode == LookupMode.EVERYTHING;
181 if (descriptor instanceof PackageViewDescriptor) {
182 return true;
183 }
184 if (descriptor instanceof ClassDescriptor) {
185 return true;
186 }
187 trace.report(CANNOT_IMPORT_FROM_ELEMENT.on(reference, descriptor));
188 return false;
189 }
190
191 @NotNull
192 public Collection<DeclarationDescriptor> lookupDescriptorsForUserType(
193 @NotNull JetUserType userType,
194 @NotNull JetScope outerScope,
195 @NotNull BindingTrace trace,
196 boolean onlyClassifiers
197 ) {
198
199 if (userType.isAbsoluteInRootPackage()) {
200 trace.report(Errors.UNSUPPORTED.on(userType, "package"));
201 return Collections.emptyList();
202 }
203
204 JetSimpleNameExpression referenceExpression = userType.getReferenceExpression();
205 if (referenceExpression == null) {
206 return Collections.emptyList();
207 }
208 JetUserType qualifier = userType.getQualifier();
209
210 // We do not want to resolve the last segment of a user type to a package
211 JetScope filteredScope = filterOutPackagesIfNeeded(outerScope, onlyClassifiers);
212
213 if (qualifier == null) {
214 return lookupDescriptorsForSimpleNameReference(referenceExpression, filteredScope, outerScope, trace, LookupMode.ONLY_CLASSES_AND_PACKAGES,
215 false, true);
216 }
217 Collection<DeclarationDescriptor> declarationDescriptors = lookupDescriptorsForUserType(qualifier, outerScope, trace, false);
218 return lookupSelectorDescriptors(referenceExpression, declarationDescriptors, trace, filteredScope, LookupMode.ONLY_CLASSES_AND_PACKAGES, true);
219 }
220
221 private static JetScope filterOutPackagesIfNeeded(final JetScope outerScope, boolean noPackages) {
222 return !noPackages ? outerScope : new AbstractScopeAdapter() {
223
224 @NotNull
225 @Override
226 protected JetScope getWorkerScope() {
227 return outerScope;
228 }
229
230 @Nullable
231 @Override
232 public PackageViewDescriptor getPackage(@NotNull Name name) {
233 return null;
234 }
235 };
236 }
237
238 @NotNull
239 public Collection<DeclarationDescriptor> lookupDescriptorsForQualifiedExpression(
240 @NotNull JetQualifiedExpression importedReference,
241 @NotNull JetScope outerScope,
242 @NotNull JetScope scopeToCheckVisibility,
243 @NotNull BindingTrace trace,
244 @NotNull LookupMode lookupMode,
245 boolean storeResult
246 ) {
247 JetExpression receiverExpression = importedReference.getReceiverExpression();
248 Collection<DeclarationDescriptor> declarationDescriptors;
249 if (receiverExpression instanceof JetQualifiedExpression) {
250 declarationDescriptors =
251 lookupDescriptorsForQualifiedExpression((JetQualifiedExpression) receiverExpression, outerScope, scopeToCheckVisibility,
252 trace, lookupMode, storeResult);
253 }
254 else {
255 assert receiverExpression instanceof JetSimpleNameExpression;
256 declarationDescriptors =
257 lookupDescriptorsForSimpleNameReference((JetSimpleNameExpression) receiverExpression, outerScope,
258 scopeToCheckVisibility, trace, lookupMode, true, storeResult);
259 }
260
261 JetExpression selectorExpression = importedReference.getSelectorExpression();
262 if (!(selectorExpression instanceof JetSimpleNameExpression)) {
263 return Collections.emptyList();
264 }
265
266 JetSimpleNameExpression selector = (JetSimpleNameExpression) selectorExpression;
267 JetSimpleNameExpression lastReference = JetPsiUtil.getLastReference(receiverExpression);
268 if (lastReference == null || !canImportMembersFrom(declarationDescriptors, lastReference, trace, lookupMode)) {
269 return Collections.emptyList();
270 }
271
272 return lookupSelectorDescriptors(selector, declarationDescriptors, trace, scopeToCheckVisibility, lookupMode, storeResult);
273 }
274
275 @NotNull
276 private Collection<DeclarationDescriptor> lookupSelectorDescriptors(
277 @NotNull JetSimpleNameExpression selector,
278 @NotNull Collection<DeclarationDescriptor> declarationDescriptors,
279 @NotNull BindingTrace trace,
280 @NotNull JetScope scopeToCheckVisibility,
281 @NotNull LookupMode lookupMode,
282 boolean storeResult
283 ) {
284 Set<LookupResult> results = Sets.newLinkedHashSet();
285 for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) {
286 if (declarationDescriptor instanceof PackageViewDescriptor) {
287 results.add(lookupSimpleNameReference(selector, ((PackageViewDescriptor) declarationDescriptor).getMemberScope(),
288 lookupMode, true));
289 }
290 if (declarationDescriptor instanceof ClassDescriptor) {
291 addResultsForClass(results, selector, lookupMode, (ClassDescriptor) declarationDescriptor);
292 }
293 }
294 return filterAndStoreResolutionResult(results, selector, trace, scopeToCheckVisibility, lookupMode, storeResult);
295 }
296
297 private static void addResultsForClass(
298 @NotNull @Mutable Set<LookupResult> results,
299 @NotNull JetSimpleNameExpression selector,
300 @NotNull LookupMode lookupMode,
301 @NotNull ClassDescriptor descriptor
302 ) {
303 JetScope scope = lookupMode == LookupMode.ONLY_CLASSES_AND_PACKAGES
304 ? descriptor.getUnsubstitutedInnerClassesScope()
305 : descriptor.getDefaultType().getMemberScope();
306 results.add(lookupSimpleNameReference(selector, scope, lookupMode, false));
307
308 results.add(lookupSimpleNameReference(selector, descriptor.getStaticScope(), lookupMode, true));
309 }
310
311
312 @NotNull
313 @SuppressWarnings("MethodMayBeStatic")
314 public Collection<DeclarationDescriptor> lookupDescriptorsForSimpleNameReference(
315 @NotNull JetSimpleNameExpression referenceExpression,
316 @NotNull JetScope outerScope,
317 @NotNull JetScope scopeToCheckVisibility,
318 @NotNull BindingTrace trace,
319 @NotNull LookupMode lookupMode,
320 boolean packageLevel,
321 boolean storeResult
322 ) {
323 LookupResult lookupResult = lookupSimpleNameReference(referenceExpression, outerScope, lookupMode, packageLevel);
324 return filterAndStoreResolutionResult(Collections.singletonList(lookupResult), referenceExpression, trace, scopeToCheckVisibility,
325 lookupMode, storeResult);
326 }
327
328 @NotNull
329 private static LookupResult lookupSimpleNameReference(
330 @NotNull JetSimpleNameExpression referenceExpression,
331 @NotNull JetScope outerScope,
332 @NotNull LookupMode lookupMode,
333 boolean packageLevel
334 ) {
335 Name referencedName = referenceExpression.getReferencedNameAsName();
336
337 Collection<DeclarationDescriptor> descriptors = Sets.newLinkedHashSet();
338 PackageViewDescriptor packageDescriptor = outerScope.getPackage(referencedName);
339 if (packageDescriptor != null) {
340 descriptors.add(packageDescriptor);
341 }
342
343 ClassifierDescriptor classifierDescriptor = outerScope.getClassifier(referencedName);
344 if (classifierDescriptor != null) {
345 descriptors.add(classifierDescriptor);
346 }
347
348 if (lookupMode == LookupMode.EVERYTHING) {
349 descriptors.addAll(outerScope.getFunctions(referencedName));
350 descriptors.addAll(outerScope.getProperties(referencedName));
351
352 VariableDescriptor localVariable = outerScope.getLocalVariable(referencedName);
353 if (localVariable != null) {
354 descriptors.add(localVariable);
355 }
356 }
357
358 return new LookupResult(descriptors, outerScope, packageLevel);
359 }
360
361 @NotNull
362 private Collection<DeclarationDescriptor> filterAndStoreResolutionResult(
363 @NotNull Collection<LookupResult> lookupResults,
364 @NotNull JetSimpleNameExpression referenceExpression,
365 @NotNull BindingTrace trace,
366 @NotNull JetScope scopeToCheckVisibility,
367 @NotNull LookupMode lookupMode,
368 boolean storeResult
369 ) {
370 if (lookupResults.isEmpty()) {
371 return Collections.emptyList();
372 }
373
374 Collection<DeclarationDescriptor> descriptors = Sets.newLinkedHashSet();
375 for (LookupResult lookupResult : lookupResults) {
376 descriptors.addAll(lookupResult.descriptors);
377 }
378
379 Collection<JetScope> possibleResolutionScopes = Lists.newArrayList();
380 for (LookupResult lookupResult : lookupResults) {
381 if (!lookupResult.descriptors.isEmpty()) {
382 possibleResolutionScopes.add(lookupResult.resolutionScope);
383 }
384 }
385 if (possibleResolutionScopes.isEmpty()) {
386 for (LookupResult lookupResult : lookupResults) {
387 possibleResolutionScopes.add(lookupResult.resolutionScope);
388 }
389 }
390
391 Collection<DeclarationDescriptor> filteredDescriptors;
392 if (lookupMode == LookupMode.ONLY_CLASSES_AND_PACKAGES) {
393 filteredDescriptors = Collections2.filter(descriptors, CLASSIFIERS_AND_PACKAGE_VIEWS);
394 }
395 else {
396 filteredDescriptors = Sets.newLinkedHashSet();
397 //functions and properties can be imported if lookupResult.packageLevel == true
398 for (LookupResult lookupResult : lookupResults) {
399 if (lookupResult.packageLevel) {
400 filteredDescriptors.addAll(lookupResult.descriptors);
401 }
402 else {
403 filteredDescriptors.addAll(Collections2.filter(lookupResult.descriptors, CLASSIFIERS_AND_PACKAGE_VIEWS));
404 }
405 }
406 }
407
408 if (storeResult) {
409 storeResolutionResult(descriptors, filteredDescriptors, referenceExpression, possibleResolutionScopes, trace,
410 scopeToCheckVisibility);
411 }
412
413 return filteredDescriptors;
414 }
415
416 private void storeResolutionResult(
417 @NotNull Collection<DeclarationDescriptor> descriptors,
418 @NotNull Collection<DeclarationDescriptor> canBeImportedDescriptors,
419 @NotNull JetSimpleNameExpression referenceExpression,
420 @NotNull Collection<JetScope> possibleResolutionScopes,
421 @NotNull BindingTrace trace,
422 @NotNull JetScope scopeToCheckVisibility
423 ) {
424 assert canBeImportedDescriptors.size() <= descriptors.size();
425 assert !possibleResolutionScopes.isEmpty();
426 //todo completion here needs all possible resolution scopes, if there are many
427 JetScope resolutionScope = possibleResolutionScopes.iterator().next();
428
429 // A special case - will fill all trace information
430 if (resolveClassPackageAmbiguity(canBeImportedDescriptors, referenceExpression, resolutionScope, trace, scopeToCheckVisibility)) {
431 return;
432 }
433
434 // Simple case of no descriptors
435 if (descriptors.isEmpty()) {
436 trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
437 trace.report(UNRESOLVED_REFERENCE.on(referenceExpression, referenceExpression));
438 return;
439 }
440
441 // Decide if expression has resolved reference
442 DeclarationDescriptor descriptor = null;
443 if (descriptors.size() == 1) {
444 descriptor = descriptors.iterator().next();
445 assert canBeImportedDescriptors.size() <= 1;
446 }
447 else if (canBeImportedDescriptors.size() == 1) {
448 descriptor = canBeImportedDescriptors.iterator().next();
449 }
450 if (descriptor != null) {
451 trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, descriptors.iterator().next());
452 trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
453
454 if (descriptor instanceof ClassifierDescriptor) {
455 symbolUsageValidator.validateTypeUsage((ClassifierDescriptor) descriptor, trace, referenceExpression);
456 }
457
458 if (descriptor instanceof DeclarationDescriptorWithVisibility) {
459 checkVisibility((DeclarationDescriptorWithVisibility) descriptor, trace, referenceExpression, scopeToCheckVisibility);
460 }
461 }
462
463 // Check for more information and additional errors
464 if (canBeImportedDescriptors.isEmpty()) {
465 assert descriptors.size() >= 1;
466 trace.report(CANNOT_BE_IMPORTED.on(referenceExpression, descriptors.iterator().next()));
467 return;
468 }
469 if (canBeImportedDescriptors.size() > 1) {
470 trace.record(BindingContext.AMBIGUOUS_REFERENCE_TARGET, referenceExpression, descriptors);
471 }
472 }
473
474 /**
475 * This method tries to resolve descriptors ambiguity between class descriptor and package descriptor for the same class.
476 * It's ok choose class for expression reference resolution.
477 *
478 * @return <code>true</code> if method has successfully resolved ambiguity
479 */
480 private static boolean resolveClassPackageAmbiguity(
481 @NotNull Collection<DeclarationDescriptor> filteredDescriptors,
482 @NotNull JetSimpleNameExpression referenceExpression,
483 @NotNull JetScope resolutionScope,
484 @NotNull BindingTrace trace,
485 @NotNull JetScope scopeToCheckVisibility
486 ) {
487 if (filteredDescriptors.size() == 2) {
488 PackageViewDescriptor packageView = null;
489 ClassDescriptor classDescriptor = null;
490
491 for (DeclarationDescriptor filteredDescriptor : filteredDescriptors) {
492 if (filteredDescriptor instanceof PackageViewDescriptor) {
493 packageView = (PackageViewDescriptor) filteredDescriptor;
494 }
495 else if (filteredDescriptor instanceof ClassDescriptor) {
496 classDescriptor = (ClassDescriptor) filteredDescriptor;
497 }
498 }
499
500 if (packageView != null && classDescriptor != null) {
501 if (packageView.getFqName().equalsTo(DescriptorUtils.getFqName(classDescriptor))) {
502 trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, classDescriptor);
503 trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
504 checkVisibility(classDescriptor, trace, referenceExpression, scopeToCheckVisibility);
505 return true;
506 }
507 }
508 }
509
510 return false;
511 }
512
513 private static void checkVisibility(
514 @NotNull DeclarationDescriptorWithVisibility descriptor,
515 @NotNull BindingTrace trace,
516 @NotNull JetSimpleNameExpression referenceExpression,
517 @NotNull JetScope scopeToCheckVisibility
518 ) {
519 if (!Visibilities.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, descriptor, scopeToCheckVisibility.getContainingDeclaration())) {
520 Visibility visibility = descriptor.getVisibility();
521 if (PsiTreeUtil.getParentOfType(referenceExpression, JetImportDirective.class) != null && !visibility.mustCheckInImports()) return;
522 //noinspection ConstantConditions
523 trace.report(INVISIBLE_REFERENCE.on(referenceExpression, descriptor, visibility, descriptor.getContainingDeclaration()));
524 }
525 }
526
527 private static class LookupResult {
528 private final Collection<DeclarationDescriptor> descriptors;
529 private final JetScope resolutionScope;
530 private final boolean packageLevel;
531
532 public LookupResult(
533 @NotNull Collection<DeclarationDescriptor> descriptors,
534 @NotNull JetScope resolutionScope,
535 boolean packageLevel
536 ) {
537 this.descriptors = descriptors;
538 this.resolutionScope = resolutionScope;
539 this.packageLevel = packageLevel;
540 }
541 }
542 }