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