/*
* 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.types.expressions;
import com.google.common.collect.
Sets;
import com.intellij.openapi.util.
Ref;
import com.intellij.psi.tree.
IElementType;
import org.jetbrains.annotations.
NotNull;
import org.jetbrains.annotations.
Nullable;
import org.jetbrains.kotlin.builtins.
KotlinBuiltIns;
import org.jetbrains.kotlin.config.
LanguageFeature;
import org.jetbrains.kotlin.config.
LanguageVersionSettings;
import org.jetbrains.kotlin.contracts.
EffectSystem;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.
DiagnosticUtilsKt;
import org.jetbrains.kotlin.incremental.
KotlinLookupLocation;
import org.jetbrains.kotlin.lexer.
KtTokens;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.
BindingContext;
import org.jetbrains.kotlin.resolve.
BindingTrace;
import org.jetbrains.kotlin.resolve.
DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.checkers.
AdditionalTypeChecker;
import org.jetbrains.kotlin.resolve.calls.context.
ResolutionContext;
import org.jetbrains.kotlin.resolve.calls.smartcasts.*;
import org.jetbrains.kotlin.resolve.constants.*;
import org.jetbrains.kotlin.resolve.constants.evaluate.
ConstantExpressionEvaluator;
import org.jetbrains.kotlin.types.
KotlinType;
import org.jetbrains.kotlin.types.
KotlinTypeKt;
import org.jetbrains.kotlin.types.
TypeConstructor;
import org.jetbrains.kotlin.types.
TypeUtils;
import org.jetbrains.kotlin.types.checker.
KotlinTypeChecker;
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.
TypeInfoFactoryKt;
import org.jetbrains.kotlin.util.
OperatorNameConventions;
import java.util.
Collection;
import static org.jetbrains.kotlin.diagnostics.
Errors.*;
import static org.jetbrains.kotlin.resolve.calls.context.
ContextDependency.
INDEPENDENT;
import static org.jetbrains.kotlin.types.
TypeUtils.*;
public class
DataFlowAnalyzer {
private final
Iterable<
AdditionalTypeChecker>
additionalTypeCheckers;
private final
ConstantExpressionEvaluator constantExpressionEvaluator;
private final
ModuleDescriptor module;
private final
KotlinBuiltIns builtIns;
private final
ExpressionTypingFacade facade;
private final
LanguageVersionSettings languageVersionSettings;
private final
EffectSystem effectSystem;
private final
DataFlowValueFactory dataFlowValueFactory;
public
DataFlowAnalyzer(
@
NotNull Iterable<
AdditionalTypeChecker>
additionalTypeCheckers,
@
NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
@
NotNull ModuleDescriptor module,
@
NotNull KotlinBuiltIns builtIns,
@
NotNull ExpressionTypingFacade facade,
@
NotNull LanguageVersionSettings languageVersionSettings,
@
NotNull EffectSystem effectSystem,
@
NotNull DataFlowValueFactory factory
) {
this.
additionalTypeCheckers =
additionalTypeCheckers;
this.
constantExpressionEvaluator =
constantExpressionEvaluator;
this.
module =
module;
this.
builtIns =
builtIns;
this.
facade =
facade;
this.
languageVersionSettings =
languageVersionSettings;
this.
effectSystem =
effectSystem;
this.
dataFlowValueFactory =
factory;
}
// NB: use this method only for functions from 'Any'
@
Nullable
private
FunctionDescriptor getOverriddenDescriptorFromClass(@
NotNull FunctionDescriptor descriptor) {
if (
descriptor.
getKind() !=
CallableMemberDescriptor.
Kind.
FAKE_OVERRIDE) return
descriptor;
Collection<? extends
FunctionDescriptor>
overriddenDescriptors =
descriptor.
getOverriddenDescriptors();
if (
overriddenDescriptors.
isEmpty()) return
descriptor;
for (
FunctionDescriptor overridden :
overriddenDescriptors) {
DeclarationDescriptor containingDeclaration =
overridden.
getContainingDeclaration();
if (
DescriptorUtils.
isClass(
containingDeclaration) ||
DescriptorUtils.
isObject(
containingDeclaration)) {
// Exactly one class should exist in the list
return
getOverriddenDescriptorFromClass(
overridden);
}
}
return null;
}
private boolean
typeHasOverriddenEquals(@
NotNull KotlinType type, @
NotNull KtElement lookupElement) {
Collection<
SimpleFunctionDescriptor>
members =
type.
getMemberScope().
getContributedFunctions(
OperatorNameConventions.
EQUALS, new
KotlinLookupLocation(
lookupElement));
for (
FunctionDescriptor member :
members) {
KotlinType returnType =
member.
getReturnType();
if (
returnType == null || !
KotlinBuiltIns.
isBoolean(
returnType)) continue;
if (
member.
getValueParameters().
size() != 1) continue;
KotlinType parameterType =
member.
getValueParameters().
iterator().
next().
getType();
if (!
KotlinBuiltIns.
isNullableAny(
parameterType)) continue;
FunctionDescriptor fromSuperClass =
getOverriddenDescriptorFromClass(
member);
if (
fromSuperClass == null) return false;
ClassifierDescriptor superClassDescriptor = (
ClassifierDescriptor)
fromSuperClass.
getContainingDeclaration();
// We should have override fun in class other than Any (to prove unknown behaviour)
return !
KotlinBuiltIns.
isAnyOrNullableAny(
superClassDescriptor.
getDefaultType());
}
return false;
}
// Returns true if we can prove that 'type' has equals method from 'Any' base type
public boolean
typeHasEqualsFromAny(@
NotNull KotlinType type, @
NotNull KtElement lookupElement) {
TypeConstructor constructor =
type.
getConstructor();
// Subtypes can override equals for non-final types
if (!
constructor.
isFinal()) return false;
// check whether 'equals' is overriden
return !
typeHasOverriddenEquals(
type,
lookupElement);
}
@
NotNull
public
DataFlowInfo extractDataFlowInfoFromCondition(
@
Nullable KtExpression condition,
boolean
conditionValue,
ExpressionTypingContext context
) {
if (
condition == null) return
context.
dataFlowInfo;
Ref<
DataFlowInfo>
result = new
Ref<>(null);
condition.
accept(new
KtVisitorVoid() {
@
Override
public void
visitIsExpression(@
NotNull KtIsExpression expression) {
if (
conditionValue && !
expression.
isNegated() || !
conditionValue &&
expression.
isNegated()) {
result.
set(
context.
trace.
get(
BindingContext.
DATAFLOW_INFO_AFTER_CONDITION,
expression));
}
}
@
Override
public void
visitBinaryExpression(@
NotNull KtBinaryExpression expression) {
IElementType operationToken =
expression.
getOperationToken();
if (
OperatorConventions.
BOOLEAN_OPERATIONS.
containsKey(
operationToken)) {
DataFlowInfo dataFlowInfo =
extractDataFlowInfoFromCondition(
expression.
getLeft(),
conditionValue,
context);
KtExpression expressionRight =
expression.
getRight();
if (
expressionRight != null) {
boolean
and =
operationToken ==
KtTokens.
ANDAND;
DataFlowInfo rightInfo =
extractDataFlowInfoFromCondition(
expressionRight,
conditionValue,
and ==
conditionValue ?
context.
replaceDataFlowInfo(
dataFlowInfo) :
context
);
if (
and ==
conditionValue) { // this means: and && conditionValue || !and && !conditionValue
dataFlowInfo =
dataFlowInfo.
and(
rightInfo);
}
else {
dataFlowInfo =
dataFlowInfo.
or(
rightInfo);
}
}
result.
set(
dataFlowInfo);
}
else {
DataFlowInfo expressionFlowInfo =
facade.
getTypeInfo(
expression,
context).
getDataFlowInfo();
KtExpression left =
expression.
getLeft();
if (
left == null) return;
KtExpression right =
expression.
getRight();
if (
right == null) return;
KotlinType lhsType =
context.
trace.
getBindingContext().
getType(
left);
if (
lhsType == null) return;
KotlinType rhsType =
context.
trace.
getBindingContext().
getType(
right);
if (
rhsType == null) return;
DataFlowValue leftValue =
dataFlowValueFactory.
createDataFlowValue(
left,
lhsType,
context);
DataFlowValue rightValue =
dataFlowValueFactory.
createDataFlowValue(
right,
rhsType,
context);
Boolean equals = null;
if (
operationToken ==
KtTokens.
EQEQ ||
operationToken ==
KtTokens.
EQEQEQ) {
equals = true;
}
else if (
operationToken ==
KtTokens.
EXCLEQ ||
operationToken ==
KtTokens.
EXCLEQEQEQ) {
equals = false;
}
else if (
operationToken ==
KtTokens.
ELVIS &&
languageVersionSettings.
supportsFeature(
LanguageFeature.
BooleanElvisBoundSmartCasts) &&
right instanceof
KtConstantExpression &&
KotlinBuiltIns.
isBoolean(
rhsType)) {
// ?: false is equivalent to == true, ?: true is equivalent to != false
equals =
KtPsiUtil.
isFalseConstant(
right);
}
if (
equals != null) {
if (
equals ==
conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue
boolean
identityEquals =
operationToken ==
KtTokens.
EQEQEQ ||
operationToken ==
KtTokens.
EXCLEQEQEQ ||
typeHasEqualsFromAny(
lhsType,
condition);
result.
set(
context.
dataFlowInfo
.
equate(
leftValue,
rightValue,
identityEquals,
languageVersionSettings)
.
and(
expressionFlowInfo));
}
else {
result.
set(
context.
dataFlowInfo
.
disequate(
leftValue,
rightValue,
languageVersionSettings)
.
and(
expressionFlowInfo));
}
}
else {
result.
set(
expressionFlowInfo);
}
}
}
@
Override
public void
visitUnaryExpression(@
NotNull KtUnaryExpression expression) {
IElementType operationTokenType =
expression.
getOperationReference().
getReferencedNameElementType();
if (
operationTokenType ==
KtTokens.
EXCL) {
KtExpression baseExpression =
expression.
getBaseExpression();
if (
baseExpression != null) {
result.
set(
extractDataFlowInfoFromCondition(
baseExpression, !
conditionValue,
context));
}
}
else {
visitExpression(
expression);
}
}
@
Override
public void
visitExpression(@
NotNull KtExpression expression) {
// In fact, everything is taken from trace here
result.
set(
facade.
getTypeInfo(
expression,
context).
getDataFlowInfo());
}
@
Override
public void
visitParenthesizedExpression(@
NotNull KtParenthesizedExpression expression) {
KtExpression body =
expression.
getExpression();
if (
body != null) {
body.
accept(this);
}
}
});
DataFlowInfo infoFromEffectSystem =
effectSystem.
extractDataFlowInfoFromCondition(
condition,
conditionValue,
context.
trace,
DescriptorUtils.
getContainingModule(
context.
scope.
getOwnerDescriptor())
);
if (
result.
get() == null) {
return
context.
dataFlowInfo.
and(
infoFromEffectSystem);
}
return
context.
dataFlowInfo.
and(
result.
get()).
and(
infoFromEffectSystem);
}
@
Nullable
public
KotlinType checkType(@
Nullable KotlinType expressionType, @
NotNull KtExpression expression, @
NotNull ResolutionContext context) {
return
checkType(
expressionType,
expression,
context, null, true);
}
@
Nullable
public
KotlinType checkType(
@
Nullable KotlinType expressionType,
@
NotNull KtExpression expression,
@
NotNull ResolutionContext context,
boolean
reportErrorForTypeMismatch
) {
return
checkType(
expressionType,
expression,
context, null,
reportErrorForTypeMismatch);
}
@
NotNull
public
KotlinTypeInfo checkType(@
NotNull KotlinTypeInfo typeInfo, @
NotNull KtExpression expression, @
NotNull ResolutionContext context) {
return
typeInfo.
replaceType(
checkType(
typeInfo.
getType(),
expression,
context));
}
@
NotNull
private
KotlinType checkTypeInternal(
@
NotNull KotlinType expressionType,
@
NotNull KtExpression expression,
@
NotNull ResolutionContext c,
@
NotNull Ref<
Boolean>
hasError,
boolean
reportErrorForTypeMismatch
) {
if (
noExpectedType(
c.
expectedType) || !
c.
expectedType.
getConstructor().
isDenotable() ||
KotlinTypeChecker.
DEFAULT.
isSubtypeOf(
expressionType,
c.
expectedType)) {
return
expressionType;
}
if (
expression instanceof
KtConstantExpression &&
reportErrorForTypeMismatch) {
ConstantValue<?>
constantValue =
constantExpressionEvaluator.
evaluateToConstantValue(
expression,
c.
trace,
c.
expectedType);
boolean
error = new
CompileTimeConstantChecker(
c,
module, true)
.
checkConstantExpressionType(
constantValue, (
KtConstantExpression)
expression,
c.
expectedType);
hasError.
set(
error);
return
expressionType;
}
if (
expression instanceof
KtWhenExpression) {
// No need in additional check because type mismatch is already reported for entries
return
expressionType;
}
SmartCastResult castResult =
checkPossibleCast(
expressionType,
expression,
c);
if (
castResult != null) return
castResult.
getResultType();
if (
reportErrorForTypeMismatch &&
!
DiagnosticUtilsKt.
reportTypeMismatchDueToTypeProjection(
c,
expression,
c.
expectedType,
expressionType) &&
!
DiagnosticUtilsKt.
reportTypeMismatchDueToScalaLikeNamedFunctionSyntax(
c,
expression,
c.
expectedType,
expressionType)) {
c.
trace.
report(
TYPE_MISMATCH.
on(
expression,
c.
expectedType,
expressionType));
}
hasError.
set(true);
return
expressionType;
}
@
Nullable
public
KotlinType checkType(
@
Nullable KotlinType expressionType,
@
NotNull KtExpression expressionToCheck,
@
NotNull ResolutionContext c,
@
Nullable Ref<
Boolean>
hasError,
boolean
reportErrorForTypeMismatch
) {
if (
hasError == null) {
hasError =
Ref.
create(false);
}
else {
hasError.
set(false);
}
KtExpression expression =
KtPsiUtil.
safeDeparenthesize(
expressionToCheck);
recordExpectedType(
c.
trace,
expression,
c.
expectedType);
if (
expressionType == null) return null;
KotlinType result =
checkTypeInternal(
expressionType,
expression,
c,
hasError,
reportErrorForTypeMismatch);
if (
Boolean.
FALSE.
equals(
hasError.
get())) {
for (
AdditionalTypeChecker checker :
additionalTypeCheckers) {
checker.
checkType(
expression,
expressionType,
result,
c);
}
}
return
result;
}
@
Nullable
public
SmartCastResult checkPossibleCast(
@
NotNull KotlinType expressionType,
@
NotNull KtExpression expression,
@
NotNull ResolutionContext c
) {
DataFlowValue dataFlowValue =
dataFlowValueFactory.
createDataFlowValue(
expression,
expressionType,
c);
return
SmartCastManager.
Companion.
checkAndRecordPossibleCast(
dataFlowValue,
c.
expectedType,
expression,
c, null, false);
}
public void
recordExpectedType(@
NotNull BindingTrace trace, @
NotNull KtExpression expression, @
NotNull KotlinType expectedType) {
if (
expectedType !=
NO_EXPECTED_TYPE) {
KotlinType normalizeExpectedType =
expectedType ==
UNIT_EXPECTED_TYPE ?
builtIns.
getUnitType() :
expectedType;
trace.
record(
BindingContext.
EXPECTED_EXPRESSION_TYPE,
expression,
normalizeExpectedType);
}
}
@
Nullable
public
KotlinType checkStatementType(@
NotNull KtExpression expression, @
NotNull ResolutionContext context) {
if (!
noExpectedType(
context.
expectedType) && !
KotlinBuiltIns.
isUnit(
context.
expectedType) &&
!
KotlinTypeKt.
isError(
context.
expectedType)) {
context.
trace.
report(
EXPECTED_TYPE_MISMATCH.
on(
expression,
context.
expectedType));
return null;
}
return
builtIns.
getUnitType();
}
@
NotNull
public
KotlinTypeInfo illegalStatementType(@
NotNull KtExpression expression, @
NotNull ExpressionTypingContext context, @
NotNull ExpressionTypingInternals facade) {
facade.
checkStatementType(
expression,
context.
replaceExpectedType(
TypeUtils.
NO_EXPECTED_TYPE).
replaceContextDependency(
INDEPENDENT));
context.
trace.
report(
EXPRESSION_EXPECTED.
on(
expression,
expression));
return
TypeInfoFactoryKt.
noTypeInfo(
context);
}
@
NotNull
public static
Collection<
KotlinType>
getAllPossibleTypes(
@
NotNull KtExpression expression,
@
NotNull KotlinType type,
@
NotNull ResolutionContext c
) {
DataFlowValue dataFlowValue =
c.
dataFlowValueFactory.
createDataFlowValue(
expression,
type,
c);
return
getAllPossibleTypes(
type,
c,
dataFlowValue,
c.
languageVersionSettings);
}
@
NotNull
public static
Collection<
KotlinType>
getAllPossibleTypes(
@
NotNull KotlinType type,
@
NotNull ResolutionContext c,
@
NotNull DataFlowValue dataFlowValue,
@
NotNull LanguageVersionSettings languageVersionSettings
) {
Collection<
KotlinType>
possibleTypes =
Sets.
newHashSet(
type);
possibleTypes.
addAll(
c.
dataFlowInfo.
getStableTypes(
dataFlowValue,
languageVersionSettings));
return
possibleTypes;
}
@
NotNull
public
KotlinTypeInfo createCheckedTypeInfo(
@
Nullable KotlinType type,
@
NotNull ResolutionContext<?>
context,
@
NotNull KtExpression expression
) {
return
checkType(
TypeInfoFactoryKt.
createTypeInfo(
type,
context),
expression,
context);
}
@
NotNull
public
KotlinTypeInfo createCompileTimeConstantTypeInfo(
@
NotNull CompileTimeConstant<?>
value,
@
NotNull KtExpression expression,
@
NotNull ExpressionTypingContext context
) {
KotlinType expressionType;
if (
value instanceof
IntegerValueTypeConstant) {
IntegerValueTypeConstant integerValueTypeConstant = (
IntegerValueTypeConstant)
value;
if (
context.
contextDependency ==
INDEPENDENT) {
expressionType =
integerValueTypeConstant.
getType(
context.
expectedType);
constantExpressionEvaluator.
updateNumberType(
expressionType,
expression,
context.
statementFilter,
context.
trace);
}
else {
expressionType =
integerValueTypeConstant.
getUnknownIntegerType();
}
}
else {
expressionType = ((
TypedCompileTimeConstant<?>)
value).
getType();
}
return
createCheckedTypeInfo(
expressionType,
context,
expression);
}
}