/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve;
import org.jetbrains.annotations.
NotNull;
import org.jetbrains.annotations.
Nullable;
import org.jetbrains.kotlin.builtins.
KotlinBuiltIns;
import org.jetbrains.kotlin.cfg.
ControlFlowInformationProvider;
import org.jetbrains.kotlin.config.
LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.
PropertyAccessorDescriptor;
import org.jetbrains.kotlin.descriptors.
PropertyDescriptor;
import org.jetbrains.kotlin.descriptors.
SimpleFunctionDescriptor;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.checkers.
PlatformDiagnosticSuppressor;
import org.jetbrains.kotlin.types.
KotlinType;
import java.util.
Map;
import static org.jetbrains.kotlin.types.
TypeUtils.
NO_EXPECTED_TYPE;
public class
ControlFlowAnalyzer {
private final
BindingTrace trace;
private final
KotlinBuiltIns builtIns;
private final
LanguageVersionSettings languageVersionSettings;
private final
PlatformDiagnosticSuppressor diagnosticSuppressor;
public
ControlFlowAnalyzer(
@
NotNull BindingTrace trace,
@
NotNull KotlinBuiltIns builtIns,
@
NotNull LanguageVersionSettings languageVersionSettings,
@
NotNull PlatformDiagnosticSuppressor diagnosticSuppressor
) {
this.
trace =
trace;
this.
builtIns =
builtIns;
this.
languageVersionSettings =
languageVersionSettings;
this.
diagnosticSuppressor =
diagnosticSuppressor;
}
public void
process(@
NotNull BodiesResolveContext c) {
for (
KtFile file :
c.
getFiles()) {
checkDeclarationContainer(
c,
file);
}
for (
KtClassOrObject aClass :
c.
getDeclaredClasses().
keySet()) {
checkDeclarationContainer(
c,
aClass);
}
for (
KtScript script :
c.
getScripts().
keySet()) {
checkDeclarationContainer(
c,
script);
}
for (
KtSecondaryConstructor constructor :
c.
getSecondaryConstructors().
keySet()) {
checkSecondaryConstructor(
constructor);
}
for (
Map.
Entry<
KtNamedFunction,
SimpleFunctionDescriptor>
entry :
c.
getFunctions().
entrySet()) {
KtNamedFunction function =
entry.
getKey();
SimpleFunctionDescriptor functionDescriptor =
entry.
getValue();
KotlinType expectedReturnType = !
function.
hasBlockBody() && !
function.
hasDeclaredReturnType()
?
NO_EXPECTED_TYPE
:
functionDescriptor.
getReturnType();
checkFunction(
c,
function,
expectedReturnType);
}
for (
Map.
Entry<
KtProperty,
PropertyDescriptor>
entry :
c.
getProperties().
entrySet()) {
KtProperty property =
entry.
getKey();
PropertyDescriptor propertyDescriptor =
entry.
getValue();
checkProperty(
c,
property,
propertyDescriptor);
}
}
private void
checkSecondaryConstructor(@
NotNull KtSecondaryConstructor constructor) {
ControlFlowInformationProvider controlFlowInformationProvider =
new
ControlFlowInformationProvider(
constructor,
trace,
languageVersionSettings,
diagnosticSuppressor);
controlFlowInformationProvider.
checkDeclaration();
controlFlowInformationProvider.
checkFunction(
builtIns.
getUnitType());
}
private void
checkDeclarationContainer(@
NotNull BodiesResolveContext c,
KtDeclarationContainer declarationContainer) {
// A pseudocode of class/object initialization corresponds to a class/object
// or initialization of properties corresponds to a package declared in a file
ControlFlowInformationProvider controlFlowInformationProvider = new
ControlFlowInformationProvider(
(
KtElement)
declarationContainer,
trace,
languageVersionSettings,
diagnosticSuppressor
);
if (
c.
getTopDownAnalysisMode().
isLocalDeclarations()) {
controlFlowInformationProvider.
checkForLocalClassOrObjectMode();
return;
}
controlFlowInformationProvider.
checkDeclaration();
}
private void
checkProperty(@
NotNull BodiesResolveContext c,
KtProperty property,
PropertyDescriptor propertyDescriptor) {
for (
KtPropertyAccessor accessor :
property.
getAccessors()) {
PropertyAccessorDescriptor accessorDescriptor =
accessor.
isGetter()
?
propertyDescriptor.
getGetter()
:
propertyDescriptor.
getSetter();
assert
accessorDescriptor != null : "no property accessor descriptor " +
accessor.
getText();
KotlinType returnType =
accessorDescriptor.
getReturnType();
checkFunction(
c,
accessor,
returnType);
}
}
private void
checkFunction(
@
NotNull BodiesResolveContext c, @
NotNull KtDeclarationWithBody function, @
Nullable KotlinType expectedReturnType
) {
ControlFlowInformationProvider controlFlowInformationProvider =
new
ControlFlowInformationProvider(
function,
trace,
languageVersionSettings,
diagnosticSuppressor);
if (
c.
getTopDownAnalysisMode().
isLocalDeclarations()) {
controlFlowInformationProvider.
checkForLocalClassOrObjectMode();
return;
}
controlFlowInformationProvider.
checkDeclaration();
controlFlowInformationProvider.
checkFunction(
expectedReturnType);
}
}