From eef68e0a944a4d065295abe468ded8b2011c385d Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Tue, 19 Jan 2016 12:25:16 +0300 Subject: [PATCH] J2K: PatternMatchingTypingVisitor.kt: convert to Kotlin and cleanup --- .../org/jetbrains/kotlin/cfg/WhenChecker.kt | 3 - .../PatternMatchingTypingVisitor.kt | 454 ++++++++---------- 2 files changed, 205 insertions(+), 252 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt index c62329b59b0..9f325178fe2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt @@ -330,11 +330,9 @@ object WhenChecker { false } - @JvmStatic fun containsNullCase(expression: KtWhenExpression, context: BindingContext) = WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, null, true).isEmpty() - @JvmStatic fun checkDeprecatedWhenSyntax(trace: BindingTrace, expression: KtWhenExpression) { if (expression.subjectExpression != null) return @@ -351,7 +349,6 @@ object WhenChecker { } } - @JvmStatic fun checkReservedPrefix(trace: BindingTrace, expression: KtWhenExpression) { checkReservedPrefixWord(trace, expression.whenKeyword, "sealed", TokenSet.EMPTY, "sealed when") } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt index 1093eca5938..d9035b376d1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt @@ -14,365 +14,321 @@ * limitations under the License. */ -package org.jetbrains.kotlin.types.expressions; +package org.jetbrains.kotlin.types.expressions -import com.google.common.collect.Sets; -import com.intellij.openapi.util.Ref; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.builtins.KotlinBuiltIns; -import org.jetbrains.kotlin.cfg.WhenChecker; -import org.jetbrains.kotlin.descriptors.ClassDescriptor; -import org.jetbrains.kotlin.diagnostics.Errors; -import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.resolve.*; -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.calls.smartcasts.SmartCastResult; -import org.jetbrains.kotlin.resolve.calls.util.CallMaker; -import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind; -import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope; -import org.jetbrains.kotlin.types.*; -import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; -import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt; +import com.google.common.collect.Sets +import com.intellij.openapi.util.Ref +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean +import org.jetbrains.kotlin.cfg.WhenChecker +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.diagnostics.Errors.* +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.TemporaryBindingTrace +import org.jetbrains.kotlin.resolve.TypeResolutionContext +import org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT +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.calls.util.CallMaker +import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.newWritableScopeImpl +import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo -import java.util.Collections; -import java.util.Set; +class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) { -import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean; -import static org.jetbrains.kotlin.diagnostics.Errors.*; -import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT; -import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE; -import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.newWritableScopeImpl; - -public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { - protected PatternMatchingTypingVisitor(@NotNull ExpressionTypingInternals facade) { - super(facade); - } - - @Override - public KotlinTypeInfo visitIsExpression(@NotNull KtIsExpression expression, ExpressionTypingContext contextWithExpectedType) { - ExpressionTypingContext context = contextWithExpectedType - .replaceExpectedType(NO_EXPECTED_TYPE) - .replaceContextDependency(INDEPENDENT); - KtExpression leftHandSide = expression.getLeftHandSide(); - KotlinTypeInfo typeInfo = facade.safeGetTypeInfo(leftHandSide, context.replaceScope(context.scope)); - KotlinType knownType = typeInfo.getType(); - if (expression.getTypeReference() != null) { - DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(leftHandSide, knownType, context); - DataFlowInfo conditionInfo = checkTypeForIs(context, knownType, expression.getTypeReference(), dataFlowValue).thenInfo; - DataFlowInfo newDataFlowInfo = conditionInfo.and(typeInfo.getDataFlowInfo()); - context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo); + override fun visitIsExpression(expression: KtIsExpression, contextWithExpectedType: ExpressionTypingContext): KotlinTypeInfo { + val context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT) + val leftHandSide = expression.leftHandSide + val typeInfo = facade.safeGetTypeInfo(leftHandSide, context.replaceScope(context.scope)) + val knownType = typeInfo.type + if (expression.typeReference != null && knownType != null) { + val dataFlowValue = DataFlowValueFactory.createDataFlowValue(leftHandSide, knownType, context) + val conditionInfo = checkTypeForIs(context, knownType, expression.typeReference, dataFlowValue).thenInfo + val newDataFlowInfo = conditionInfo.and(typeInfo.dataFlowInfo) + context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo) } - return components.dataFlowAnalyzer.checkType(typeInfo.replaceType(components.builtIns.getBooleanType()), expression, contextWithExpectedType); + return components.dataFlowAnalyzer.checkType(typeInfo.replaceType(components.builtIns.booleanType), expression, contextWithExpectedType) } - @Override - public KotlinTypeInfo visitWhenExpression(@NotNull KtWhenExpression expression, ExpressionTypingContext context) { - return visitWhenExpression(expression, context, false); - } + override fun visitWhenExpression(expression: KtWhenExpression, context: ExpressionTypingContext) = + visitWhenExpression(expression, context, false) - public KotlinTypeInfo visitWhenExpression(KtWhenExpression expression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) { - WhenChecker.checkDeprecatedWhenSyntax(contextWithExpectedType.trace, expression); - WhenChecker.checkReservedPrefix(contextWithExpectedType.trace, expression); + fun visitWhenExpression(expression: KtWhenExpression, contextWithExpectedType: ExpressionTypingContext, isStatement: Boolean): KotlinTypeInfo { + WhenChecker.checkDeprecatedWhenSyntax(contextWithExpectedType.trace, expression) + WhenChecker.checkReservedPrefix(contextWithExpectedType.trace, expression) - components.dataFlowAnalyzer.recordExpectedType(contextWithExpectedType.trace, expression, contextWithExpectedType.expectedType); + components.dataFlowAnalyzer.recordExpectedType(contextWithExpectedType.trace, expression, contextWithExpectedType.expectedType) - ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT); + var context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT) // TODO :change scope according to the bound value in the when header - KtExpression subjectExpression = expression.getSubjectExpression(); + val subjectExpression = expression.subjectExpression - KotlinType subjectType; - boolean loopBreakContinuePossible = false; + val subjectType: KotlinType + var loopBreakContinuePossible = false if (subjectExpression == null) { - subjectType = ErrorUtils.createErrorType("Unknown type"); + subjectType = ErrorUtils.createErrorType("Unknown type") } else { - KotlinTypeInfo typeInfo = facade.safeGetTypeInfo(subjectExpression, context); - loopBreakContinuePossible = typeInfo.getJumpOutPossible(); - subjectType = typeInfo.getType(); - assert subjectType != null; - if (TypeUtils.isNullableType(subjectType) && !WhenChecker.containsNullCase(expression, context.trace.getBindingContext())) { - TemporaryBindingTrace trace = TemporaryBindingTrace.create(context.trace, "Temporary trace for when subject nullability"); - ExpressionTypingContext subjectContext = - context.replaceExpectedType(TypeUtils.makeNotNullable(subjectType)).replaceBindingTrace(trace); - SmartCastResult castResult = components.dataFlowAnalyzer.checkPossibleCast( - subjectType, KtPsiUtil.safeDeparenthesize(subjectExpression), subjectContext - ); - if (castResult != null && castResult.isCorrect()) { - trace.commit(); + val typeInfo = facade.safeGetTypeInfo(subjectExpression, context) + loopBreakContinuePossible = typeInfo.jumpOutPossible + subjectType = typeInfo.type!! + if (TypeUtils.isNullableType(subjectType) && !WhenChecker.containsNullCase(expression, context.trace.bindingContext)) { + val trace = TemporaryBindingTrace.create(context.trace, "Temporary trace for when subject nullability") + val subjectContext = context.replaceExpectedType(TypeUtils.makeNotNullable(subjectType)).replaceBindingTrace(trace) + val castResult = DataFlowAnalyzer.checkPossibleCast( + subjectType, KtPsiUtil.safeDeparenthesize(subjectExpression), subjectContext) + if (castResult != null && castResult.isCorrect) { + trace.commit() } } - context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo()); + context = context.replaceDataFlowInfo(typeInfo.dataFlowInfo) } - DataFlowValue subjectDataFlowValue = subjectExpression != null - ? DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, context) - : DataFlowValue.nullValue(components.builtIns); + val subjectDataFlowValue = if (subjectExpression != null) + DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, context) + else + DataFlowValue.nullValue(components.builtIns) // TODO : exhaustive patterns - Set expressionTypes = Sets.newHashSet(); - DataFlowInfo commonDataFlowInfo = null; - DataFlowInfo elseDataFlowInfo = context.dataFlowInfo; - DataFlowValue whenValue = DataFlowValueFactory.createDataFlowValue(expression, components.builtIns.getNullableAnyType(), context); - for (KtWhenEntry whenEntry : expression.getEntries()) { - DataFlowInfos infosForCondition = getDataFlowInfosForEntryCondition( - whenEntry, context.replaceDataFlowInfo(elseDataFlowInfo), subjectExpression, subjectType, subjectDataFlowValue); - elseDataFlowInfo = elseDataFlowInfo.and(infosForCondition.elseInfo); + val expressionTypes = Sets.newHashSet() + var commonDataFlowInfo: DataFlowInfo? = null + var elseDataFlowInfo = context.dataFlowInfo + val whenValue = DataFlowValueFactory.createDataFlowValue(expression, components.builtIns.nullableAnyType, context) + for (whenEntry in expression.entries) { + val infosForCondition = getDataFlowInfosForEntryCondition( + whenEntry, context.replaceDataFlowInfo(elseDataFlowInfo), subjectExpression, subjectType, subjectDataFlowValue) + elseDataFlowInfo = elseDataFlowInfo.and(infosForCondition.elseInfo) - KtExpression bodyExpression = whenEntry.getExpression(); + val bodyExpression = whenEntry.expression if (bodyExpression != null) { - LexicalWritableScope scopeToExtend = newWritableScopeImpl(context, LexicalScopeKind.WHEN); - ExpressionTypingContext newContext = contextWithExpectedType - .replaceScope(scopeToExtend).replaceDataFlowInfo(infosForCondition.thenInfo).replaceContextDependency(INDEPENDENT); - CoercionStrategy coercionStrategy = isStatement ? CoercionStrategy.COERCION_TO_UNIT : CoercionStrategy.NO_COERCION; - KotlinTypeInfo typeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( - scopeToExtend, Collections.singletonList(bodyExpression), coercionStrategy, newContext); - loopBreakContinuePossible |= typeInfo.getJumpOutPossible(); - KotlinType type = typeInfo.getType(); + val scopeToExtend = newWritableScopeImpl(context, LexicalScopeKind.WHEN) + val newContext = contextWithExpectedType.replaceScope(scopeToExtend).replaceDataFlowInfo(infosForCondition.thenInfo).replaceContextDependency(INDEPENDENT) + val coercionStrategy = if (isStatement) CoercionStrategy.COERCION_TO_UNIT else CoercionStrategy.NO_COERCION + var typeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope( + scopeToExtend, listOf(bodyExpression), coercionStrategy, newContext) + loopBreakContinuePossible = loopBreakContinuePossible or typeInfo.jumpOutPossible + val type = typeInfo.type if (type != null) { - expressionTypes.add(type); - DataFlowValue entryValue = DataFlowValueFactory.createDataFlowValue(bodyExpression, type, context); - typeInfo = typeInfo.replaceDataFlowInfo(typeInfo.getDataFlowInfo().assign(whenValue, entryValue)); + expressionTypes.add(type) + val entryValue = DataFlowValueFactory.createDataFlowValue(bodyExpression, type, context) + typeInfo = typeInfo.replaceDataFlowInfo(typeInfo.dataFlowInfo.assign(whenValue, entryValue)) } if (commonDataFlowInfo == null) { - commonDataFlowInfo = typeInfo.getDataFlowInfo(); + commonDataFlowInfo = typeInfo.dataFlowInfo } else { - commonDataFlowInfo = commonDataFlowInfo.or(typeInfo.getDataFlowInfo()); + commonDataFlowInfo = commonDataFlowInfo.or(typeInfo.dataFlowInfo) } } } - boolean isExhaustive = WhenChecker.isWhenExhaustive(expression, context.trace); + val isExhaustive = WhenChecker.isWhenExhaustive(expression, context.trace) if (commonDataFlowInfo == null) { - commonDataFlowInfo = context.dataFlowInfo; + commonDataFlowInfo = context.dataFlowInfo } - else if (expression.getElseExpression() == null && !isExhaustive) { + else if (expression.elseExpression == null && !isExhaustive) { // Without else expression in non-exhaustive when, we *must* take initial data flow info into account, // because data flow can bypass all when branches in this case - commonDataFlowInfo = commonDataFlowInfo.or(context.dataFlowInfo); + commonDataFlowInfo = commonDataFlowInfo.or(context.dataFlowInfo) } - KotlinType resultType = expressionTypes.isEmpty() ? null : CommonSupertypes.commonSupertype(expressionTypes); + var resultType: KotlinType? = if (expressionTypes.isEmpty()) null else CommonSupertypes.commonSupertype(expressionTypes) if (resultType != null) { - DataFlowValue resultValue = DataFlowValueFactory.createDataFlowValue(expression, resultType, context); - commonDataFlowInfo = commonDataFlowInfo.assign(resultValue, whenValue); - if (isExhaustive && expression.getElseExpression() == null && KotlinBuiltIns.isNothing(resultType)) { - context.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression); + val resultValue = DataFlowValueFactory.createDataFlowValue(expression, resultType, context) + commonDataFlowInfo = commonDataFlowInfo.assign(resultValue, whenValue) + if (isExhaustive && expression.elseExpression == null && KotlinBuiltIns.isNothing(resultType)) { + context.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression) } - resultType = components.dataFlowAnalyzer.checkType(resultType, expression, contextWithExpectedType); + resultType = components.dataFlowAnalyzer.checkType(resultType, expression, contextWithExpectedType) } - return TypeInfoFactoryKt.createTypeInfo(resultType, - commonDataFlowInfo, - loopBreakContinuePossible, - contextWithExpectedType.dataFlowInfo); + return createTypeInfo(resultType, + commonDataFlowInfo, + loopBreakContinuePossible, + contextWithExpectedType.dataFlowInfo) } - @NotNull - private DataFlowInfos getDataFlowInfosForEntryCondition( - @NotNull KtWhenEntry whenEntry, - @NotNull ExpressionTypingContext context, - @Nullable KtExpression subjectExpression, - @NotNull KotlinType subjectType, - @NotNull DataFlowValue subjectDataFlowValue - ) { - if (whenEntry.isElse()) { - return new DataFlowInfos(context.dataFlowInfo); + private fun getDataFlowInfosForEntryCondition( + whenEntry: KtWhenEntry, + context: ExpressionTypingContext, + subjectExpression: KtExpression?, + subjectType: KotlinType, + subjectDataFlowValue: DataFlowValue): DataFlowInfos { + if (whenEntry.isElse) { + return DataFlowInfos(context.dataFlowInfo) } - DataFlowInfos infos = null; - ExpressionTypingContext contextForCondition = context; - for (KtWhenCondition condition : whenEntry.getConditions()) { - DataFlowInfos conditionInfos = checkWhenCondition(subjectExpression, subjectType, condition, - contextForCondition, subjectDataFlowValue); + var infos: DataFlowInfos? = null + var contextForCondition = context + for (condition in whenEntry.conditions) { + val conditionInfos = checkWhenCondition(subjectExpression, subjectType, condition, + contextForCondition, subjectDataFlowValue) if (infos != null) { - infos = new DataFlowInfos(infos.thenInfo.or(conditionInfos.thenInfo), infos.elseInfo.and(conditionInfos.elseInfo)); + infos = DataFlowInfos(infos.thenInfo.or(conditionInfos.thenInfo), infos.elseInfo.and(conditionInfos.elseInfo)) } else { - infos = conditionInfos; + infos = conditionInfos } - contextForCondition = contextForCondition.replaceDataFlowInfo(conditionInfos.elseInfo); + contextForCondition = contextForCondition.replaceDataFlowInfo(conditionInfos.elseInfo) } - return infos != null ? infos : new DataFlowInfos(context.dataFlowInfo); + return if (infos != null) infos else DataFlowInfos(context.dataFlowInfo) } - private DataFlowInfos checkWhenCondition( - @Nullable final KtExpression subjectExpression, - final KotlinType subjectType, - KtWhenCondition condition, - final ExpressionTypingContext context, - final DataFlowValue subjectDataFlowValue - ) { - final Ref newDataFlowInfo = new Ref(noChange(context)); - condition.accept(new KtVisitorVoid() { - @Override - public void visitWhenConditionInRange(@NotNull KtWhenConditionInRange condition) { - KtExpression rangeExpression = condition.getRangeExpression(); - if (rangeExpression == null) return; + private fun checkWhenCondition( + subjectExpression: KtExpression?, + subjectType: KotlinType, + condition: KtWhenCondition, + context: ExpressionTypingContext, + subjectDataFlowValue: DataFlowValue): DataFlowInfos { + val newDataFlowInfo = Ref(noChange(context)) + condition.accept(object : KtVisitorVoid() { + override fun visitWhenConditionInRange(condition: KtWhenConditionInRange) { + val rangeExpression = condition.rangeExpression ?: return if (subjectExpression == null) { - context.trace.report(EXPECTED_CONDITION.on(condition)); - DataFlowInfo dataFlowInfo = facade.getTypeInfo(rangeExpression, context).getDataFlowInfo(); - newDataFlowInfo.set(new DataFlowInfos(dataFlowInfo, dataFlowInfo)); - return; + context.trace.report(EXPECTED_CONDITION.on(condition)) + val dataFlowInfo = facade.getTypeInfo(rangeExpression, context).dataFlowInfo + newDataFlowInfo.set(DataFlowInfos(dataFlowInfo, dataFlowInfo)) + return } - ValueArgument argumentForSubject = CallMaker.makeExternalValueArgument(subjectExpression); - KotlinTypeInfo typeInfo = facade.checkInExpression(condition, condition.getOperationReference(), - argumentForSubject, rangeExpression, context); - DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo(); - newDataFlowInfo.set(new DataFlowInfos(dataFlowInfo, dataFlowInfo)); - KotlinType type = typeInfo.getType(); + val argumentForSubject = CallMaker.makeExternalValueArgument(subjectExpression) + val typeInfo = facade.checkInExpression(condition, condition.operationReference, + argumentForSubject, rangeExpression, context) + val dataFlowInfo = typeInfo.dataFlowInfo + newDataFlowInfo.set(DataFlowInfos(dataFlowInfo, dataFlowInfo)) + val type = typeInfo.type if (type == null || !isBoolean(type)) { - context.trace.report(TYPE_MISMATCH_IN_RANGE.on(condition)); + context.trace.report(TYPE_MISMATCH_IN_RANGE.on(condition)) } } - @Override - public void visitWhenConditionIsPattern(@NotNull KtWhenConditionIsPattern condition) { + override fun visitWhenConditionIsPattern(condition: KtWhenConditionIsPattern) { if (subjectExpression == null) { - context.trace.report(EXPECTED_CONDITION.on(condition)); + context.trace.report(EXPECTED_CONDITION.on(condition)) } - if (condition.getTypeReference() != null) { - DataFlowInfos result = checkTypeForIs(context, subjectType, condition.getTypeReference(), subjectDataFlowValue); - if (condition.isNegated()) { - newDataFlowInfo.set(new DataFlowInfos(result.elseInfo, result.thenInfo)); + if (condition.typeReference != null) { + val result = checkTypeForIs(context, subjectType, condition.typeReference, subjectDataFlowValue) + if (condition.isNegated) { + newDataFlowInfo.set(DataFlowInfos(result.elseInfo, result.thenInfo)) } else { - newDataFlowInfo.set(result); + newDataFlowInfo.set(result) } } } - @Override - public void visitWhenConditionWithExpression(@NotNull KtWhenConditionWithExpression condition) { - KtExpression expression = condition.getExpression(); + override fun visitWhenConditionWithExpression(condition: KtWhenConditionWithExpression) { + val expression = condition.expression if (expression != null) { newDataFlowInfo.set(checkTypeForExpressionCondition(context, expression, subjectType, subjectExpression == null, - subjectDataFlowValue)); + subjectDataFlowValue)) } } - @Override - public void visitKtElement(@NotNull KtElement element) { - context.trace.report(UNSUPPORTED.on(element, getClass().getCanonicalName())); + override fun visitKtElement(element: KtElement) { + context.trace.report(UNSUPPORTED.on(element, javaClass.canonicalName)) } - }); - return newDataFlowInfo.get(); + }) + return newDataFlowInfo.get() } - private static class DataFlowInfos { - private final DataFlowInfo thenInfo; - private final DataFlowInfo elseInfo; + private class DataFlowInfos(val thenInfo: DataFlowInfo, val elseInfo: DataFlowInfo = thenInfo) - private DataFlowInfos(DataFlowInfo thenInfo, DataFlowInfo elseInfo) { - this.thenInfo = thenInfo; - this.elseInfo = elseInfo; - } - - private DataFlowInfos(DataFlowInfo info) { - this(info, info); - } - } - - private DataFlowInfos checkTypeForExpressionCondition( - ExpressionTypingContext context, - KtExpression expression, - KotlinType subjectType, - boolean conditionExpected, - DataFlowValue subjectDataFlowValue - ) { + private fun checkTypeForExpressionCondition( + context: ExpressionTypingContext, + expression: KtExpression?, + subjectType: KotlinType, + conditionExpected: Boolean, + subjectDataFlowValue: DataFlowValue + ): DataFlowInfos { + var newContext = context if (expression == null) { - return noChange(context); + return noChange(newContext) } - KotlinTypeInfo typeInfo = facade.getTypeInfo(expression, context); - KotlinType type = typeInfo.getType(); - if (type == null) { - return noChange(context); - } - context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo()); + val typeInfo = facade.getTypeInfo(expression, newContext) + val type = typeInfo.type ?: return noChange(newContext) + newContext = newContext.replaceDataFlowInfo(typeInfo.dataFlowInfo) if (conditionExpected) { - KotlinType booleanType = components.builtIns.getBooleanType(); + val booleanType = components.builtIns.booleanType if (!KotlinTypeChecker.DEFAULT.equalTypes(booleanType, type)) { - context.trace.report(TYPE_MISMATCH_IN_CONDITION.on(expression, type)); + newContext.trace.report(TYPE_MISMATCH_IN_CONDITION.on(expression, type)) } else { - DataFlowInfo ifInfo = components.dataFlowAnalyzer.extractDataFlowInfoFromCondition(expression, true, context); - DataFlowInfo elseInfo = components.dataFlowAnalyzer.extractDataFlowInfoFromCondition(expression, false, context); - return new DataFlowInfos(ifInfo, elseInfo); + val ifInfo = components.dataFlowAnalyzer.extractDataFlowInfoFromCondition(expression, true, newContext) + val elseInfo = components.dataFlowAnalyzer.extractDataFlowInfoFromCondition(expression, false, newContext) + return DataFlowInfos(ifInfo, elseInfo) } - return noChange(context); + return noChange(newContext) } - checkTypeCompatibility(context, type, subjectType, expression); - DataFlowValue expressionDataFlowValue = - DataFlowValueFactory.createDataFlowValue(expression, type, context); - DataFlowInfos result = noChange(context); - result = new DataFlowInfos( + checkTypeCompatibility(newContext, type, subjectType, expression) + val expressionDataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, newContext) + var result = noChange(newContext) + result = DataFlowInfos( result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue), - result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue) - ); - return result; + result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue)) + return result } - private DataFlowInfos checkTypeForIs( - ExpressionTypingContext context, - KotlinType subjectType, - KtTypeReference typeReferenceAfterIs, - DataFlowValue subjectDataFlowValue - ) { + private fun checkTypeForIs( + context: ExpressionTypingContext, + subjectType: KotlinType, + typeReferenceAfterIs: KtTypeReference?, + subjectDataFlowValue: DataFlowValue + ): DataFlowInfos { if (typeReferenceAfterIs == null) { - return noChange(context); + return noChange(context) } - TypeResolutionContext typeResolutionContext = new TypeResolutionContext(context.scope, context.trace, true, /*allowBareTypes=*/ true); - PossiblyBareType possiblyBareTarget = components.typeResolver.resolvePossiblyBareType(typeResolutionContext, typeReferenceAfterIs); - KotlinType targetType = TypeReconstructionUtil.reconstructBareType(typeReferenceAfterIs, possiblyBareTarget, subjectType, context.trace, components.builtIns); + val typeResolutionContext = TypeResolutionContext(context.scope, context.trace, true, /*allowBareTypes=*/ true) + val possiblyBareTarget = components.typeResolver.resolvePossiblyBareType(typeResolutionContext, typeReferenceAfterIs) + val targetType = TypeReconstructionUtil.reconstructBareType(typeReferenceAfterIs, possiblyBareTarget, subjectType, context.trace, components.builtIns) - if (DynamicTypesKt.isDynamic(targetType)) { - context.trace.report(DYNAMIC_NOT_ALLOWED.on(typeReferenceAfterIs)); + if (targetType.isDynamic()) { + context.trace.report(DYNAMIC_NOT_ALLOWED.on(typeReferenceAfterIs)) } - ClassDescriptor targetDescriptor = TypeUtils.getClassDescriptor(targetType); + val targetDescriptor = TypeUtils.getClassDescriptor(targetType) if (targetDescriptor != null && DescriptorUtils.isEnumEntry(targetDescriptor)) { - context.trace.report(IS_ENUM_ENTRY.on(typeReferenceAfterIs)); + context.trace.report(IS_ENUM_ENTRY.on(typeReferenceAfterIs)) } - if (!subjectType.isMarkedNullable() && targetType.isMarkedNullable()) { - KtTypeElement element = typeReferenceAfterIs.getTypeElement(); - assert element instanceof KtNullableType : "element must be instance of " + KtNullableType.class.getName(); - KtNullableType nullableType = (KtNullableType) element; - context.trace.report(Errors.USELESS_NULLABLE_CHECK.on(nullableType)); + if (!subjectType.isMarkedNullable && targetType.isMarkedNullable) { + val element = typeReferenceAfterIs.typeElement + assert(element is KtNullableType) { "element must be instance of " + KtNullableType::class.java.name } + context.trace.report(Errors.USELESS_NULLABLE_CHECK.on(element as KtNullableType)) } - checkTypeCompatibility(context, targetType, subjectType, typeReferenceAfterIs); + checkTypeCompatibility(context, targetType, subjectType, typeReferenceAfterIs) if (CastDiagnosticsUtil.isCastErased(subjectType, targetType, KotlinTypeChecker.DEFAULT)) { - context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, targetType)); + context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, targetType)) } - return new DataFlowInfos(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, targetType), context.dataFlowInfo); + return DataFlowInfos(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, targetType), context.dataFlowInfo) } - private static DataFlowInfos noChange(ExpressionTypingContext context) { - return new DataFlowInfos(context.dataFlowInfo, context.dataFlowInfo); - } + private fun noChange(context: ExpressionTypingContext) = DataFlowInfos(context.dataFlowInfo, context.dataFlowInfo) /* * (a: SubjectType) is Type */ - private static void checkTypeCompatibility( - @NotNull ExpressionTypingContext context, - @Nullable KotlinType type, - @NotNull KotlinType subjectType, - @NotNull KtElement reportErrorOn + private fun checkTypeCompatibility( + context: ExpressionTypingContext, + type: KotlinType?, + subjectType: KotlinType, + reportErrorOn: KtElement ) { // TODO : Take smart casts into account? if (type == null) { - return; + return } if (TypeIntersector.isIntersectionEmpty(type, subjectType)) { - context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType)); - return; + context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType)) + return } // check if the pattern is essentially a 'null' expression if (KotlinBuiltIns.isNullableNothing(type) && !TypeUtils.isNullableType(subjectType)) { - context.trace.report(SENSELESS_NULL_IN_WHEN.on(reportErrorOn)); + context.trace.report(SENSELESS_NULL_IN_WHEN.on(reportErrorOn)) } } }