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