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 org.jetbrains.annotations.NotNull;
020    import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider;
021    import org.jetbrains.jet.lang.descriptors.PropertyAccessorDescriptor;
022    import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
023    import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
024    import org.jetbrains.jet.lang.psi.*;
025    import org.jetbrains.jet.lang.types.JetType;
026    
027    import javax.inject.Inject;
028    import java.util.List;
029    import java.util.Map;
030    
031    import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
032    
033    public class ControlFlowAnalyzer {
034        private TopDownAnalysisParameters topDownAnalysisParameters;
035        private BindingTrace trace;
036    
037        @Inject
038        public void setTopDownAnalysisParameters(TopDownAnalysisParameters topDownAnalysisParameters) {
039            this.topDownAnalysisParameters = topDownAnalysisParameters;
040        }
041    
042        @Inject
043        public void setTrace(BindingTrace trace) {
044            this.trace = trace;
045        }
046    
047        public void process(@NotNull BodiesResolveContext bodiesResolveContext) {
048            for (JetFile file : bodiesResolveContext.getFiles()) {
049                if (!bodiesResolveContext.completeAnalysisNeeded(file)) continue;
050                checkDeclarationContainer(file);
051            }
052            for (JetClass aClass : bodiesResolveContext.getClasses().keySet()) {
053                if (!bodiesResolveContext.completeAnalysisNeeded(aClass)) continue;
054                checkDeclarationContainer(aClass);
055            }
056            for (JetObjectDeclaration objectDeclaration : bodiesResolveContext.getObjects().keySet()) {
057                if (!bodiesResolveContext.completeAnalysisNeeded(objectDeclaration)) continue;
058                checkDeclarationContainer(objectDeclaration);
059            }
060            for (Map.Entry<JetNamedFunction, SimpleFunctionDescriptor> entry : bodiesResolveContext.getFunctions().entrySet()) {
061                JetNamedFunction function = entry.getKey();
062                SimpleFunctionDescriptor functionDescriptor = entry.getValue();
063                if (!bodiesResolveContext.completeAnalysisNeeded(function)) continue;
064                JetType expectedReturnType = !function.hasBlockBody() && !function.hasDeclaredReturnType()
065                                                   ? NO_EXPECTED_TYPE
066                                                   : functionDescriptor.getReturnType();
067                checkFunction(function, expectedReturnType);
068            }
069            for (Map.Entry<JetProperty, PropertyDescriptor> entry : bodiesResolveContext.getProperties().entrySet()) {
070                JetProperty property = entry.getKey();
071                if (!bodiesResolveContext.completeAnalysisNeeded(property)) continue;
072                PropertyDescriptor propertyDescriptor = entry.getValue();
073                checkProperty(property, propertyDescriptor);
074            }
075        }
076    
077        private void checkDeclarationContainer(JetDeclarationContainer declarationContainer) {
078            // A pseudocode of class/object initialization corresponds to a class/object
079            // or initialization of properties corresponds to a package declared in a file
080            JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetElement) declarationContainer, trace);
081            flowInformationProvider.recordInitializedVariables();
082    
083            if (topDownAnalysisParameters.isDeclaredLocally()) return;
084    
085            flowInformationProvider.markUninitializedVariables();
086        }
087    
088        private void checkProperty(JetProperty property, PropertyDescriptor propertyDescriptor) {
089            for (JetPropertyAccessor accessor : property.getAccessors()) {
090                PropertyAccessorDescriptor accessorDescriptor = accessor.isGetter()
091                                                                ? propertyDescriptor.getGetter()
092                                                                : propertyDescriptor.getSetter();
093                assert accessorDescriptor != null;
094                checkFunction(accessor, accessorDescriptor.getReturnType());
095            }
096        }
097    
098        private void checkFunction(JetDeclarationWithBody function, @NotNull JetType expectedReturnType) {
099            assert function instanceof JetDeclaration;
100    
101            JetExpression bodyExpression = function.getBodyExpression();
102            if (bodyExpression == null) return;
103            JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetDeclaration) function, trace);
104    
105            boolean isPropertyAccessor = function instanceof JetPropertyAccessor;
106            if (!isPropertyAccessor) {
107                flowInformationProvider.recordInitializedVariables();
108            }
109    
110            if (topDownAnalysisParameters.isDeclaredLocally()) return;
111    
112            flowInformationProvider.checkDefiniteReturn(expectedReturnType);
113    
114            if (!isPropertyAccessor) {
115                // Property accessor is checked through initialization of a class/object or package properties (at 'checkDeclarationContainer')
116                flowInformationProvider.markUninitializedVariables();
117            }
118    
119            flowInformationProvider.markUnusedVariables();
120    
121            flowInformationProvider.markUnusedLiteralsInBlock();
122        }
123    }