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