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