001    /*
002     * Copyright 2010-2015 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.kotlin.resolve;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
022    import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider;
023    import org.jetbrains.kotlin.config.LanguageVersionSettings;
024    import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor;
025    import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
026    import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor;
027    import org.jetbrains.kotlin.psi.*;
028    import org.jetbrains.kotlin.types.KotlinType;
029    
030    import java.util.Map;
031    
032    import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
033    
034    public class ControlFlowAnalyzer {
035        @NotNull private final BindingTrace trace;
036        @NotNull private final KotlinBuiltIns builtIns;
037        @NotNull private final LanguageVersionSettings languageVersionSettings;
038    
039        public ControlFlowAnalyzer(
040                @NotNull BindingTrace trace, @NotNull KotlinBuiltIns builtIns, @NotNull LanguageVersionSettings languageVersionSettings
041        ) {
042            this.trace = trace;
043            this.builtIns = builtIns;
044            this.languageVersionSettings = languageVersionSettings;
045        }
046    
047        public void process(@NotNull BodiesResolveContext c) {
048            for (KtFile file : c.getFiles()) {
049                checkDeclarationContainer(c, file);
050            }
051            for (KtClassOrObject aClass : c.getDeclaredClasses().keySet()) {
052                checkDeclarationContainer(c, aClass);
053            }
054            for (KtScript script : c.getScripts().keySet()) {
055                checkDeclarationContainer(c, script);
056            }
057            for (KtSecondaryConstructor constructor : c.getSecondaryConstructors().keySet()) {
058                checkSecondaryConstructor(constructor);
059            }
060            for (Map.Entry<KtNamedFunction, SimpleFunctionDescriptor> entry : c.getFunctions().entrySet()) {
061                KtNamedFunction function = entry.getKey();
062                SimpleFunctionDescriptor functionDescriptor = entry.getValue();
063                KotlinType expectedReturnType = !function.hasBlockBody() && !function.hasDeclaredReturnType()
064                                                   ? NO_EXPECTED_TYPE
065                                                   : functionDescriptor.getReturnType();
066                checkFunction(c, function, expectedReturnType);
067            }
068            for (Map.Entry<KtProperty, PropertyDescriptor> entry : c.getProperties().entrySet()) {
069                KtProperty property = entry.getKey();
070                PropertyDescriptor propertyDescriptor = entry.getValue();
071                checkProperty(c, property, propertyDescriptor);
072            }
073        }
074    
075        private void checkSecondaryConstructor(@NotNull KtSecondaryConstructor constructor) {
076            ControlFlowInformationProvider controlFlowInformationProvider =
077                    new ControlFlowInformationProvider(constructor, trace, languageVersionSettings);
078            controlFlowInformationProvider.checkDeclaration();
079            controlFlowInformationProvider.checkFunction(builtIns.getUnitType());
080        }
081    
082        private void checkDeclarationContainer(@NotNull BodiesResolveContext c, KtDeclarationContainer declarationContainer) {
083            // A pseudocode of class/object initialization corresponds to a class/object
084            // or initialization of properties corresponds to a package declared in a file
085            ControlFlowInformationProvider controlFlowInformationProvider =
086                    new ControlFlowInformationProvider((KtElement) declarationContainer, trace, languageVersionSettings);
087            if (c.getTopDownAnalysisMode().isLocalDeclarations()) {
088                controlFlowInformationProvider.checkForLocalClassOrObjectMode();
089                return;
090            }
091            controlFlowInformationProvider.checkDeclaration();
092        }
093    
094        private void checkProperty(@NotNull BodiesResolveContext c, KtProperty property, PropertyDescriptor propertyDescriptor) {
095            for (KtPropertyAccessor accessor : property.getAccessors()) {
096                PropertyAccessorDescriptor accessorDescriptor = accessor.isGetter()
097                                                                ? propertyDescriptor.getGetter()
098                                                                : propertyDescriptor.getSetter();
099                assert accessorDescriptor != null : "no property accessor descriptor " + accessor.getText();
100                KotlinType returnType = accessorDescriptor.getReturnType();
101                checkFunction(c, accessor, returnType);
102            }
103        }
104    
105        private void checkFunction(@NotNull BodiesResolveContext c, @NotNull KtDeclarationWithBody function, @Nullable KotlinType expectedReturnType) {
106            if (!function.hasBody()) return;
107            ControlFlowInformationProvider controlFlowInformationProvider =
108                    new ControlFlowInformationProvider(function, trace, languageVersionSettings);
109            if (c.getTopDownAnalysisMode().isLocalDeclarations()) {
110                controlFlowInformationProvider.checkForLocalClassOrObjectMode();
111                return;
112            }
113            controlFlowInformationProvider.checkDeclaration();
114            controlFlowInformationProvider.checkFunction(expectedReturnType);
115        }
116    }