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
017package org.jetbrains.jet.lang.resolve;
018
019import com.google.common.base.Predicate;
020import com.google.common.collect.Collections2;
021import com.google.common.collect.Lists;
022import com.google.common.collect.Sets;
023import org.jetbrains.annotations.NotNull;
024import org.jetbrains.annotations.Nullable;
025import org.jetbrains.jet.lang.descriptors.*;
026import org.jetbrains.jet.lang.diagnostics.Errors;
027import org.jetbrains.jet.lang.psi.*;
028import org.jetbrains.jet.lang.resolve.name.Name;
029import org.jetbrains.jet.lang.resolve.scopes.JetScope;
030
031import java.util.Collection;
032import java.util.Collections;
033import java.util.Set;
034
035import static org.jetbrains.jet.lang.diagnostics.Errors.*;
036
037public 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}