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                importer.addAliasImport(descriptor, aliasName);
108            }
109    
110            return descriptors;
111        }
112    
113        private boolean canImportMembersFrom(@NotNull Collection<? extends DeclarationDescriptor> descriptors,
114                @NotNull JetSimpleNameExpression reference, @NotNull BindingTrace trace, @NotNull LookupMode lookupMode
115        ) {
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            TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(trace, "trace to find out if members can be imported from", reference);
125            boolean canImport = false;
126            for (DeclarationDescriptor descriptor : descriptors) {
127                canImport |= canImportMembersFrom(descriptor, reference, temporaryTrace, lookupMode);
128            }
129            if (!canImport) {
130                temporaryTrace.commit();
131            }
132            return canImport;
133        }
134    
135        private boolean canImportMembersFrom(@NotNull DeclarationDescriptor descriptor,
136                @NotNull JetSimpleNameExpression reference, @NotNull BindingTrace trace, @NotNull LookupMode lookupMode
137        ) {
138    
139            assert lookupMode == LookupMode.EVERYTHING;
140            if (descriptor instanceof NamespaceDescriptor) {
141                return true;
142            }
143            if (descriptor instanceof ClassDescriptor && !((ClassDescriptor)descriptor).getKind().isObject()) {
144                return true;
145            }
146            trace.report(CANNOT_IMPORT_FROM_ELEMENT.on(reference, descriptor));
147            return false;
148        }
149    
150        @NotNull
151        public Collection<? extends DeclarationDescriptor> lookupDescriptorsForUserType(@NotNull JetUserType userType,
152                @NotNull JetScope outerScope, @NotNull BindingTrace trace) {
153    
154            if (userType.isAbsoluteInRootNamespace()) {
155                trace.report(Errors.UNSUPPORTED.on(userType, "package"));
156                return Collections.emptyList();
157            }
158            JetSimpleNameExpression referenceExpression = userType.getReferenceExpression();
159            if (referenceExpression == null) {
160                return Collections.emptyList();
161            }
162            JetUserType qualifier = userType.getQualifier();
163            if (qualifier == null) {
164                return lookupDescriptorsForSimpleNameReference(referenceExpression, outerScope, outerScope, trace, LookupMode.ONLY_CLASSES, false, true);
165            }
166            Collection<? extends DeclarationDescriptor> declarationDescriptors = lookupDescriptorsForUserType(qualifier, outerScope, trace);
167            return lookupSelectorDescriptors(referenceExpression, declarationDescriptors, trace, outerScope, LookupMode.ONLY_CLASSES, true);
168        }
169    
170        @NotNull
171        public Collection<? extends DeclarationDescriptor> lookupDescriptorsForQualifiedExpression(@NotNull JetQualifiedExpression importedReference,
172                @NotNull JetScope outerScope, @NotNull JetScope scopeToCheckVisibility, @NotNull BindingTrace trace, @NotNull LookupMode lookupMode, boolean storeResult) {
173    
174            JetExpression receiverExpression = importedReference.getReceiverExpression();
175            Collection<? extends DeclarationDescriptor> declarationDescriptors;
176            if (receiverExpression instanceof JetQualifiedExpression) {
177                declarationDescriptors = lookupDescriptorsForQualifiedExpression((JetQualifiedExpression)receiverExpression, outerScope, scopeToCheckVisibility, trace,
178                                                                                 lookupMode, storeResult);
179            }
180            else {
181                assert receiverExpression instanceof JetSimpleNameExpression;
182                declarationDescriptors = lookupDescriptorsForSimpleNameReference((JetSimpleNameExpression)receiverExpression, outerScope, scopeToCheckVisibility, trace,
183                                                                                 lookupMode, true, storeResult);
184            }
185    
186            JetExpression selectorExpression = importedReference.getSelectorExpression();
187            if (!(selectorExpression instanceof JetSimpleNameExpression)) {
188                return Collections.emptyList();
189            }
190    
191            JetSimpleNameExpression selector = (JetSimpleNameExpression)selectorExpression;
192            JetSimpleNameExpression lastReference = JetPsiUtil.getLastReference(receiverExpression);
193            if (lastReference == null || !canImportMembersFrom(declarationDescriptors, lastReference, trace, lookupMode)) {
194                return Collections.emptyList();
195            }
196    
197            return lookupSelectorDescriptors(selector, declarationDescriptors, trace, scopeToCheckVisibility, lookupMode, storeResult);
198        }
199    
200        @NotNull
201        private Collection<? extends DeclarationDescriptor> lookupSelectorDescriptors(@NotNull JetSimpleNameExpression selector,
202                @NotNull Collection<? extends DeclarationDescriptor> declarationDescriptors, @NotNull BindingTrace trace,
203                @NotNull JetScope scopeToCheckVisibility, @NotNull LookupMode lookupMode, boolean storeResult) {
204    
205            Set<SuccessfulLookupResult> results = Sets.newHashSet();
206            for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) {
207                if (declarationDescriptor instanceof NamespaceDescriptor) {
208                    addResult(results, lookupSimpleNameReference(selector, ((NamespaceDescriptor)declarationDescriptor).getMemberScope(),
209                                                                 lookupMode, true));
210                }
211                if (declarationDescriptor instanceof ClassDescriptor) {
212                    addResult(results, lookupSimpleNameReference(selector, getAppropriateScope((ClassDescriptor)declarationDescriptor,
213                                                                                               lookupMode), lookupMode, false));
214                    ClassDescriptor classObjectDescriptor = ((ClassDescriptor)declarationDescriptor).getClassObjectDescriptor();
215                    if (classObjectDescriptor != null) {
216                        addResult(results, lookupSimpleNameReference(selector, getAppropriateScope(classObjectDescriptor, lookupMode),
217                                                                     lookupMode, false));
218                    }
219                }
220            }
221            return filterAndStoreResolutionResult(results, selector, trace, scopeToCheckVisibility, lookupMode, storeResult);
222        }
223    
224        @NotNull
225        private JetScope getAppropriateScope(@NotNull ClassDescriptor classDescriptor, @NotNull LookupMode lookupMode) {
226            return lookupMode == LookupMode.ONLY_CLASSES ? classDescriptor.getUnsubstitutedInnerClassesScope() : classDescriptor.getDefaultType().getMemberScope();
227        }
228    
229        private void addResult(@NotNull Set<SuccessfulLookupResult> results, @NotNull LookupResult result) {
230            if (result == LookupResult.EMPTY) return;
231            results.add((SuccessfulLookupResult)result);
232        }
233    
234    
235        @NotNull
236        public Collection<? extends DeclarationDescriptor> lookupDescriptorsForSimpleNameReference(@NotNull JetSimpleNameExpression referenceExpression,
237                @NotNull JetScope outerScope, @NotNull JetScope scopeToCheckVisibility, @NotNull BindingTrace trace, @NotNull LookupMode lookupMode, boolean namespaceLevel, boolean storeResult) {
238    
239            LookupResult lookupResult = lookupSimpleNameReference(referenceExpression, outerScope, lookupMode, namespaceLevel);
240            if (lookupResult == LookupResult.EMPTY) return Collections.emptyList();
241            return filterAndStoreResolutionResult(Collections.singletonList((SuccessfulLookupResult)lookupResult), referenceExpression, trace, scopeToCheckVisibility,
242                                                  lookupMode, storeResult);
243        }
244    
245        @NotNull
246        private LookupResult lookupSimpleNameReference(@NotNull JetSimpleNameExpression referenceExpression,
247                @NotNull JetScope outerScope, @NotNull LookupMode lookupMode, boolean namespaceLevel) {
248    
249            Name referencedName = referenceExpression.getReferencedNameAsName();
250    
251            Set<DeclarationDescriptor> descriptors = Sets.newHashSet();
252            NamespaceDescriptor namespaceDescriptor = outerScope.getNamespace(referencedName);
253            if (namespaceDescriptor != null) {
254                descriptors.add(namespaceDescriptor);
255            }
256    
257            ClassifierDescriptor classifierDescriptor = outerScope.getClassifier(referencedName);
258            if (classifierDescriptor != null) {
259                descriptors.add(classifierDescriptor);
260            }
261    
262            if (lookupMode == LookupMode.ONLY_CLASSES) {
263                ClassDescriptor objectDescriptor = outerScope.getObjectDescriptor(referencedName);
264                if (objectDescriptor != null) {
265                    descriptors.add(objectDescriptor);
266                }
267            }
268            else {
269                descriptors.addAll(outerScope.getFunctions(referencedName));
270                descriptors.addAll(outerScope.getProperties(referencedName));
271    
272                VariableDescriptor localVariable = outerScope.getLocalVariable(referencedName);
273                if (localVariable != null) {
274                    descriptors.add(localVariable);
275                }
276            }
277            return new SuccessfulLookupResult(descriptors, outerScope, namespaceLevel);
278        }
279    
280        @NotNull
281        private Collection<? extends DeclarationDescriptor> filterAndStoreResolutionResult(@NotNull Collection<SuccessfulLookupResult> lookupResults,
282                @NotNull JetSimpleNameExpression referenceExpression, @NotNull final BindingTrace trace, @NotNull JetScope scopeToCheckVisibility,
283                @NotNull LookupMode lookupMode, boolean storeResult) {
284    
285            if (lookupResults.isEmpty()) {
286                return Collections.emptyList();
287            }
288            Collection<DeclarationDescriptor> descriptors = Sets.newLinkedHashSet();
289            for (SuccessfulLookupResult lookupResult : lookupResults) {
290                descriptors.addAll(lookupResult.descriptors);
291            }
292    
293            Collection<JetScope> possibleResolutionScopes = Lists.newArrayList();
294            for (SuccessfulLookupResult lookupResult : lookupResults) {
295                if (!lookupResult.descriptors.isEmpty()) {
296                    possibleResolutionScopes.add(lookupResult.resolutionScope);
297                }
298            }
299            if (possibleResolutionScopes.isEmpty()) {
300                for (SuccessfulLookupResult lookupResult : lookupResults) {
301                    possibleResolutionScopes.add(lookupResult.resolutionScope);
302                }
303            }
304    
305            Collection<DeclarationDescriptor> filteredDescriptors;
306            if (lookupMode == LookupMode.ONLY_CLASSES) {
307                filteredDescriptors = Collections2.filter(descriptors, new Predicate<DeclarationDescriptor>() {
308                    @Override
309                    public boolean apply(@Nullable DeclarationDescriptor descriptor) {
310                        return (descriptor instanceof ClassifierDescriptor) ||
311                               (descriptor instanceof NamespaceDescriptor);
312                    }
313                });
314            }
315            else {
316                filteredDescriptors = Sets.newLinkedHashSet();
317                //functions and properties can be imported if lookupResult.namespaceLevel == true
318                for (SuccessfulLookupResult lookupResult : lookupResults) {
319                    if (lookupResult.namespaceLevel) {
320                        filteredDescriptors.addAll(lookupResult.descriptors);
321                        continue;
322                    }
323                    filteredDescriptors.addAll(Collections2.filter(lookupResult.descriptors, new Predicate<DeclarationDescriptor>() {
324                        @Override
325                        public boolean apply(@Nullable DeclarationDescriptor descriptor) {
326                            return (descriptor instanceof NamespaceDescriptor) ||
327                                   (descriptor instanceof ClassifierDescriptor) ||
328                                   ((descriptor instanceof PropertyDescriptor) &&
329                                    (trace.get(BindingContext.OBJECT_DECLARATION_CLASS, ((PropertyDescriptor) descriptor)) != null));
330                        }
331                    }));
332                }
333            }
334            if (storeResult) {
335                storeResolutionResult(descriptors, filteredDescriptors, referenceExpression, possibleResolutionScopes, trace,
336                                      scopeToCheckVisibility);
337            }
338            return filteredDescriptors;
339        }
340    
341        private void storeResolutionResult(@NotNull Collection<? extends DeclarationDescriptor> descriptors,
342                @NotNull Collection<? extends DeclarationDescriptor> canBeImportedDescriptors,
343                @NotNull JetSimpleNameExpression referenceExpression,
344                @NotNull Collection<JetScope> possibleResolutionScopes,
345                @NotNull BindingTrace trace,
346                @NotNull JetScope scopeToCheckVisibility) {
347    
348            assert canBeImportedDescriptors.size() <= descriptors.size();
349            assert !possibleResolutionScopes.isEmpty();
350            //todo completion here needs all possible resolution scopes, if there are many
351            JetScope resolutionScope = possibleResolutionScopes.iterator().next();
352    
353            // A special case - will fill all trace information
354            if (resolveClassNamespaceAmbiguity(canBeImportedDescriptors, referenceExpression, resolutionScope, trace, scopeToCheckVisibility)) {
355                return;
356            }
357    
358            // Simple case of no descriptors
359            if (descriptors.isEmpty()) {
360                trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
361                trace.report(UNRESOLVED_REFERENCE.on(referenceExpression, referenceExpression));
362                return;
363            }
364    
365            // Decide if expression has resolved reference
366            DeclarationDescriptor descriptor = null;
367            if (descriptors.size() == 1) {
368                descriptor = descriptors.iterator().next();
369                assert canBeImportedDescriptors.size() <= 1;
370            }
371            else if (canBeImportedDescriptors.size() == 1) {
372                descriptor = canBeImportedDescriptors.iterator().next();
373            }
374            if (descriptor != null) {
375                trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, descriptors.iterator().next());
376                trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
377    
378                if (descriptor instanceof DeclarationDescriptorWithVisibility) {
379                    checkVisibility((DeclarationDescriptorWithVisibility)descriptor, trace, referenceExpression, scopeToCheckVisibility);
380                }
381            }
382    
383            // Check for more information and additional errors
384            if (canBeImportedDescriptors.isEmpty()) {
385                assert descriptors.size() >= 1;
386                trace.report(CANNOT_BE_IMPORTED.on(referenceExpression, descriptors.iterator().next()));
387                return;
388            }
389            if (canBeImportedDescriptors.size() > 1) {
390                trace.record(BindingContext.AMBIGUOUS_REFERENCE_TARGET, referenceExpression, descriptors);
391            }
392        }
393    
394        /**
395         * This method tries to resolve descriptors ambiguity between class descriptor and namespace descriptor for the same class.
396         * It's ok choose class for expression reference resolution.
397         *
398         * @return <code>true</code> if method has successfully resolved ambiguity
399         */
400        private boolean resolveClassNamespaceAmbiguity(@NotNull Collection<? extends DeclarationDescriptor> filteredDescriptors,
401            @NotNull JetSimpleNameExpression referenceExpression, @NotNull JetScope resolutionScope, @NotNull BindingTrace trace,
402            @NotNull JetScope scopeToCheckVisibility) {
403    
404            if (filteredDescriptors.size() == 2) {
405                NamespaceDescriptor namespaceDescriptor = null;
406                ClassDescriptor classDescriptor = null;
407    
408                for (DeclarationDescriptor filteredDescriptor : filteredDescriptors) {
409                    if (filteredDescriptor instanceof NamespaceDescriptor) {
410                        namespaceDescriptor = (NamespaceDescriptor)filteredDescriptor;
411                    }
412                    else if (filteredDescriptor instanceof ClassDescriptor) {
413                        classDescriptor = (ClassDescriptor)filteredDescriptor;
414                    }
415                }
416    
417                if (namespaceDescriptor != null && classDescriptor != null) {
418                    if (DescriptorUtils.getFQName(namespaceDescriptor).equalsTo(DescriptorUtils.getFQName(classDescriptor))) {
419                        trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, classDescriptor);
420                        trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
421                        checkVisibility(classDescriptor, trace, referenceExpression, scopeToCheckVisibility);
422                        return true;
423                    }
424                }
425            }
426    
427            return false;
428        }
429    
430        private void checkVisibility(@NotNull DeclarationDescriptorWithVisibility descriptor, @NotNull BindingTrace trace,
431                @NotNull JetSimpleNameExpression referenceExpression, @NotNull JetScope scopeToCheckVisibility) {
432    
433            if (!Visibilities.isVisible(descriptor, scopeToCheckVisibility.getContainingDeclaration())) {
434                trace.report(INVISIBLE_REFERENCE.on(referenceExpression, descriptor, descriptor.getVisibility(), descriptor.getContainingDeclaration()));
435            }
436        }
437    
438        private interface LookupResult {
439            LookupResult EMPTY = new LookupResult() {
440            };
441        }
442    
443        private static class SuccessfulLookupResult implements LookupResult {
444            final Collection<? extends DeclarationDescriptor> descriptors;
445            final JetScope resolutionScope;
446            final boolean namespaceLevel;
447    
448            private SuccessfulLookupResult(Collection<? extends DeclarationDescriptor> descriptors,
449                                           JetScope resolutionScope,
450                                           boolean namespaceLevel) {
451                this.descriptors = descriptors;
452                this.resolutionScope = resolutionScope;
453                this.namespaceLevel = namespaceLevel;
454            }
455        }
456    }