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