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