001    /*
002     * Copyright 2010-2015 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.kotlin.resolve.lazy;
018    
019    import com.intellij.psi.PsiElement;
020    import com.intellij.psi.util.PsiTreeUtil;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.kotlin.context.GlobalContext;
023    import org.jetbrains.kotlin.descriptors.*;
024    import org.jetbrains.kotlin.incremental.KotlinLookupLocation;
025    import org.jetbrains.kotlin.incremental.components.LookupLocation;
026    import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
027    import org.jetbrains.kotlin.name.FqName;
028    import org.jetbrains.kotlin.name.Name;
029    import org.jetbrains.kotlin.psi.*;
030    import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
031    import org.jetbrains.kotlin.renderer.DescriptorRenderer;
032    import org.jetbrains.kotlin.resolve.BindingContext;
033    import org.jetbrains.kotlin.resolve.BindingTrace;
034    import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyPackageDescriptor;
035    import org.jetbrains.kotlin.resolve.scopes.MemberScope;
036    import org.jetbrains.kotlin.storage.LockBasedLazyResolveStorageManager;
037    
038    import javax.inject.Inject;
039    import java.util.List;
040    
041    public class LazyDeclarationResolver {
042    
043        @NotNull private final TopLevelDescriptorProvider topLevelDescriptorProvider;
044        @NotNull private final BindingTrace trace;
045    
046        protected DeclarationScopeProvider scopeProvider;
047    
048        // component dependency cycle
049        @Inject
050        public void setDeclarationScopeProvider(@NotNull DeclarationScopeProviderImpl scopeProvider) {
051            this.scopeProvider = scopeProvider;
052        }
053    
054        @Deprecated
055        public LazyDeclarationResolver(
056                @NotNull GlobalContext globalContext,
057                @NotNull BindingTrace delegationTrace,
058                @NotNull TopLevelDescriptorProvider topLevelDescriptorProvider
059        ) {
060            this.topLevelDescriptorProvider = topLevelDescriptorProvider;
061            LockBasedLazyResolveStorageManager lockBasedLazyResolveStorageManager =
062                    new LockBasedLazyResolveStorageManager(globalContext.getStorageManager());
063    
064            this.trace = lockBasedLazyResolveStorageManager.createSafeTrace(delegationTrace);
065        }
066    
067        @NotNull
068        public ClassDescriptor getClassDescriptor(@NotNull KtClassOrObject classOrObject, @NotNull LookupLocation location) {
069            MemberScope scope = getMemberScopeDeclaredIn(classOrObject, location);
070    
071            // Why not use the result here. Because it may be that there is a redeclaration:
072            //     class A {} class A { fun foo(): A<completion here>}
073            // and if we find the class by name only, we may b-not get the right one.
074            // This call is only needed to make sure the classes are written to trace
075            ClassifierDescriptor scopeDescriptor = scope.getContributedClassifier(classOrObject.getNameAsSafeName(), location);
076            DeclarationDescriptor descriptor = getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, classOrObject);
077    
078            if (descriptor == null) {
079                throw new IllegalArgumentException(
080                       String.format("Could not find a classifier for %s.\n" +
081                                     "Found descriptor: %s (%s).\n",
082                                     PsiUtilsKt.getElementTextWithContext(classOrObject),
083                                     scopeDescriptor != null ? DescriptorRenderer.DEBUG_TEXT.render(scopeDescriptor) : "null",
084                                     scopeDescriptor != null ? (scopeDescriptor.getContainingDeclaration().getClass()) : null));
085            }
086    
087            return (ClassDescriptor) descriptor;
088        }
089    
090        @NotNull
091        private BindingContext getBindingContext() {
092            return trace.getBindingContext();
093        }
094    
095        @NotNull
096        public DeclarationDescriptor resolveToDescriptor(@NotNull KtDeclaration declaration) {
097            return resolveToDescriptor(declaration, /*track =*/true);
098        }
099    
100        @NotNull
101        private DeclarationDescriptor resolveToDescriptor(@NotNull KtDeclaration declaration, final boolean track) {
102            DeclarationDescriptor result = declaration.accept(new KtVisitor<DeclarationDescriptor, Void>() {
103                @NotNull
104                private LookupLocation lookupLocationFor(@NotNull KtDeclaration declaration, boolean isTopLevel) {
105                    return isTopLevel && track ? new KotlinLookupLocation(declaration) : NoLookupLocation.WHEN_RESOLVE_DECLARATION;
106                }
107    
108                @Override
109                public DeclarationDescriptor visitClass(@NotNull KtClass klass, Void data) {
110                    return getClassDescriptor(klass, lookupLocationFor(klass, klass.isTopLevel()));
111                }
112    
113                @Override
114                public DeclarationDescriptor visitObjectDeclaration(@NotNull KtObjectDeclaration declaration, Void data) {
115                    return getClassDescriptor(declaration, lookupLocationFor(declaration, declaration.isTopLevel()));
116                }
117    
118                @Override
119                public DeclarationDescriptor visitTypeParameter(@NotNull KtTypeParameter parameter, Void data) {
120                    KtTypeParameterListOwner ownerElement = PsiTreeUtil.getParentOfType(parameter, KtTypeParameterListOwner.class);
121                    assert ownerElement != null : "Owner not found for type parameter: " + parameter.getText();
122                    DeclarationDescriptor ownerDescriptor = resolveToDescriptor(ownerElement, /*track =*/false);
123    
124                    List<TypeParameterDescriptor> typeParameters;
125                    if (ownerDescriptor instanceof CallableDescriptor) {
126                        CallableDescriptor callableDescriptor = (CallableDescriptor) ownerDescriptor;
127                        typeParameters = callableDescriptor.getTypeParameters();
128                    }
129                    else if (ownerDescriptor instanceof ClassDescriptor) {
130                        ClassDescriptor classDescriptor = (ClassDescriptor) ownerDescriptor;
131                        typeParameters = classDescriptor.getTypeConstructor().getParameters();
132                    }
133                    else {
134                        throw new IllegalStateException("Unknown owner kind for a type parameter: " + ownerDescriptor);
135                    }
136    
137                    Name name = parameter.getNameAsSafeName();
138                    for (TypeParameterDescriptor typeParameterDescriptor : typeParameters) {
139                        if (typeParameterDescriptor.getName().equals(name)) {
140                            return typeParameterDescriptor;
141                        }
142                    }
143    
144                    throw new IllegalStateException("Type parameter " + name + " not found for " + ownerDescriptor);
145                }
146    
147                @Override
148                public DeclarationDescriptor visitNamedFunction(@NotNull KtNamedFunction function, Void data) {
149                    LookupLocation location = lookupLocationFor(function, function.isTopLevel());
150                    MemberScope scopeForDeclaration = getMemberScopeDeclaredIn(function, location);
151                    scopeForDeclaration.getContributedFunctions(function.getNameAsSafeName(), location);
152                    return getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, function);
153                }
154    
155                @Override
156                public DeclarationDescriptor visitParameter(@NotNull KtParameter parameter, Void data) {
157                    PsiElement grandFather = parameter.getParent().getParent();
158                    if (grandFather instanceof KtPrimaryConstructor) {
159                        KtClassOrObject jetClass = ((KtPrimaryConstructor) grandFather).getContainingClassOrObject();
160                        // This is a primary constructor parameter
161                        ClassDescriptor classDescriptor = getClassDescriptor(jetClass, lookupLocationFor(jetClass, false));
162                        if (parameter.hasValOrVar()) {
163                            classDescriptor.getDefaultType().getMemberScope().getContributedVariables(parameter.getNameAsSafeName(), lookupLocationFor(parameter, false));
164                            return getBindingContext().get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter);
165                        }
166                        else {
167                            ConstructorDescriptor constructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
168                            assert constructor != null: "There are constructor parameters found, so a constructor should also exist";
169                            constructor.getValueParameters();
170                            return getBindingContext().get(BindingContext.VALUE_PARAMETER, parameter);
171                        }
172                    }
173                    else if (grandFather instanceof KtNamedFunction) {
174                        FunctionDescriptor function = (FunctionDescriptor) visitNamedFunction((KtNamedFunction) grandFather, data);
175                        function.getValueParameters();
176                        return getBindingContext().get(BindingContext.VALUE_PARAMETER, parameter);
177                    }
178                    else if (grandFather instanceof KtSecondaryConstructor) {
179                        ConstructorDescriptor constructorDescriptor = (ConstructorDescriptor) visitSecondaryConstructor(
180                                (KtSecondaryConstructor) grandFather, data
181                        );
182                        constructorDescriptor.getValueParameters();
183                        return getBindingContext().get(BindingContext.VALUE_PARAMETER, parameter);
184                    }
185                    else {
186                        //TODO: support parameters in accessors and other places(?)
187                        return super.visitParameter(parameter, data);
188                    }
189                }
190    
191                @Override
192                public DeclarationDescriptor visitSecondaryConstructor(@NotNull KtSecondaryConstructor constructor, Void data) {
193                    getClassDescriptor((KtClassOrObject) constructor.getParent().getParent(), lookupLocationFor(constructor, false)).getConstructors();
194                    return getBindingContext().get(BindingContext.CONSTRUCTOR, constructor);
195                }
196    
197                @Override
198                public DeclarationDescriptor visitPrimaryConstructor(@NotNull KtPrimaryConstructor constructor, Void data) {
199                    getClassDescriptor(constructor.getContainingClassOrObject(), lookupLocationFor(constructor, false)).getConstructors();
200                    return getBindingContext().get(BindingContext.CONSTRUCTOR, constructor);
201                }
202    
203                @Override
204                public DeclarationDescriptor visitProperty(@NotNull KtProperty property, Void data) {
205                    LookupLocation location = lookupLocationFor(property, property.isTopLevel());
206                    MemberScope scopeForDeclaration = getMemberScopeDeclaredIn(property, location);
207                    scopeForDeclaration.getContributedVariables(property.getNameAsSafeName(), location);
208                    return getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, property);
209                }
210    
211                @Override
212                public DeclarationDescriptor visitScript(@NotNull KtScript script, Void data) {
213                    return topLevelDescriptorProvider.getScriptDescriptor(script);
214                }
215    
216                @Override
217                public DeclarationDescriptor visitKtElement(@NotNull KtElement element, Void data) {
218                    throw new IllegalArgumentException("Unsupported declaration type: " + element + " " +
219                                                       PsiUtilsKt.getElementTextWithContext(element));
220                }
221            }, null);
222            if (result == null) {
223                throw new NoDescriptorForDeclarationException(declaration);
224            }
225            return result;
226        }
227    
228        @NotNull
229        /*package*/ MemberScope getMemberScopeDeclaredIn(@NotNull KtDeclaration declaration, @NotNull LookupLocation location) {
230            KtDeclaration parentDeclaration = KtStubbedPsiUtil.getContainingDeclaration(declaration);
231            boolean isTopLevel = parentDeclaration == null;
232            if (isTopLevel) { // for top level declarations we search directly in package because of possible conflicts with imports
233                FqName fqName = ((KtFile) declaration.getContainingFile()).getPackageFqName();
234                LazyPackageDescriptor packageDescriptor = topLevelDescriptorProvider.getPackageFragment(fqName);
235                assert packageDescriptor != null;
236                return packageDescriptor.getMemberScope();
237            }
238            else {
239                if (parentDeclaration instanceof KtClassOrObject) {
240                    return getClassDescriptor((KtClassOrObject) parentDeclaration, location).getUnsubstitutedMemberScope();
241                } else {
242                    throw new IllegalStateException("Don't call this method for local declarations: " + declaration + "\n" +
243                                                    PsiUtilsKt.getElementTextWithContext(declaration));
244                }
245            }
246        }
247    }