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.collect.HashMultimap;
020    import com.google.common.collect.Multimap;
021    import com.intellij.psi.PsiElement;
022    import com.intellij.psi.util.PsiTreeUtil;
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.psi.*;
027    import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer;
028    import org.jetbrains.jet.lang.resolve.lazy.LazyImportScope;
029    import org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyClassDescriptor;
030    import org.jetbrains.jet.lang.resolve.name.FqName;
031    
032    import javax.inject.Inject;
033    import java.util.ArrayList;
034    import java.util.Collection;
035    import java.util.List;
036    
037    import static org.jetbrains.jet.lang.diagnostics.Errors.MANY_CLASS_OBJECTS;
038    import static org.jetbrains.jet.lang.diagnostics.Errors.UNSUPPORTED;
039    
040    public class LazyTopDownAnalyzer {
041    
042        @NotNull
043        private BindingTrace trace;
044        @NotNull
045        private DeclarationResolver declarationResolver;
046        @NotNull
047        private OverrideResolver overrideResolver;
048        @NotNull
049        private OverloadResolver overloadResolver;
050        @NotNull
051        private ModuleDescriptor moduleDescriptor;
052        @NotNull
053        private BodyResolver bodyResolver;
054    
055        @Inject
056        public void setTrace(@NotNull BindingTrace trace) {
057            this.trace = trace;
058        }
059    
060        @Inject
061        public void setDeclarationResolver(@NotNull DeclarationResolver declarationResolver) {
062            this.declarationResolver = declarationResolver;
063        }
064    
065        @Inject
066        public void setOverrideResolver(@NotNull OverrideResolver overrideResolver) {
067            this.overrideResolver = overrideResolver;
068        }
069    
070        @Inject
071        public void setOverloadResolver(@NotNull OverloadResolver overloadResolver) {
072            this.overloadResolver = overloadResolver;
073        }
074    
075        @Inject
076        public void setModuleDescriptor(@NotNull ModuleDescriptor moduleDescriptor) {
077            this.moduleDescriptor = moduleDescriptor;
078        }
079    
080        @Inject
081        public void setBodyResolver(@NotNull BodyResolver bodyResolver) {
082            this.bodyResolver = bodyResolver;
083        }
084    
085        @NotNull
086        public TopDownAnalysisContext analyzeDeclarations(
087                final KotlinCodeAnalyzer resolveSession,
088                @NotNull TopDownAnalysisParameters topDownAnalysisParameters,
089                @NotNull Collection<? extends PsiElement> declarations
090        ) {
091            assert topDownAnalysisParameters.isLazyTopDownAnalysis() : "Lazy analyzer is run in non-lazy mode";
092    
093            final TopDownAnalysisContext c = new TopDownAnalysisContext(topDownAnalysisParameters);
094            final Multimap<FqName, JetElement> topLevelFqNames = HashMultimap.create();
095    
096            final List<JetProperty> properties = new ArrayList<JetProperty>();
097            final List<JetNamedFunction> functions = new ArrayList<JetNamedFunction>();
098    
099            // fill in the context
100            for (PsiElement declaration : declarations) {
101                declaration.accept(
102                        new JetVisitorVoid() {
103    
104                            private void registerDeclarations(@NotNull List<JetDeclaration> declarations) {
105                                for (JetDeclaration jetDeclaration : declarations) {
106                                    jetDeclaration.accept(this);
107                                }
108                            }
109    
110                            @Override
111                            public void visitDeclaration(@NotNull JetDeclaration dcl) {
112                                throw new IllegalArgumentException("Unsupported declaration: " + dcl + " " + dcl.getText());
113                            }
114    
115                            @Override
116                            public void visitJetFile(@NotNull JetFile file) {
117                                if (file.isScript()) {
118                                    JetScript script = file.getScript();
119                                    assert script != null;
120    
121                                    c.getScripts().put(script, resolveSession.getScriptDescriptor(script));
122                                }
123                                else {
124                                    JetPackageDirective packageDirective = file.getPackageDirective();
125                                    assert packageDirective != null : "No package in a non-script file: " + file;
126    
127                                    c.addFile(file);
128    
129                                    packageDirective.accept(this);
130                                    DescriptorResolver.registerFileInPackage(trace, file);
131    
132                                    registerDeclarations(file.getDeclarations());
133    
134                                    topLevelFqNames.put(file.getPackageFqName(), packageDirective);
135                                }
136                            }
137    
138                            @Override
139                            public void visitPackageDirective(@NotNull JetPackageDirective directive) {
140                                DescriptorResolver.resolvePackageHeader(directive, moduleDescriptor, trace);
141                            }
142    
143                            @Override
144                            public void visitImportDirective(@NotNull JetImportDirective importDirective) {
145                                LazyImportScope importScope = resolveSession.getScopeProvider().getExplicitImportsScopeForFile(importDirective.getContainingJetFile());
146                                importScope.forceResolveImportDirective(importDirective);
147                            }
148    
149                            private void visitClassOrObject(@NotNull JetClassOrObject classOrObject) {
150                                ClassDescriptorWithResolutionScopes descriptor =
151                                        (ClassDescriptorWithResolutionScopes) resolveSession.getClassDescriptor(classOrObject);
152    
153                                c.getDeclaredClasses().put(classOrObject, descriptor);
154                                registerDeclarations(classOrObject.getDeclarations());
155                                registerTopLevelFqName(topLevelFqNames, classOrObject, descriptor);
156    
157                                checkManyClassObjects(classOrObject);
158                            }
159    
160                            private void checkManyClassObjects(JetClassOrObject classOrObject) {
161                                boolean classObjectAlreadyFound = false;
162                                for (JetDeclaration jetDeclaration : classOrObject.getDeclarations()) {
163                                    jetDeclaration.accept(this);
164    
165                                    if (jetDeclaration instanceof JetClassObject) {
166                                        if (classObjectAlreadyFound) {
167                                            trace.report(MANY_CLASS_OBJECTS.on((JetClassObject) jetDeclaration));
168                                        }
169                                        classObjectAlreadyFound = true;
170                                    }
171                                }
172                            }
173    
174                            @Override
175                            public void visitClass(@NotNull JetClass klass) {
176                                visitClassOrObject(klass);
177    
178                                registerPrimaryConstructorParameters(klass);
179                            }
180    
181                            private void registerPrimaryConstructorParameters(@NotNull JetClass klass) {
182                                for (JetParameter jetParameter : klass.getPrimaryConstructorParameters()) {
183                                    if (jetParameter.getValOrVarNode() != null) {
184                                        c.getPrimaryConstructorParameterProperties().put(
185                                                jetParameter,
186                                                (PropertyDescriptor) resolveSession.resolveToDescriptor(jetParameter)
187                                        );
188                                    }
189                                }
190                            }
191    
192                            @Override
193                            public void visitClassObject(@NotNull JetClassObject classObject) {
194                                visitClassOrObject(classObject.getObjectDeclaration());
195                            }
196    
197                            @Override
198                            public void visitEnumEntry(@NotNull JetEnumEntry enumEntry) {
199                                visitClassOrObject(enumEntry);
200                            }
201    
202                            @Override
203                            public void visitObjectDeclaration(@NotNull JetObjectDeclaration declaration) {
204                                visitClassOrObject(declaration);
205                            }
206    
207                            @Override
208                            public void visitAnonymousInitializer(@NotNull JetClassInitializer initializer) {
209                                registerScope(c, resolveSession, initializer);
210                                JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(initializer, JetClassOrObject.class);
211                                c.getAnonymousInitializers().put(
212                                        initializer,
213                                        (ClassDescriptorWithResolutionScopes) resolveSession.resolveToDescriptor(classOrObject)
214                                );
215                            }
216    
217                            @Override
218                            public void visitTypedef(@NotNull JetTypedef typedef) {
219                                trace.report(UNSUPPORTED.on(typedef, "Typedefs are not supported"));
220                            }
221    
222                            @Override
223                            public void visitMultiDeclaration(@NotNull JetMultiDeclaration multiDeclaration) {
224                                // Ignore: multi-declarations are only allowed locally
225                            }
226    
227                            @Override
228                            public void visitNamedFunction(@NotNull JetNamedFunction function) {
229                                functions.add(function);
230                            }
231    
232                            @Override
233                            public void visitProperty(@NotNull JetProperty property) {
234                                properties.add(property);
235                            }
236                        }
237                );
238            }
239    
240            createFunctionDescriptors(c, resolveSession, functions);
241    
242            createPropertyDescriptors(c, resolveSession, topLevelFqNames, properties);
243    
244            resolveAllHeadersInClasses(c);
245    
246            declarationResolver.checkRedeclarationsInPackages(resolveSession, topLevelFqNames);
247            declarationResolver.checkRedeclarationsInInnerClassNames(c);
248    
249            overrideResolver.check(c);
250    
251            resolveImportsInAllFiles(c, resolveSession);
252    
253            overloadResolver.process(c);
254    
255            bodyResolver.resolveBodies(c);
256    
257    
258            return c;
259        }
260    
261        private static void resolveImportsInAllFiles(TopDownAnalysisContext c, KotlinCodeAnalyzer resolveSession) {
262            for (JetFile file : c.getFiles()) {
263                resolveAndCheckImports(file, resolveSession);
264            }
265    
266            for (JetScript script : c.getScripts().keySet()) {
267                resolveAndCheckImports(script.getContainingJetFile(), resolveSession);
268            }
269        }
270    
271        private static void resolveAllHeadersInClasses(TopDownAnalysisContext c) {
272            for (ClassDescriptorWithResolutionScopes classDescriptor : c.getAllClasses()) {
273                ((LazyClassDescriptor) classDescriptor).resolveMemberHeaders();
274            }
275        }
276    
277        private static void createPropertyDescriptors(
278                TopDownAnalysisContext c,
279                KotlinCodeAnalyzer resolveSession,
280                Multimap<FqName, JetElement> topLevelFqNames,
281                List<JetProperty> properties
282        ) {
283            for (JetProperty property : properties) {
284                PropertyDescriptor descriptor = (PropertyDescriptor) resolveSession.resolveToDescriptor(property);
285    
286                c.getProperties().put(property, descriptor);
287                registerTopLevelFqName(topLevelFqNames, property, descriptor);
288    
289                registerScope(c, resolveSession, property);
290                registerScope(c, resolveSession, property.getGetter());
291                registerScope(c, resolveSession, property.getSetter());
292            }
293        }
294    
295        private static void createFunctionDescriptors(
296                TopDownAnalysisContext c,
297                KotlinCodeAnalyzer resolveSession,
298                List<JetNamedFunction> functions
299        ) {
300            for (JetNamedFunction function : functions) {
301                c.getFunctions().put(
302                        function,
303                        (SimpleFunctionDescriptor) resolveSession.resolveToDescriptor(function)
304                );
305                registerScope(c, resolveSession, function);
306            }
307        }
308    
309        private static void resolveAndCheckImports(@NotNull JetFile file, @NotNull KotlinCodeAnalyzer resolveSession) {
310            LazyImportScope fileScope = resolveSession.getScopeProvider().getExplicitImportsScopeForFile(file);
311            fileScope.forceResolveAllContents();
312        }
313    
314        private static void registerScope(
315                @NotNull TopDownAnalysisContext c,
316                @NotNull KotlinCodeAnalyzer resolveSession,
317                @Nullable JetDeclaration declaration
318        ) {
319            if (declaration == null) return;
320            c.registerDeclaringScope(
321                    declaration,
322                    resolveSession.getScopeProvider().getResolutionScopeForDeclaration(declaration)
323            );
324        }
325    
326        private static void registerTopLevelFqName(
327                @NotNull Multimap<FqName, JetElement> topLevelFqNames,
328                @NotNull JetNamedDeclaration declaration,
329                @NotNull DeclarationDescriptor descriptor
330        ) {
331            if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
332                FqName fqName = declaration.getFqName();
333                if (fqName != null) {
334                    topLevelFqNames.put(fqName, declaration);
335                }
336            }
337        }
338    
339    }
340    
341