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