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