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.lazy;
018
019import com.google.common.base.Function;
020import com.google.common.base.Functions;
021import com.google.common.base.Predicates;
022import com.google.common.collect.Lists;
023import com.intellij.psi.PsiElement;
024import com.intellij.psi.PsiFile;
025import com.intellij.psi.util.PsiTreeUtil;
026import org.jetbrains.annotations.NotNull;
027import org.jetbrains.annotations.Nullable;
028import org.jetbrains.jet.di.InjectorForBodyResolve;
029import org.jetbrains.jet.lang.descriptors.*;
030import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorBase;
031import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptor;
032import org.jetbrains.jet.lang.psi.*;
033import org.jetbrains.jet.lang.resolve.*;
034import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyClassDescriptor;
035import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyPackageDescriptor;
036import org.jetbrains.jet.lang.resolve.name.FqName;
037import org.jetbrains.jet.lang.resolve.name.Name;
038import org.jetbrains.jet.lang.resolve.scopes.JetScope;
039import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
040import org.jetbrains.jet.util.QualifiedNamesUtil;
041
042import java.util.*;
043
044public class ResolveSessionUtils {
045
046    // This name is used as a key for the case when something has no name _due to a syntactic error_
047    // Example: fun (x: Int) = 5
048    //          There's no name for this function in the PSI
049    // The name contains a GUID to avoid clashes, if a clash happens, it's not a big deal: the code does not compile anyway
050    public static final Name NO_NAME_FOR_LAZY_RESOLVE = Name.identifier("no_name_in_PSI_for_lazy_resolve_3d19d79d_1ba9_4cd0_b7f5_b46aa3cd5d40");
051
052    private ResolveSessionUtils() {
053    }
054
055    @SuppressWarnings("unchecked")
056    private static final BodyResolveContextForLazy EMPTY_CONTEXT = new BodyResolveContextForLazy((Function) Functions.constant(null));
057
058    private static class BodyResolveContextForLazy implements BodiesResolveContext {
059
060        private final Function<JetDeclaration, JetScope> declaringScopes;
061
062        private BodyResolveContextForLazy(@NotNull Function<JetDeclaration, JetScope> declaringScopes) {
063            this.declaringScopes = declaringScopes;
064        }
065
066        @Override
067        public Collection<JetFile> getFiles() {
068            return Collections.emptySet();
069        }
070
071        @Override
072        public Map<JetClass, MutableClassDescriptor> getClasses() {
073            return Collections.emptyMap();
074        }
075
076        @Override
077        public Map<JetObjectDeclaration, MutableClassDescriptor> getObjects() {
078            return Collections.emptyMap();
079        }
080
081        @Override
082        public Map<JetProperty, PropertyDescriptor> getProperties() {
083            return Collections.emptyMap();
084        }
085
086        @Override
087        public Map<JetNamedFunction, SimpleFunctionDescriptor> getFunctions() {
088            return Collections.emptyMap();
089        }
090
091        @Override
092        public Function<JetDeclaration, JetScope> getDeclaringScopes() {
093            return declaringScopes;
094        }
095
096        @Override
097        public Map<JetScript, ScriptDescriptor> getScripts() {
098            return Collections.emptyMap();
099        }
100
101        @Override
102        public Map<JetScript, WritableScope> getScriptScopes() {
103            return Collections.emptyMap();
104        }
105
106        @Override
107        public void setTopDownAnalysisParameters(TopDownAnalysisParameters parameters) {
108        }
109
110        @Override
111        public boolean completeAnalysisNeeded(@NotNull PsiElement element) {
112            return true;
113        }
114    }
115
116    public static @NotNull BindingContext resolveToExpression(
117            @NotNull ResolveSession resolveSession,
118            @NotNull JetElement expression
119    ) {
120        DelegatingBindingTrace trace = new DelegatingBindingTrace(
121                resolveSession.getBindingContext(), "trace to resolve expression", expression);
122        JetFile file = (JetFile) expression.getContainingFile();
123
124        @SuppressWarnings("unchecked")
125        PsiElement topmostCandidateForAdditionalResolve = JetPsiUtil.getTopmostParentOfTypes(expression,
126                JetNamedFunction.class, JetClassInitializer.class, JetProperty.class, JetDelegationSpecifierList.class);
127
128        if (topmostCandidateForAdditionalResolve != null) {
129            if (topmostCandidateForAdditionalResolve instanceof JetNamedFunction) {
130                functionAdditionalResolve(resolveSession, (JetNamedFunction) topmostCandidateForAdditionalResolve, trace, file);
131            }
132            else if (topmostCandidateForAdditionalResolve instanceof JetClassInitializer) {
133                initializerAdditionalResolve(resolveSession, (JetClassInitializer) topmostCandidateForAdditionalResolve, trace, file);
134            }
135            else if (topmostCandidateForAdditionalResolve instanceof JetProperty) {
136                propertyAdditionalResolve(resolveSession, (JetProperty) topmostCandidateForAdditionalResolve, trace, file);
137            }
138            else if (topmostCandidateForAdditionalResolve instanceof JetDelegationSpecifierList) {
139                delegationSpecifierAdditionalResolve(resolveSession, (JetDelegationSpecifierList) topmostCandidateForAdditionalResolve,
140                                                     trace, file);
141            }
142            else {
143                assert false : "Invalid type of the topmost parent";
144            }
145
146            return trace.getBindingContext();
147        }
148
149        if (expression instanceof JetExpression) {
150            JetExpression jetExpression = (JetExpression) expression;
151            // Setup resolution scope explicitly
152            if (trace.getBindingContext().get(BindingContext.RESOLUTION_SCOPE, jetExpression) == null) {
153                JetScope scope = getExpressionMemberScope(resolveSession, jetExpression);
154                if (scope != null) {
155                    trace.record(BindingContext.RESOLUTION_SCOPE, jetExpression, scope);
156                }
157            }
158        }
159
160        return trace.getBindingContext();
161    }
162
163    private static void delegationSpecifierAdditionalResolve(
164            KotlinCodeAnalyzer analyzer,
165            JetDelegationSpecifierList specifier, DelegatingBindingTrace trace, JetFile file) {
166        BodyResolver bodyResolver = createBodyResolverWithEmptyContext(trace, file, analyzer.getRootModuleDescriptor());
167
168        JetClassOrObject classOrObject = (JetClassOrObject) specifier.getParent();
169        LazyClassDescriptor descriptor = (LazyClassDescriptor) analyzer.resolveToDescriptor(classOrObject);
170
171        // Activate resolving of supertypes
172        descriptor.getTypeConstructor().getSupertypes();
173
174        bodyResolver.resolveDelegationSpecifierList(classOrObject, descriptor,
175                                                    descriptor.getUnsubstitutedPrimaryConstructor(),
176                                                    descriptor.getScopeForClassHeaderResolution(),
177                                                    descriptor.getScopeForMemberDeclarationResolution());
178    }
179
180    private static void propertyAdditionalResolve(ResolveSession resolveSession, final JetProperty jetProperty, DelegatingBindingTrace trace, JetFile file) {
181        final JetScope propertyResolutionScope = resolveSession.getInjector().getScopeProvider().getResolutionScopeForDeclaration(
182                jetProperty);
183
184        BodyResolveContextForLazy bodyResolveContext = new BodyResolveContextForLazy(new Function<JetDeclaration, JetScope>() {
185            @Override
186            public JetScope apply(JetDeclaration declaration) {
187                assert declaration.getParent() == jetProperty : "Must be called only for property accessors, but called for " + declaration;
188                return propertyResolutionScope;
189            }
190        });
191        BodyResolver bodyResolver = createBodyResolver(trace, file, bodyResolveContext, resolveSession.getRootModuleDescriptor());
192        PropertyDescriptor descriptor = (PropertyDescriptor) resolveSession.resolveToDescriptor(jetProperty);
193
194        JetExpression propertyInitializer = jetProperty.getInitializer();
195        if (propertyInitializer != null) {
196            bodyResolver.resolvePropertyInitializer(jetProperty, descriptor, propertyInitializer, propertyResolutionScope);
197        }
198
199        JetExpression propertyDelegate = jetProperty.getDelegateExpression();
200        if (propertyDelegate != null) {
201            bodyResolver.resolvePropertyDelegate(jetProperty, descriptor, propertyDelegate, propertyResolutionScope, propertyResolutionScope);
202        }
203
204        bodyResolver.resolvePropertyAccessors(jetProperty, descriptor);
205    }
206
207    private static void functionAdditionalResolve(
208            ResolveSession resolveSession,
209            JetNamedFunction namedFunction,
210            DelegatingBindingTrace trace,
211            JetFile file
212    ) {
213        BodyResolver bodyResolver = createBodyResolverWithEmptyContext(trace, file, resolveSession.getRootModuleDescriptor());
214        JetScope scope = resolveSession.getInjector().getScopeProvider().getResolutionScopeForDeclaration(namedFunction);
215        FunctionDescriptor functionDescriptor = (FunctionDescriptor) resolveSession.resolveToDescriptor(namedFunction);
216        bodyResolver.resolveFunctionBody(trace, namedFunction, functionDescriptor, scope);
217    }
218
219    private static boolean initializerAdditionalResolve(
220            KotlinCodeAnalyzer analyzer,
221            JetClassInitializer classInitializer,
222            DelegatingBindingTrace trace,
223            JetFile file
224    ) {
225        BodyResolver bodyResolver = createBodyResolverWithEmptyContext(trace, file, analyzer.getRootModuleDescriptor());
226        JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(classInitializer, JetClassOrObject.class);
227        LazyClassDescriptor classOrObjectDescriptor = (LazyClassDescriptor) analyzer.resolveToDescriptor(classOrObject);
228        bodyResolver.resolveAnonymousInitializers(classOrObject, classOrObjectDescriptor.getUnsubstitutedPrimaryConstructor(),
229                classOrObjectDescriptor.getScopeForPropertyInitializerResolution());
230
231        return true;
232    }
233
234    private static BodyResolver createBodyResolver(DelegatingBindingTrace trace, JetFile file, BodyResolveContextForLazy bodyResolveContext,
235            ModuleDescriptor module) {
236        TopDownAnalysisParameters parameters = new TopDownAnalysisParameters(
237                Predicates.<PsiFile>alwaysTrue(), false, true, Collections.<AnalyzerScriptParameter>emptyList());
238        InjectorForBodyResolve bodyResolve = new InjectorForBodyResolve(file.getProject(), parameters, trace, bodyResolveContext, module);
239        return bodyResolve.getBodyResolver();
240    }
241
242    private static BodyResolver createBodyResolverWithEmptyContext(
243            DelegatingBindingTrace trace,
244            JetFile file,
245            ModuleDescriptor module
246    ) {
247        return createBodyResolver(trace, file, EMPTY_CONTEXT, module);
248    }
249
250    private static JetScope getExpressionResolutionScope(@NotNull ResolveSession resolveSession, @NotNull JetExpression expression) {
251        ScopeProvider provider = resolveSession.getInjector().getScopeProvider();
252        JetDeclaration parentDeclaration = PsiTreeUtil.getParentOfType(expression, JetDeclaration.class);
253        if (parentDeclaration == null) {
254            return provider.getFileScope((JetFile) expression.getContainingFile());
255        }
256        return provider.getResolutionScopeForDeclaration(parentDeclaration);
257    }
258
259    public static JetScope getExpressionMemberScope(@NotNull ResolveSession resolveSession, @NotNull JetExpression expression) {
260        DelegatingBindingTrace trace = new DelegatingBindingTrace(
261                resolveSession.getBindingContext(), "trace to resolve a member scope of expression", expression);
262
263        if (BindingContextUtils.isExpressionWithValidReference(expression, resolveSession.getBindingContext())) {
264            QualifiedExpressionResolver qualifiedExpressionResolver = resolveSession.getInjector().getQualifiedExpressionResolver();
265
266            // In some type declaration
267            if (expression.getParent() instanceof JetUserType) {
268                JetUserType qualifier = ((JetUserType) expression.getParent()).getQualifier();
269                if (qualifier != null) {
270                    Collection<? extends DeclarationDescriptor> descriptors = qualifiedExpressionResolver
271                            .lookupDescriptorsForUserType(qualifier, getExpressionResolutionScope(resolveSession, expression), trace);
272
273                    for (DeclarationDescriptor descriptor : descriptors) {
274                        if (descriptor instanceof LazyPackageDescriptor) {
275                            return ((LazyPackageDescriptor) descriptor).getMemberScope();
276                        }
277                    }
278                }
279            }
280
281            // Inside import
282            if (PsiTreeUtil.getParentOfType(expression, JetImportDirective.class, false) != null) {
283                NamespaceDescriptor rootPackage = resolveSession.getPackageDescriptorByFqName(FqName.ROOT);
284                assert rootPackage != null;
285
286                if (expression.getParent() instanceof JetDotQualifiedExpression) {
287                    JetExpression element = ((JetDotQualifiedExpression) expression.getParent()).getReceiverExpression();
288                    String name = ((JetFile) expression.getContainingFile()).getPackageName();
289
290                    NamespaceDescriptor filePackage =
291                            name != null ? resolveSession.getPackageDescriptorByFqName(new FqName(name)) : rootPackage;
292                    assert filePackage != null : "File package should be already resolved and be found";
293
294                    JetScope scope = filePackage.getMemberScope();
295                    Collection<? extends DeclarationDescriptor> descriptors;
296
297                    if (element instanceof JetDotQualifiedExpression) {
298                        descriptors = qualifiedExpressionResolver.lookupDescriptorsForQualifiedExpression(
299                                (JetDotQualifiedExpression) element, rootPackage.getMemberScope(), scope, trace,
300                                QualifiedExpressionResolver.LookupMode.EVERYTHING, false);
301                    }
302                    else {
303                        descriptors = qualifiedExpressionResolver.lookupDescriptorsForSimpleNameReference(
304                                (JetSimpleNameExpression) element, rootPackage.getMemberScope(), scope, trace,
305                                QualifiedExpressionResolver.LookupMode.EVERYTHING, false, false);
306                    }
307
308                    for (DeclarationDescriptor descriptor : descriptors) {
309                        if (descriptor instanceof NamespaceDescriptor) {
310                            return ((NamespaceDescriptor) descriptor).getMemberScope();
311                        }
312                    }
313                }
314                else {
315                    return rootPackage.getMemberScope();
316                }
317            }
318
319            // Inside package declaration
320            JetNamespaceHeader namespaceHeader = PsiTreeUtil.getParentOfType(expression, JetNamespaceHeader.class, false);
321            if (namespaceHeader != null) {
322                NamespaceDescriptor packageDescriptor = resolveSession.getPackageDescriptorByFqName(
323                        namespaceHeader.getParentFqName((JetReferenceExpression) expression));
324                if (packageDescriptor != null) {
325                    return packageDescriptor.getMemberScope();
326                }
327            }
328        }
329
330        return null;
331    }
332
333    @NotNull
334    public static Collection<ClassDescriptor> getClassDescriptorsByFqName(
335                @NotNull KotlinCodeAnalyzer analyzer,
336                @NotNull FqName fqName
337    ) {
338        return getClassOrObjectDescriptorsByFqName(analyzer, fqName, false);
339    }
340
341    @NotNull
342    public static Collection<ClassDescriptor> getClassOrObjectDescriptorsByFqName(
343            @NotNull KotlinCodeAnalyzer analyzer,
344            @NotNull FqName fqName,
345            boolean includeObjectDeclarations
346    ) {
347        if (fqName.isRoot()) {
348            return Collections.emptyList();
349        }
350
351        Collection<ClassDescriptor> classDescriptors = Lists.newArrayList();
352
353        FqName packageFqName = fqName.parent();
354        while (true) {
355            NamespaceDescriptor packageDescriptor = analyzer.getPackageDescriptorByFqName(packageFqName);
356            if (packageDescriptor != null) {
357                FqName classInPackagePath = new FqName(QualifiedNamesUtil.tail(packageFqName, fqName));
358                Collection<ClassDescriptor> descriptors = getClassOrObjectDescriptorsByFqName(packageDescriptor, classInPackagePath,
359                                                                                              includeObjectDeclarations);
360                classDescriptors.addAll(descriptors);
361            }
362
363            if (packageFqName.isRoot()) {
364                break;
365            }
366            else {
367                packageFqName = packageFqName.parent();
368            }
369        }
370
371        return classDescriptors;
372    }
373
374    private static Collection<ClassDescriptor> getClassOrObjectDescriptorsByFqName(
375            NamespaceDescriptor packageDescriptor,
376            FqName path,
377            boolean includeObjectDeclarations
378    ) {
379        if (path.isRoot()) {
380            return Collections.emptyList();
381        }
382
383        Collection<JetScope> scopes = Arrays.asList(packageDescriptor.getMemberScope());
384
385        List<Name> names = path.pathSegments();
386        if (names.size() > 1) {
387            for (Name subName : path.pathSegments().subList(0, names.size() - 1)) {
388                Collection<JetScope> tempScopes = Lists.newArrayList();
389                for (JetScope scope : scopes) {
390                    ClassifierDescriptor classifier = scope.getClassifier(subName);
391                    if (classifier instanceof ClassDescriptorBase) {
392                        ClassDescriptorBase classDescriptor = (ClassDescriptorBase) classifier;
393                        tempScopes.add(classDescriptor.getUnsubstitutedInnerClassesScope());
394                    }
395                }
396                scopes = tempScopes;
397            }
398        }
399
400        Name shortName = path.shortName();
401        Collection<ClassDescriptor> resultClassifierDescriptors = Lists.newArrayList();
402        for (JetScope scope : scopes) {
403            ClassifierDescriptor classifier = scope.getClassifier(shortName);
404            if (classifier instanceof ClassDescriptor) {
405                resultClassifierDescriptors.add((ClassDescriptor) classifier);
406            }
407            if (includeObjectDeclarations) {
408                ClassDescriptor objectDescriptor = scope.getObjectDescriptor(shortName);
409                if (objectDescriptor != null) {
410                    resultClassifierDescriptors.add(objectDescriptor);
411                }
412            }
413        }
414
415        return resultClassifierDescriptors;
416    }
417
418    @NotNull
419    public static Name safeNameForLazyResolve(@NotNull JetNamed named) {
420        Name name = named.getNameAsName();
421        return safeNameForLazyResolve(name);
422    }
423
424    @NotNull
425    public static Name safeNameForLazyResolve(@Nullable Name name) {
426        return name != null ? name : NO_NAME_FOR_LAZY_RESOLVE;
427    }
428}