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