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