Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.types.expressions.DataFlowAnalyzer Maven / Gradle / Ivy
/*
* 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.LanguageVersionSettings;
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 additionalTypeCheckers;
private final ConstantExpressionEvaluator constantExpressionEvaluator;
private final KotlinBuiltIns builtIns;
private final SmartCastManager smartCastManager;
private final ExpressionTypingFacade facade;
private final LanguageVersionSettings languageVersionSettings;
public DataFlowAnalyzer(
@NotNull Iterable additionalTypeCheckers,
@NotNull ConstantExpressionEvaluator constantExpressionEvaluator,
@NotNull KotlinBuiltIns builtIns,
@NotNull SmartCastManager smartCastManager,
@NotNull ExpressionTypingFacade facade,
@NotNull LanguageVersionSettings languageVersionSettings
) {
this.additionalTypeCheckers = additionalTypeCheckers;
this.constantExpressionEvaluator = constantExpressionEvaluator;
this.builtIns = builtIns;
this.smartCastManager = smartCastManager;
this.facade = facade;
this.languageVersionSettings = languageVersionSettings;
}
// NB: use this method only for functions from 'Any'
@Nullable
private static FunctionDescriptor getOverriddenDescriptorFromClass(@NotNull FunctionDescriptor descriptor) {
if (descriptor.getKind() != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) return descriptor;
Collection 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 static boolean typeHasOverriddenEquals(@NotNull KotlinType type, @NotNull KtElement lookupElement) {
Collection 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 static 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 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;
}
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);
}
}
});
if (result.get() == null) {
return context.dataFlowInfo;
}
return context.dataFlowInfo.and(result.get());
}
@Nullable
public KotlinType checkType(@Nullable KotlinType expressionType, @NotNull KtExpression expression, @NotNull ResolutionContext context) {
return checkType(expressionType, expression, context, null);
}
@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 hasError
) {
if (noExpectedType(c.expectedType) || !c.expectedType.getConstructor().isDenotable() ||
KotlinTypeChecker.DEFAULT.isSubtypeOf(expressionType, c.expectedType)) {
return expressionType;
}
if (expression instanceof KtConstantExpression) {
ConstantValue constantValue = constantExpressionEvaluator.evaluateToConstantValue(expression, c.trace, c.expectedType);
boolean error = new CompileTimeConstantChecker(c, builtIns, 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 (!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 hasError
) {
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);
if (Boolean.FALSE.equals(hasError.get())) {
for (AdditionalTypeChecker checker : additionalTypeCheckers) {
checker.checkType(expression, expressionType, result, c);
}
}
return result;
}
@Nullable
public static 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 static 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 getAllPossibleTypes(
@NotNull KtExpression expression,
@NotNull KotlinType type,
@NotNull ResolutionContext c
) {
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c);
return getAllPossibleTypes(type, c, dataFlowValue);
}
@NotNull
public static Collection getAllPossibleTypes(
@NotNull KotlinType type,
@NotNull ResolutionContext c,
@NotNull DataFlowValue dataFlowValue
) {
Collection possibleTypes = Sets.newHashSet(type);
possibleTypes.addAll(c.dataFlowInfo.getStableTypes(dataFlowValue));
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);
}
}