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