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 017package org.jetbrains.jet.lang.resolve; 018 019import com.google.common.base.Predicate; 020import com.google.common.collect.Collections2; 021import com.google.common.collect.Lists; 022import com.google.common.collect.Sets; 023import org.jetbrains.annotations.NotNull; 024import org.jetbrains.annotations.Nullable; 025import org.jetbrains.jet.lang.descriptors.*; 026import org.jetbrains.jet.lang.diagnostics.Errors; 027import org.jetbrains.jet.lang.psi.*; 028import org.jetbrains.jet.lang.resolve.name.Name; 029import org.jetbrains.jet.lang.resolve.scopes.JetScope; 030 031import java.util.Collection; 032import java.util.Collections; 033import java.util.Set; 034 035import static org.jetbrains.jet.lang.diagnostics.Errors.*; 036 037public 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}