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 org.jetbrains.annotations.Mutable;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.kotlin.descriptors.*;
028    import org.jetbrains.kotlin.diagnostics.Errors;
029    import org.jetbrains.kotlin.incremental.KotlinLookupLocation;
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 java.util.Collection;
038    import java.util.Collections;
039    import java.util.Set;
040    
041    import static org.jetbrains.kotlin.diagnostics.Errors.*;
042    
043    public class QualifiedExpressionResolver {
044        @NotNull private final SymbolUsageValidator symbolUsageValidator;
045        @NotNull private final ImportDirectiveProcessor importDirectiveProcessor = new ImportDirectiveProcessor(this);
046    
047        public QualifiedExpressionResolver(@NotNull SymbolUsageValidator symbolUsageValidator) {
048            this.symbolUsageValidator = symbolUsageValidator;
049        }
050    
051        private static final Predicate<DeclarationDescriptor> CLASSIFIERS_AND_PACKAGE_VIEWS = new Predicate<DeclarationDescriptor>() {
052            @Override
053            public boolean apply(@Nullable DeclarationDescriptor descriptor) {
054                return descriptor instanceof ClassifierDescriptor || descriptor instanceof PackageViewDescriptor;
055            }
056        };
057    
058        public enum LookupMode {
059            // Only classifier and packages are resolved
060            ONLY_CLASSES_AND_PACKAGES,
061    
062            // Resolve all descriptors
063            EVERYTHING
064        }
065    
066        @NotNull
067        public JetScope processImportReference(
068                @NotNull JetImportDirective importDirective,
069                @NotNull ModuleDescriptor moduleDescriptor,
070                @NotNull BindingTrace trace,
071                @NotNull LookupMode lookupMode
072        ) {
073            return importDirectiveProcessor.processImportReference(importDirective, moduleDescriptor, trace, lookupMode);
074        }
075    
076        @NotNull
077        public Collection<DeclarationDescriptor> lookupDescriptorsForUserType(
078                @NotNull JetUserType userType,
079                @NotNull JetScope outerScope,
080                @NotNull BindingTrace trace,
081                boolean onlyClassifiers
082        ) {
083    
084            if (userType.isAbsoluteInRootPackage()) {
085                trace.report(Errors.UNSUPPORTED.on(userType, "package"));
086                return Collections.emptyList();
087            }
088    
089            JetSimpleNameExpression referenceExpression = userType.getReferenceExpression();
090            /*
091            if (referenceExpression == null) {
092                return Collections.emptyList();
093            }
094            */
095            JetUserType qualifier = userType.getQualifier();
096    
097            // We do not want to resolve the last segment of a user type to a package
098            JetScope filteredScope = filterOutPackagesIfNeeded(outerScope, onlyClassifiers);
099    
100            DeclarationDescriptor shouldBeVisibleFrom = outerScope.getContainingDeclaration();
101            if (qualifier == null) {
102                if (referenceExpression == null) return Collections.emptyList();
103                return lookupDescriptorsForSimpleNameReference(referenceExpression, filteredScope, shouldBeVisibleFrom, trace,
104                                                               LookupMode.ONLY_CLASSES_AND_PACKAGES,
105                                                               false, true);
106            }
107            Collection<DeclarationDescriptor> declarationDescriptors = lookupDescriptorsForUserType(qualifier, outerScope, trace, false);
108            if (referenceExpression == null) return Collections.emptyList();
109            return lookupSelectorDescriptors(referenceExpression, declarationDescriptors, trace, shouldBeVisibleFrom,
110                                             LookupMode.ONLY_CLASSES_AND_PACKAGES, true);
111        }
112    
113        private static JetScope filterOutPackagesIfNeeded(final JetScope outerScope, boolean noPackages) {
114            return !noPackages ? outerScope : new AbstractScopeAdapter() {
115    
116                @NotNull
117                @Override
118                protected JetScope getWorkerScope() {
119                    return outerScope;
120                }
121    
122                @Nullable
123                @Override
124                public PackageViewDescriptor getPackage(@NotNull Name name) {
125                    return null;
126                }
127            };
128        }
129    
130        @NotNull
131        public Collection<DeclarationDescriptor> lookupSelectorDescriptors(
132                @NotNull JetSimpleNameExpression selector,
133                @NotNull Collection<DeclarationDescriptor> declarationDescriptors,
134                @NotNull BindingTrace trace,
135                @NotNull DeclarationDescriptor shouldBeVisibleFrom,
136                @NotNull LookupMode lookupMode,
137                boolean storeResult
138        ) {
139            Set<LookupResult> results = Sets.newLinkedHashSet();
140            for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) {
141                if (declarationDescriptor instanceof PackageViewDescriptor) {
142                    results.add(lookupSimpleNameReference(selector, ((PackageViewDescriptor) declarationDescriptor).getMemberScope(),
143                                                          lookupMode, true));
144                }
145                if (declarationDescriptor instanceof ClassDescriptor) {
146                    addResultsForClass(results, selector, lookupMode, (ClassDescriptor) declarationDescriptor);
147                }
148            }
149            return filterAndStoreResolutionResult(results, selector, trace, shouldBeVisibleFrom, lookupMode, storeResult);
150        }
151    
152        private static void addResultsForClass(
153                @NotNull @Mutable Set<LookupResult> results,
154                @NotNull JetSimpleNameExpression selector,
155                @NotNull LookupMode lookupMode,
156                @NotNull ClassDescriptor descriptor
157        ) {
158            JetScope scope = lookupMode == LookupMode.ONLY_CLASSES_AND_PACKAGES
159                             ? descriptor.getUnsubstitutedInnerClassesScope()
160                             : descriptor.getDefaultType().getMemberScope();
161            results.add(lookupSimpleNameReference(selector, scope, lookupMode, false));
162    
163            results.add(lookupSimpleNameReference(selector, descriptor.getStaticScope(), lookupMode, true));
164        }
165    
166    
167        @NotNull
168        @SuppressWarnings("MethodMayBeStatic")
169        public Collection<DeclarationDescriptor> lookupDescriptorsForSimpleNameReference(
170                @NotNull JetSimpleNameExpression referenceExpression,
171                @NotNull JetScope outerScope,
172                @NotNull DeclarationDescriptor shouldBeVisibleFrom,
173                @NotNull BindingTrace trace,
174                @NotNull LookupMode lookupMode,
175                boolean packageLevel,
176                boolean storeResult
177        ) {
178            LookupResult lookupResult = lookupSimpleNameReference(referenceExpression, outerScope, lookupMode, packageLevel);
179            return filterAndStoreResolutionResult(Collections.singletonList(lookupResult), referenceExpression, trace, shouldBeVisibleFrom,
180                                                  lookupMode, storeResult);
181        }
182    
183        @NotNull
184        private static LookupResult lookupSimpleNameReference(
185                @NotNull JetSimpleNameExpression referenceExpression,
186                @NotNull JetScope outerScope,
187                @NotNull LookupMode lookupMode,
188                boolean packageLevel
189        ) {
190            Name referencedName = referenceExpression.getReferencedNameAsName();
191    
192            Collection<DeclarationDescriptor> descriptors = Sets.newLinkedHashSet();
193            PackageViewDescriptor packageDescriptor = outerScope.getPackage(referencedName);
194            if (packageDescriptor != null) {
195                descriptors.add(packageDescriptor);
196            }
197    
198            KotlinLookupLocation location = new KotlinLookupLocation(referenceExpression);
199    
200            ClassifierDescriptor classifierDescriptor = outerScope.getClassifier(referencedName, location);
201            if (classifierDescriptor != null) {
202                descriptors.add(classifierDescriptor);
203            }
204    
205            if (lookupMode == LookupMode.EVERYTHING) {
206                descriptors.addAll(outerScope.getFunctions(referencedName, location));
207                descriptors.addAll(outerScope.getProperties(referencedName, location));
208    
209                VariableDescriptor localVariable = outerScope.getLocalVariable(referencedName);
210                if (localVariable != null) {
211                    descriptors.add(localVariable);
212                }
213            }
214    
215            return new LookupResult(descriptors, outerScope, packageLevel);
216        }
217    
218        @NotNull
219        private Collection<DeclarationDescriptor> filterAndStoreResolutionResult(
220                @NotNull Collection<LookupResult> lookupResults,
221                @NotNull JetSimpleNameExpression referenceExpression,
222                @NotNull BindingTrace trace,
223                @NotNull DeclarationDescriptor shouldBeVisibleFrom,
224                @NotNull LookupMode lookupMode,
225                boolean storeResult
226        ) {
227            if (lookupResults.isEmpty()) {
228                return Collections.emptyList();
229            }
230    
231            Collection<DeclarationDescriptor> descriptors = Sets.newLinkedHashSet();
232            for (LookupResult lookupResult : lookupResults) {
233                descriptors.addAll(lookupResult.descriptors);
234            }
235    
236            Collection<DeclarationDescriptor> filteredDescriptors;
237            if (lookupMode == LookupMode.ONLY_CLASSES_AND_PACKAGES) {
238                filteredDescriptors = Collections2.filter(descriptors, CLASSIFIERS_AND_PACKAGE_VIEWS);
239            }
240            else {
241                filteredDescriptors = Sets.newLinkedHashSet();
242                //functions and properties can be imported if lookupResult.packageLevel == true
243                for (LookupResult lookupResult : lookupResults) {
244                    if (lookupResult.packageLevel) {
245                        filteredDescriptors.addAll(lookupResult.descriptors);
246                    }
247                    else {
248                        filteredDescriptors.addAll(Collections2.filter(lookupResult.descriptors, CLASSIFIERS_AND_PACKAGE_VIEWS));
249                    }
250                }
251            }
252    
253            if (storeResult) {
254                Collection<JetScope> possibleResolutionScopes = Lists.newArrayList();
255                for (LookupResult lookupResult : lookupResults) {
256                    if (!lookupResult.descriptors.isEmpty()) {
257                        possibleResolutionScopes.add(lookupResult.resolutionScope);
258                    }
259                }
260                if (possibleResolutionScopes.isEmpty()) {
261                    for (LookupResult lookupResult : lookupResults) {
262                        possibleResolutionScopes.add(lookupResult.resolutionScope);
263                    }
264                }
265    
266                storeResolutionResult(descriptors, filteredDescriptors, referenceExpression, possibleResolutionScopes, trace,
267                                      shouldBeVisibleFrom);
268            }
269    
270            return filteredDescriptors;
271        }
272    
273        private void storeResolutionResult(
274                @NotNull Collection<DeclarationDescriptor> descriptors,
275                @NotNull Collection<DeclarationDescriptor> canBeImportedDescriptors,
276                @NotNull JetSimpleNameExpression referenceExpression,
277                @NotNull Collection<JetScope> possibleResolutionScopes,
278                @NotNull BindingTrace trace,
279                @NotNull DeclarationDescriptor shouldBeVisibleFrom
280        ) {
281            assert canBeImportedDescriptors.size() <= descriptors.size();
282            assert !possibleResolutionScopes.isEmpty();
283            //todo completion here needs all possible resolution scopes, if there are many
284            JetScope resolutionScope = possibleResolutionScopes.iterator().next();
285    
286            // A special case - will fill all trace information
287            if (resolveClassPackageAmbiguity(canBeImportedDescriptors, referenceExpression, resolutionScope, trace, shouldBeVisibleFrom)) {
288                return;
289            }
290    
291            // Simple case of no descriptors
292            if (descriptors.isEmpty()) {
293                trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
294                trace.report(UNRESOLVED_REFERENCE.on(referenceExpression, referenceExpression));
295                return;
296            }
297    
298            // Decide if expression has resolved reference
299            DeclarationDescriptor descriptor = null;
300            if (descriptors.size() == 1) {
301                descriptor = descriptors.iterator().next();
302                assert canBeImportedDescriptors.size() <= 1;
303            }
304            else if (canBeImportedDescriptors.size() == 1) {
305                descriptor = canBeImportedDescriptors.iterator().next();
306            }
307            if (descriptor != null) {
308                trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, descriptors.iterator().next());
309                trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
310    
311                if (descriptor instanceof ClassifierDescriptor) {
312                    symbolUsageValidator.validateTypeUsage((ClassifierDescriptor) descriptor, trace, referenceExpression);
313                }
314    
315                if (descriptor instanceof DeclarationDescriptorWithVisibility) {
316                    checkVisibility((DeclarationDescriptorWithVisibility) descriptor, trace, referenceExpression, shouldBeVisibleFrom);
317                }
318            }
319    
320            // Check for more information and additional errors
321            if (canBeImportedDescriptors.isEmpty()) {
322                assert descriptors.size() >= 1;
323                trace.report(CANNOT_BE_IMPORTED.on(referenceExpression, descriptors.iterator().next()));
324                return;
325            }
326            if (canBeImportedDescriptors.size() > 1) {
327                trace.record(BindingContext.AMBIGUOUS_REFERENCE_TARGET, referenceExpression, descriptors);
328            }
329        }
330    
331        /**
332         * This method tries to resolve descriptors ambiguity between class descriptor and package descriptor for the same class.
333         * It's ok choose class for expression reference resolution.
334         *
335         * @return <code>true</code> if method has successfully resolved ambiguity
336         */
337        private static boolean resolveClassPackageAmbiguity(
338                @NotNull Collection<DeclarationDescriptor> filteredDescriptors,
339                @NotNull JetSimpleNameExpression referenceExpression,
340                @NotNull JetScope resolutionScope,
341                @NotNull BindingTrace trace,
342                @NotNull DeclarationDescriptor shouldBeVisibleFrom
343        ) {
344            if (filteredDescriptors.size() == 2) {
345                PackageViewDescriptor packageView = null;
346                ClassDescriptor classDescriptor = null;
347    
348                for (DeclarationDescriptor filteredDescriptor : filteredDescriptors) {
349                    if (filteredDescriptor instanceof PackageViewDescriptor) {
350                        packageView = (PackageViewDescriptor) filteredDescriptor;
351                    }
352                    else if (filteredDescriptor instanceof ClassDescriptor) {
353                        classDescriptor = (ClassDescriptor) filteredDescriptor;
354                    }
355                }
356    
357                if (packageView != null && classDescriptor != null) {
358                    if (packageView.getFqName().equalsTo(DescriptorUtils.getFqName(classDescriptor))) {
359                        trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, classDescriptor);
360                        trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
361                        checkVisibility(classDescriptor, trace, referenceExpression, shouldBeVisibleFrom);
362                        return true;
363                    }
364                }
365            }
366    
367            return false;
368        }
369    
370        private static void checkVisibility(
371                @NotNull DeclarationDescriptorWithVisibility descriptor,
372                @NotNull BindingTrace trace,
373                @NotNull JetSimpleNameExpression referenceExpression,
374                @NotNull DeclarationDescriptor shouldBeVisibleFrom
375        ) {
376            if (!Visibilities.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, descriptor, shouldBeVisibleFrom)) {
377                Visibility visibility = descriptor.getVisibility();
378                if (PsiTreeUtil.getParentOfType(referenceExpression, JetImportDirective.class) != null && !visibility.mustCheckInImports()) {
379                    return;
380                }
381                //noinspection ConstantConditions
382                trace.report(INVISIBLE_REFERENCE.on(referenceExpression, descriptor, visibility, descriptor.getContainingDeclaration()));
383            }
384        }
385    
386        private static class LookupResult {
387            private final Collection<DeclarationDescriptor> descriptors;
388            private final JetScope resolutionScope;
389            private final boolean packageLevel;
390    
391            public LookupResult(
392                    @NotNull Collection<DeclarationDescriptor> descriptors,
393                    @NotNull JetScope resolutionScope,
394                    boolean packageLevel
395            ) {
396                this.descriptors = descriptors;
397                this.resolutionScope = resolutionScope;
398                this.packageLevel = packageLevel;
399            }
400        }
401    }