/*
* Copyright 2010-2016 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.types.expressions;
import com.intellij.psi.tree.IElementType;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.ScriptDescriptor;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.*;
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency;
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory;
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind;
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
import org.jetbrains.kotlin.resolve.scopes.TraceBasedLocalRedeclarationChecker;
import org.jetbrains.kotlin.types.ErrorUtils;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
import java.util.Iterator;
import java.util.List;
import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
import static org.jetbrains.kotlin.types.TypeUtils.UNIT_EXPECTED_TYPE;
import static org.jetbrains.kotlin.types.expressions.CoercionStrategy.COERCION_TO_UNIT;
public class ExpressionTypingServices {
private final ExpressionTypingFacade expressionTypingFacade;
private final ExpressionTypingComponents expressionTypingComponents;
@NotNull private final AnnotationChecker annotationChecker;
@NotNull private final StatementFilter statementFilter;
public ExpressionTypingServices(
@NotNull ExpressionTypingComponents components,
@NotNull AnnotationChecker annotationChecker,
@NotNull StatementFilter statementFilter,
@NotNull ExpressionTypingVisitorDispatcher.ForDeclarations facade
) {
this.expressionTypingComponents = components;
this.annotationChecker = annotationChecker;
this.statementFilter = statementFilter;
this.expressionTypingFacade = facade;
}
@NotNull public StatementFilter getStatementFilter() {
return statementFilter;
}
@NotNull
public KotlinType safeGetType(
@NotNull LexicalScope scope,
@NotNull KtExpression expression,
@NotNull KotlinType expectedType,
@NotNull DataFlowInfo dataFlowInfo,
@NotNull BindingTrace trace
) {
KotlinType type = getType(scope, expression, expectedType, dataFlowInfo, trace);
return type != null ? type : ErrorUtils.createErrorType("Type for " + expression.getText());
}
@NotNull
public KotlinTypeInfo getTypeInfo(
@NotNull LexicalScope scope,
@NotNull KtExpression expression,
@NotNull KotlinType expectedType,
@NotNull DataFlowInfo dataFlowInfo,
@NotNull BindingTrace trace,
boolean isStatement
) {
return getTypeInfo(scope, expression, expectedType, dataFlowInfo, trace, isStatement, expression, ContextDependency.INDEPENDENT);
}
@NotNull
public KotlinTypeInfo getTypeInfo(
@NotNull LexicalScope scope,
@NotNull KtExpression expression,
@NotNull KotlinType expectedType,
@NotNull DataFlowInfo dataFlowInfo,
@NotNull BindingTrace trace,
boolean isStatement,
@NotNull KtExpression contextExpression,
@NotNull ContextDependency contextDependency
) {
ExpressionTypingContext context = ExpressionTypingContext.newContext(
trace, scope, dataFlowInfo, expectedType, contextDependency, statementFilter
);
if (contextExpression != expression) {
context = context.replaceExpressionContextProvider(arg -> arg == expression ? contextExpression : null);
}
return expressionTypingFacade.getTypeInfo(expression, context, isStatement);
}
@NotNull
public KotlinTypeInfo getTypeInfo(@NotNull KtExpression expression, @NotNull ResolutionContext resolutionContext) {
return expressionTypingFacade.getTypeInfo(expression, ExpressionTypingContext.newContext(resolutionContext));
}
@Nullable
public KotlinType getType(
@NotNull LexicalScope scope,
@NotNull KtExpression expression,
@NotNull KotlinType expectedType,
@NotNull DataFlowInfo dataFlowInfo,
@NotNull BindingTrace trace
) {
return getTypeInfo(scope, expression, expectedType, dataFlowInfo, trace, false).getType();
}
/////////////////////////////////////////////////////////
public void checkFunctionReturnType(
@NotNull LexicalScope functionInnerScope,
@NotNull KtDeclarationWithBody function,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull DataFlowInfo dataFlowInfo,
@Nullable KotlinType expectedReturnType,
BindingTrace trace
) {
if (expectedReturnType == null) {
expectedReturnType = functionDescriptor.getReturnType();
if (!function.hasBlockBody() && !function.hasDeclaredReturnType()) {
expectedReturnType = NO_EXPECTED_TYPE;
}
}
checkFunctionReturnType(function, ExpressionTypingContext.newContext(
trace,
functionInnerScope, dataFlowInfo, expectedReturnType != null ? expectedReturnType : NO_EXPECTED_TYPE
));
}
/*package*/ void checkFunctionReturnType(KtDeclarationWithBody function, ExpressionTypingContext context) {
KtExpression bodyExpression = function.getBodyExpression();
if (bodyExpression == null) return;
boolean blockBody = function.hasBlockBody();
ExpressionTypingContext newContext =
blockBody
? context.replaceExpectedType(NO_EXPECTED_TYPE)
: context;
expressionTypingFacade.getTypeInfo(bodyExpression, newContext, blockBody);
}
@NotNull
public KotlinTypeInfo getBlockReturnedType(KtBlockExpression expression, ExpressionTypingContext context, boolean isStatement) {
return getBlockReturnedType(expression, isStatement ? CoercionStrategy.COERCION_TO_UNIT : CoercionStrategy.NO_COERCION, context);
}
@NotNull
public KotlinTypeInfo getBlockReturnedType(
@NotNull KtBlockExpression expression,
@NotNull CoercionStrategy coercionStrategyForLastExpression,
@NotNull ExpressionTypingContext context
) {
List<KtExpression> block = StatementFilterKt.filterStatements(statementFilter, expression);
DeclarationDescriptor containingDescriptor = context.scope.getOwnerDescriptor();
TraceBasedLocalRedeclarationChecker redeclarationChecker
= new TraceBasedLocalRedeclarationChecker(context.trace, expressionTypingComponents.overloadChecker);
LexicalWritableScope scope = new LexicalWritableScope(context.scope, containingDescriptor, false, redeclarationChecker,
LexicalScopeKind.CODE_BLOCK);
KotlinTypeInfo r;
if (block.isEmpty()) {
r = expressionTypingComponents.dataFlowAnalyzer
.createCheckedTypeInfo(expressionTypingComponents.builtIns.getUnitType(), context, expression);
}
else {
r = getBlockReturnedTypeWithWritableScope(scope, block, coercionStrategyForLastExpression,
context.replaceStatementFilter(statementFilter));
}
scope.freeze();
if (containingDescriptor instanceof ScriptDescriptor) {
context.trace.record(BindingContext.SCRIPT_SCOPE, (ScriptDescriptor) containingDescriptor, scope);
}
return r;
}
@NotNull
public KotlinType getBodyExpressionType(
@NotNull BindingTrace trace,
@NotNull LexicalScope outerScope,
@NotNull DataFlowInfo dataFlowInfo,
@NotNull KtDeclarationWithBody function,
@NotNull FunctionDescriptor functionDescriptor
) {
KtExpression bodyExpression = function.getBodyExpression();
assert bodyExpression != null;
LexicalScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace,
expressionTypingComponents.overloadChecker);
ExpressionTypingContext context = ExpressionTypingContext.newContext(
trace, functionInnerScope, dataFlowInfo, NO_EXPECTED_TYPE
);
KotlinTypeInfo typeInfo = expressionTypingFacade.getTypeInfo(bodyExpression, context, function.hasBlockBody());
KotlinType type = typeInfo.getType();
if (type != null) {
return type;
}
else {
return ErrorUtils.createErrorType("Error function type");
}
}
/**
* Visits block statements propagating data flow information from the first to the last.
* Determines block returned type and data flow information at the end of the block AND
* at the nearest jump point from the block beginning.
*/
/*package*/ KotlinTypeInfo getBlockReturnedTypeWithWritableScope(
@NotNull LexicalWritableScope scope,
@NotNull List<? extends KtElement> block,
@NotNull CoercionStrategy coercionStrategyForLastExpression,
@NotNull ExpressionTypingContext context
) {
if (block.isEmpty()) {
return TypeInfoFactoryKt.createTypeInfo(expressionTypingComponents.builtIns.getUnitType(), context);
}
ExpressionTypingInternals blockLevelVisitor = new ExpressionTypingVisitorDispatcher.ForBlock(
expressionTypingComponents, annotationChecker, scope);
ExpressionTypingContext newContext = context.replaceScope(scope).replaceExpectedType(NO_EXPECTED_TYPE);
KotlinTypeInfo result = TypeInfoFactoryKt.noTypeInfo(context);
// Jump point data flow info
DataFlowInfo beforeJumpInfo = newContext.dataFlowInfo;
boolean jumpOutPossible = false;
for (Iterator<? extends KtElement> iterator = block.iterator(); iterator.hasNext(); ) {
KtElement statement = iterator.next();
if (!(statement instanceof KtExpression)) {
continue;
}
KtExpression statementExpression = (KtExpression) statement;
if (!iterator.hasNext()) {
result = getTypeOfLastExpressionInBlock(
statementExpression, newContext.replaceExpectedType(context.expectedType), coercionStrategyForLastExpression,
blockLevelVisitor);
if (result.getType() != null && statementExpression.getParent() instanceof KtBlockExpression) {
DataFlowValue lastExpressionValue = DataFlowValueFactory.createDataFlowValue(
statementExpression, result.getType(), context);
DataFlowValue blockExpressionValue = DataFlowValueFactory.createDataFlowValue(
(KtBlockExpression) statementExpression.getParent(), result.getType(), context);
result = result.replaceDataFlowInfo(result.getDataFlowInfo().assign(blockExpressionValue, lastExpressionValue,
expressionTypingComponents.languageVersionSettings));
}
}
else {
result = blockLevelVisitor
.getTypeInfo(statementExpression, newContext.replaceContextDependency(ContextDependency.INDEPENDENT), true);
}
DataFlowInfo newDataFlowInfo = result.getDataFlowInfo();
// If jump is not possible, we take new data flow info before jump
if (!jumpOutPossible) {
beforeJumpInfo = result.getJumpFlowInfo();
jumpOutPossible = result.getJumpOutPossible();
}
if (newDataFlowInfo != context.dataFlowInfo) {
newContext = newContext.replaceDataFlowInfo(newDataFlowInfo);
// We take current data flow info if jump there is not possible
}
blockLevelVisitor = new ExpressionTypingVisitorDispatcher.ForBlock(expressionTypingComponents, annotationChecker, scope);
}
return result.replaceJumpOutPossible(jumpOutPossible).replaceJumpFlowInfo(beforeJumpInfo);
}
private KotlinTypeInfo getTypeOfLastExpressionInBlock(
@NotNull KtExpression statementExpression,
@NotNull ExpressionTypingContext context,
@NotNull CoercionStrategy coercionStrategyForLastExpression,
@NotNull ExpressionTypingInternals blockLevelVisitor
) {
if (context.expectedType != NO_EXPECTED_TYPE) {
KotlinType expectedType;
if (context.expectedType == UNIT_EXPECTED_TYPE ||//the first check is necessary to avoid invocation 'isUnit(UNIT_EXPECTED_TYPE)'
(coercionStrategyForLastExpression == COERCION_TO_UNIT && KotlinBuiltIns.isUnit(context.expectedType))) {
expectedType = UNIT_EXPECTED_TYPE;
}
else {
expectedType = context.expectedType;
}
return blockLevelVisitor.getTypeInfo(statementExpression, context.replaceExpectedType(expectedType), true);
}
KotlinTypeInfo result = blockLevelVisitor.getTypeInfo(statementExpression, context, true);
if (coercionStrategyForLastExpression == COERCION_TO_UNIT) {
boolean mightBeUnit = false;
if (statementExpression instanceof KtDeclaration) {
if (!(statementExpression instanceof KtNamedFunction) || statementExpression.getName() != null) {
mightBeUnit = true;
}
}
if (statementExpression instanceof KtBinaryExpression) {
KtBinaryExpression binaryExpression = (KtBinaryExpression) statementExpression;
IElementType operationType = binaryExpression.getOperationToken();
//noinspection SuspiciousMethodCalls
if (operationType == KtTokens.EQ || OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
mightBeUnit = true;
}
}
if (mightBeUnit) {
// ExpressionTypingVisitorForStatements should return only null or Unit for declarations and assignments,
// but (for correct assignment / initialization analysis) data flow info must be preserved
assert result.getType() == null || KotlinBuiltIns.isUnit(result.getType());
result = result.replaceType(expressionTypingComponents.builtIns.getUnitType());
}
}
return result;
}
}