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.Mutable;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.jet.lang.descriptors.*;
027    import org.jetbrains.jet.lang.diagnostics.Errors;
028    import org.jetbrains.jet.lang.psi.*;
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<DeclarationDescriptor> processImportReference(
056                @NotNull JetImportDirective importDirective,
057                @NotNull JetScope scope,
058                @NotNull JetScope scopeToCheckVisibility,
059                @NotNull Importer importer,
060                @NotNull BindingTrace trace,
061                @NotNull ModuleDescriptor module,
062                @NotNull LookupMode lookupMode
063        ) {
064            if (importDirective.isAbsoluteInRootPackage()) {
065                trace.report(UNSUPPORTED.on(importDirective, "TypeHierarchyResolver")); // TODO
066                return Collections.emptyList();
067            }
068            JetExpression importedReference = importDirective.getImportedReference();
069            if (importedReference == null) {
070                return Collections.emptyList();
071            }
072    
073            Collection<DeclarationDescriptor> descriptors;
074            if (importedReference instanceof JetQualifiedExpression) {
075                //store result only when we find all descriptors, not only classes on the second phase
076                descriptors = lookupDescriptorsForQualifiedExpression(
077                        (JetQualifiedExpression) importedReference, scope, scopeToCheckVisibility, trace,
078                        lookupMode, lookupMode == LookupMode.EVERYTHING);
079            }
080            else {
081                assert importedReference instanceof JetSimpleNameExpression;
082                descriptors = lookupDescriptorsForSimpleNameReference(
083                        (JetSimpleNameExpression) importedReference, scope, scopeToCheckVisibility, trace,
084                        lookupMode, true, lookupMode == LookupMode.EVERYTHING);
085            }
086    
087            JetSimpleNameExpression referenceExpression = JetPsiUtil.getLastReference(importedReference);
088            if (importDirective.isAllUnder()) {
089                if (referenceExpression == null || !canImportMembersFrom(descriptors, referenceExpression, trace, lookupMode)) {
090                    return Collections.emptyList();
091                }
092    
093                for (DeclarationDescriptor descriptor : descriptors) {
094                    importer.addAllUnderImport(descriptor, module.getPlatformToKotlinClassMap());
095                }
096                return Collections.emptyList();
097            }
098    
099            Name aliasName = JetPsiUtil.getAliasName(importDirective);
100            if (aliasName == null) {
101                return Collections.emptyList();
102            }
103    
104            for (DeclarationDescriptor descriptor : descriptors) {
105                importer.addAliasImport(descriptor, aliasName);
106            }
107    
108            return descriptors;
109        }
110    
111        private static boolean canImportMembersFrom(
112                @NotNull Collection<DeclarationDescriptor> descriptors,
113                @NotNull JetSimpleNameExpression reference,
114                @NotNull BindingTrace trace,
115                @NotNull LookupMode lookupMode
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    
125            TemporaryBindingTrace temporaryTrace =
126                    TemporaryBindingTrace.create(trace, "trace to find out if members can be imported from", reference);
127            boolean canImport = false;
128            for (DeclarationDescriptor descriptor : descriptors) {
129                canImport |= canImportMembersFrom(descriptor, reference, temporaryTrace, lookupMode);
130            }
131            if (!canImport) {
132                temporaryTrace.commit();
133            }
134            return canImport;
135        }
136    
137        private static boolean canImportMembersFrom(
138                @NotNull DeclarationDescriptor descriptor,
139                @NotNull JetSimpleNameExpression reference,
140                @NotNull BindingTrace trace,
141                @NotNull LookupMode lookupMode
142        ) {
143            assert lookupMode == LookupMode.EVERYTHING;
144            if (descriptor instanceof PackageViewDescriptor) {
145                return true;
146            }
147            if (descriptor instanceof ClassDescriptor && !((ClassDescriptor) descriptor).getKind().isSingleton()) {
148                return true;
149            }
150            trace.report(CANNOT_IMPORT_FROM_ELEMENT.on(reference, descriptor));
151            return false;
152        }
153    
154        @NotNull
155        public Collection<DeclarationDescriptor> lookupDescriptorsForUserType(
156                @NotNull JetUserType userType,
157                @NotNull JetScope outerScope,
158                @NotNull BindingTrace trace
159        ) {
160            if (userType.isAbsoluteInRootPackage()) {
161                trace.report(Errors.UNSUPPORTED.on(userType, "package"));
162                return Collections.emptyList();
163            }
164    
165            JetSimpleNameExpression referenceExpression = userType.getReferenceExpression();
166            if (referenceExpression == null) {
167                return Collections.emptyList();
168            }
169            JetUserType qualifier = userType.getQualifier();
170            if (qualifier == null) {
171                return lookupDescriptorsForSimpleNameReference(referenceExpression, outerScope, outerScope, trace, LookupMode.ONLY_CLASSES,
172                                                               false, true);
173            }
174            Collection<DeclarationDescriptor> declarationDescriptors = lookupDescriptorsForUserType(qualifier, outerScope, trace);
175            return lookupSelectorDescriptors(referenceExpression, declarationDescriptors, trace, outerScope, LookupMode.ONLY_CLASSES, true);
176        }
177    
178        @NotNull
179        public Collection<DeclarationDescriptor> lookupDescriptorsForQualifiedExpression(
180                @NotNull JetQualifiedExpression importedReference,
181                @NotNull JetScope outerScope,
182                @NotNull JetScope scopeToCheckVisibility,
183                @NotNull BindingTrace trace,
184                @NotNull LookupMode lookupMode,
185                boolean storeResult
186        ) {
187            JetExpression receiverExpression = importedReference.getReceiverExpression();
188            Collection<DeclarationDescriptor> declarationDescriptors;
189            if (receiverExpression instanceof JetQualifiedExpression) {
190                declarationDescriptors =
191                        lookupDescriptorsForQualifiedExpression((JetQualifiedExpression) receiverExpression, outerScope, scopeToCheckVisibility,
192                                                                trace, lookupMode, storeResult);
193            }
194            else {
195                assert receiverExpression instanceof JetSimpleNameExpression;
196                declarationDescriptors =
197                        lookupDescriptorsForSimpleNameReference((JetSimpleNameExpression) receiverExpression, outerScope,
198                                                                scopeToCheckVisibility, trace, lookupMode, true, storeResult);
199            }
200    
201            JetExpression selectorExpression = importedReference.getSelectorExpression();
202            if (!(selectorExpression instanceof JetSimpleNameExpression)) {
203                return Collections.emptyList();
204            }
205    
206            JetSimpleNameExpression selector = (JetSimpleNameExpression) selectorExpression;
207            JetSimpleNameExpression lastReference = JetPsiUtil.getLastReference(receiverExpression);
208            if (lastReference == null || !canImportMembersFrom(declarationDescriptors, lastReference, trace, lookupMode)) {
209                return Collections.emptyList();
210            }
211    
212            return lookupSelectorDescriptors(selector, declarationDescriptors, trace, scopeToCheckVisibility, lookupMode, storeResult);
213        }
214    
215        @NotNull
216        private static Collection<DeclarationDescriptor> lookupSelectorDescriptors(
217                @NotNull JetSimpleNameExpression selector,
218                @NotNull Collection<DeclarationDescriptor> declarationDescriptors,
219                @NotNull BindingTrace trace,
220                @NotNull JetScope scopeToCheckVisibility,
221                @NotNull LookupMode lookupMode,
222                boolean storeResult
223        ) {
224            Set<LookupResult> results = Sets.newLinkedHashSet();
225            for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) {
226                if (declarationDescriptor instanceof PackageViewDescriptor) {
227                    results.add(lookupSimpleNameReference(selector, ((PackageViewDescriptor) declarationDescriptor).getMemberScope(),
228                                                          lookupMode, true));
229                }
230                if (declarationDescriptor instanceof ClassDescriptor) {
231                    addResultsForClass(results, selector, lookupMode, (ClassDescriptor) declarationDescriptor);
232                }
233            }
234            return filterAndStoreResolutionResult(results, selector, trace, scopeToCheckVisibility, lookupMode, storeResult);
235        }
236    
237        private static void addResultsForClass(
238                @NotNull @Mutable Set<LookupResult> results,
239                @NotNull JetSimpleNameExpression selector,
240                @NotNull LookupMode lookupMode,
241                @NotNull ClassDescriptor descriptor
242        ) {
243            JetScope scope = lookupMode == LookupMode.ONLY_CLASSES
244                             ? descriptor.getUnsubstitutedInnerClassesScope()
245                             : descriptor.getDefaultType().getMemberScope();
246            results.add(lookupSimpleNameReference(selector, scope, lookupMode, false));
247    
248            results.add(lookupSimpleNameReference(selector, descriptor.getStaticScope(), lookupMode, true));
249    
250            ClassDescriptor classObject = descriptor.getClassObjectDescriptor();
251            if (classObject != null) {
252                addResultsForClass(results, selector, lookupMode, classObject);
253            }
254        }
255    
256    
257        @NotNull
258        @SuppressWarnings("MethodMayBeStatic")
259        public Collection<DeclarationDescriptor> lookupDescriptorsForSimpleNameReference(
260                @NotNull JetSimpleNameExpression referenceExpression,
261                @NotNull JetScope outerScope,
262                @NotNull JetScope scopeToCheckVisibility,
263                @NotNull BindingTrace trace,
264                @NotNull LookupMode lookupMode,
265                boolean packageLevel,
266                boolean storeResult
267        ) {
268            LookupResult lookupResult = lookupSimpleNameReference(referenceExpression, outerScope, lookupMode, packageLevel);
269            return filterAndStoreResolutionResult(Collections.singletonList(lookupResult), referenceExpression, trace, scopeToCheckVisibility,
270                                                  lookupMode, storeResult);
271        }
272    
273        @NotNull
274        private static LookupResult lookupSimpleNameReference(
275                @NotNull JetSimpleNameExpression referenceExpression,
276                @NotNull JetScope outerScope,
277                @NotNull LookupMode lookupMode,
278                boolean packageLevel
279        ) {
280            Name referencedName = referenceExpression.getReferencedNameAsName();
281    
282            Collection<DeclarationDescriptor> descriptors = Sets.newLinkedHashSet();
283            PackageViewDescriptor packageDescriptor = outerScope.getPackage(referencedName);
284            if (packageDescriptor != null) {
285                descriptors.add(packageDescriptor);
286            }
287    
288            ClassifierDescriptor classifierDescriptor = outerScope.getClassifier(referencedName);
289            if (classifierDescriptor != null) {
290                descriptors.add(classifierDescriptor);
291            }
292    
293            if (lookupMode == LookupMode.EVERYTHING) {
294                descriptors.addAll(outerScope.getFunctions(referencedName));
295                descriptors.addAll(outerScope.getProperties(referencedName));
296    
297                VariableDescriptor localVariable = outerScope.getLocalVariable(referencedName);
298                if (localVariable != null) {
299                    descriptors.add(localVariable);
300                }
301            }
302    
303            return new LookupResult(descriptors, outerScope, packageLevel);
304        }
305    
306        @NotNull
307        private static Collection<DeclarationDescriptor> filterAndStoreResolutionResult(
308                @NotNull Collection<LookupResult> lookupResults,
309                @NotNull JetSimpleNameExpression referenceExpression,
310                @NotNull BindingTrace trace,
311                @NotNull JetScope scopeToCheckVisibility,
312                @NotNull LookupMode lookupMode,
313                boolean storeResult
314        ) {
315            if (lookupResults.isEmpty()) {
316                return Collections.emptyList();
317            }
318    
319            Collection<DeclarationDescriptor> descriptors = Sets.newLinkedHashSet();
320            for (LookupResult lookupResult : lookupResults) {
321                descriptors.addAll(lookupResult.descriptors);
322            }
323    
324            Collection<JetScope> possibleResolutionScopes = Lists.newArrayList();
325            for (LookupResult lookupResult : lookupResults) {
326                if (!lookupResult.descriptors.isEmpty()) {
327                    possibleResolutionScopes.add(lookupResult.resolutionScope);
328                }
329            }
330            if (possibleResolutionScopes.isEmpty()) {
331                for (LookupResult lookupResult : lookupResults) {
332                    possibleResolutionScopes.add(lookupResult.resolutionScope);
333                }
334            }
335    
336            Collection<DeclarationDescriptor> filteredDescriptors;
337            if (lookupMode == LookupMode.ONLY_CLASSES) {
338                filteredDescriptors = Collections2.filter(descriptors, CLASSIFIERS_AND_PACKAGE_VIEWS);
339            }
340            else {
341                filteredDescriptors = Sets.newLinkedHashSet();
342                //functions and properties can be imported if lookupResult.packageLevel == true
343                for (LookupResult lookupResult : lookupResults) {
344                    if (lookupResult.packageLevel) {
345                        filteredDescriptors.addAll(lookupResult.descriptors);
346                    }
347                    else {
348                        filteredDescriptors.addAll(Collections2.filter(lookupResult.descriptors, CLASSIFIERS_AND_PACKAGE_VIEWS));
349                    }
350                }
351            }
352    
353            if (storeResult) {
354                storeResolutionResult(descriptors, filteredDescriptors, referenceExpression, possibleResolutionScopes, trace,
355                                      scopeToCheckVisibility);
356            }
357    
358            return filteredDescriptors;
359        }
360    
361        private static void storeResolutionResult(
362                @NotNull Collection<DeclarationDescriptor> descriptors,
363                @NotNull Collection<DeclarationDescriptor> canBeImportedDescriptors,
364                @NotNull JetSimpleNameExpression referenceExpression,
365                @NotNull Collection<JetScope> possibleResolutionScopes,
366                @NotNull BindingTrace trace,
367                @NotNull JetScope scopeToCheckVisibility
368        ) {
369            assert canBeImportedDescriptors.size() <= descriptors.size();
370            assert !possibleResolutionScopes.isEmpty();
371            //todo completion here needs all possible resolution scopes, if there are many
372            JetScope resolutionScope = possibleResolutionScopes.iterator().next();
373    
374            // A special case - will fill all trace information
375            if (resolveClassPackageAmbiguity(canBeImportedDescriptors, referenceExpression, resolutionScope, trace, scopeToCheckVisibility)) {
376                return;
377            }
378    
379            // Simple case of no descriptors
380            if (descriptors.isEmpty()) {
381                trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
382                trace.report(UNRESOLVED_REFERENCE.on(referenceExpression, referenceExpression));
383                return;
384            }
385    
386            // Decide if expression has resolved reference
387            DeclarationDescriptor descriptor = null;
388            if (descriptors.size() == 1) {
389                descriptor = descriptors.iterator().next();
390                assert canBeImportedDescriptors.size() <= 1;
391            }
392            else if (canBeImportedDescriptors.size() == 1) {
393                descriptor = canBeImportedDescriptors.iterator().next();
394            }
395            if (descriptor != null) {
396                trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, descriptors.iterator().next());
397                trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
398    
399                if (descriptor instanceof DeclarationDescriptorWithVisibility) {
400                    checkVisibility((DeclarationDescriptorWithVisibility) descriptor, trace, referenceExpression, scopeToCheckVisibility);
401                }
402            }
403    
404            // Check for more information and additional errors
405            if (canBeImportedDescriptors.isEmpty()) {
406                assert descriptors.size() >= 1;
407                trace.report(CANNOT_BE_IMPORTED.on(referenceExpression, descriptors.iterator().next()));
408                return;
409            }
410            if (canBeImportedDescriptors.size() > 1) {
411                trace.record(BindingContext.AMBIGUOUS_REFERENCE_TARGET, referenceExpression, descriptors);
412            }
413        }
414    
415        /**
416         * This method tries to resolve descriptors ambiguity between class descriptor and package descriptor for the same class.
417         * It's ok choose class for expression reference resolution.
418         *
419         * @return <code>true</code> if method has successfully resolved ambiguity
420         */
421        private static boolean resolveClassPackageAmbiguity(
422                @NotNull Collection<DeclarationDescriptor> filteredDescriptors,
423                @NotNull JetSimpleNameExpression referenceExpression,
424                @NotNull JetScope resolutionScope,
425                @NotNull BindingTrace trace,
426                @NotNull JetScope scopeToCheckVisibility
427        ) {
428            if (filteredDescriptors.size() == 2) {
429                PackageViewDescriptor packageView = null;
430                ClassDescriptor classDescriptor = null;
431    
432                for (DeclarationDescriptor filteredDescriptor : filteredDescriptors) {
433                    if (filteredDescriptor instanceof PackageViewDescriptor) {
434                        packageView = (PackageViewDescriptor) filteredDescriptor;
435                    }
436                    else if (filteredDescriptor instanceof ClassDescriptor) {
437                        classDescriptor = (ClassDescriptor) filteredDescriptor;
438                    }
439                }
440    
441                if (packageView != null && classDescriptor != null) {
442                    if (packageView.getFqName().equalsTo(DescriptorUtils.getFqName(classDescriptor))) {
443                        trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, classDescriptor);
444                        trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
445                        checkVisibility(classDescriptor, trace, referenceExpression, scopeToCheckVisibility);
446                        return true;
447                    }
448                }
449            }
450    
451            return false;
452        }
453    
454        private static void checkVisibility(
455                @NotNull DeclarationDescriptorWithVisibility descriptor,
456                @NotNull BindingTrace trace,
457                @NotNull JetSimpleNameExpression referenceExpression,
458                @NotNull JetScope scopeToCheckVisibility
459        ) {
460            if (!Visibilities.isVisible(descriptor, scopeToCheckVisibility.getContainingDeclaration())) {
461                //noinspection ConstantConditions
462                trace.report(INVISIBLE_REFERENCE.on(referenceExpression, descriptor, descriptor.getVisibility(),
463                                                    descriptor.getContainingDeclaration()));
464            }
465        }
466    
467        private static class LookupResult {
468            private final Collection<DeclarationDescriptor> descriptors;
469            private final JetScope resolutionScope;
470            private final boolean packageLevel;
471    
472            public LookupResult(
473                    @NotNull Collection<DeclarationDescriptor> descriptors,
474                    @NotNull JetScope resolutionScope,
475                    boolean packageLevel
476            ) {
477                this.descriptors = descriptors;
478                this.resolutionScope = resolutionScope;
479                this.packageLevel = packageLevel;
480            }
481        }
482    }