diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt index e2a3161977a..f2599145d66 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt @@ -14,229 +14,191 @@ * limitations under the License. */ -package org.jetbrains.kotlin.resolve.calls.smartcasts; +package org.jetbrains.kotlin.resolve.calls.smartcasts -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import kotlin.collections.CollectionsKt; -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.psi.KtExpression; -import org.jetbrains.kotlin.resolve.BindingContext; -import org.jetbrains.kotlin.resolve.BindingTrace; -import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver; -import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; -import org.jetbrains.kotlin.types.KotlinType; -import org.jetbrains.kotlin.types.TypeIntersector; -import org.jetbrains.kotlin.types.TypeUtils; -import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; - -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import static org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE; -import static org.jetbrains.kotlin.resolve.BindingContext.IMPLICIT_RECEIVER_SMARTCAST; -import static org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST; +import com.google.common.collect.Lists +import com.google.common.collect.Sets +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingContext.IMPLICIT_RECEIVER_SMARTCAST +import org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeIntersector +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker // We do not want to make methods static to keep SmartCastManager as a component @SuppressWarnings("MethodMayBeStatic") -public class SmartCastManager { +class SmartCastManager { - @NotNull - public List getSmartCastVariants( - @NotNull ReceiverValue receiverToCast, - @NotNull ResolutionContext context - ) { - return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.scope.getOwnerDescriptor(), context.dataFlowInfo); + fun getSmartCastVariants( + receiverToCast: ReceiverValue, + context: ResolutionContext<*>): List { + return getSmartCastVariants(receiverToCast, context.trace.bindingContext, context.scope.ownerDescriptor, context.dataFlowInfo) } - @NotNull - public List getSmartCastVariants( - @NotNull ReceiverValue receiverToCast, - @NotNull BindingContext bindingContext, - @NotNull DeclarationDescriptor containingDeclarationOrModule, - @NotNull DataFlowInfo dataFlowInfo - ) { - List variants = Lists.newArrayList(); - variants.add(receiverToCast.getType()); - variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclarationOrModule, dataFlowInfo, receiverToCast)); - return variants; + fun getSmartCastVariants( + receiverToCast: ReceiverValue, + bindingContext: BindingContext, + containingDeclarationOrModule: DeclarationDescriptor, + dataFlowInfo: DataFlowInfo): List { + val variants = Lists.newArrayList() + variants.add(receiverToCast.type) + variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclarationOrModule, dataFlowInfo, receiverToCast)) + return variants } - @NotNull - public List getSmartCastVariantsWithLessSpecificExcluded( - @NotNull ReceiverValue receiverToCast, - @NotNull BindingContext bindingContext, - @NotNull DeclarationDescriptor containingDeclarationOrModule, - @NotNull DataFlowInfo dataFlowInfo - ) { - final List variants = CollectionsKt.distinct( - getSmartCastVariants(receiverToCast, bindingContext, containingDeclarationOrModule, dataFlowInfo)); - return CollectionsKt.filter(variants, new Function1() { - @Override - public Boolean invoke(final KotlinType type) { - return CollectionsKt.none(variants, new Function1() { - @Override - public Boolean invoke(KotlinType another) { - return another != type && KotlinTypeChecker.DEFAULT.isSubtypeOf(another, type); - } - }); - } - }); + fun getSmartCastVariantsWithLessSpecificExcluded( + receiverToCast: ReceiverValue, + bindingContext: BindingContext, + containingDeclarationOrModule: DeclarationDescriptor, + dataFlowInfo: DataFlowInfo): List { + val variants = getSmartCastVariants(receiverToCast, bindingContext, containingDeclarationOrModule, dataFlowInfo).distinct() + return variants.filter { type -> variants.none { another -> another !== type && KotlinTypeChecker.DEFAULT.isSubtypeOf(another, type) } } } /** * @return variants @param receiverToCast may be cast to according to context dataFlowInfo, receiverToCast itself is NOT included */ - @NotNull - public Collection getSmartCastVariantsExcludingReceiver( - @NotNull ResolutionContext context, - @NotNull ReceiverValue receiverToCast - ) { - return getSmartCastVariantsExcludingReceiver(context.trace.getBindingContext(), - context.scope.getOwnerDescriptor(), + fun getSmartCastVariantsExcludingReceiver( + context: ResolutionContext<*>, + receiverToCast: ReceiverValue): Collection { + return getSmartCastVariantsExcludingReceiver(context.trace.bindingContext, + context.scope.ownerDescriptor, context.dataFlowInfo, - receiverToCast); + receiverToCast) } /** * @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included */ - @NotNull - public Collection getSmartCastVariantsExcludingReceiver( - @NotNull BindingContext bindingContext, - @NotNull DeclarationDescriptor containingDeclarationOrModule, - @NotNull DataFlowInfo dataFlowInfo, - @NotNull ReceiverValue receiverToCast - ) { - DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue( - receiverToCast, bindingContext, containingDeclarationOrModule - ); + fun getSmartCastVariantsExcludingReceiver( + bindingContext: BindingContext, + containingDeclarationOrModule: DeclarationDescriptor, + dataFlowInfo: DataFlowInfo, + receiverToCast: ReceiverValue): Collection { + val dataFlowValue = DataFlowValueFactory.createDataFlowValue( + receiverToCast, bindingContext, containingDeclarationOrModule) - return dataFlowInfo.getCollectedTypes(dataFlowValue); + return dataFlowInfo.getCollectedTypes(dataFlowValue) } - public boolean isSubTypeBySmartCastIgnoringNullability( - @NotNull ReceiverValue receiverArgument, - @NotNull KotlinType receiverParameterType, - @NotNull ResolutionContext context - ) { - List smartCastTypes = getSmartCastVariants(receiverArgument, context); - return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null; + fun isSubTypeBySmartCastIgnoringNullability( + receiverArgument: ReceiverValue, + receiverParameterType: KotlinType, + context: ResolutionContext<*>): Boolean { + val smartCastTypes = getSmartCastVariants(receiverArgument, context) + return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null } - @Nullable - private KotlinType getSmartCastSubType( - @NotNull KotlinType receiverParameterType, - @NotNull Collection smartCastTypes - ) { - Set subTypes = Sets.newHashSet(); - for (KotlinType smartCastType : smartCastTypes) { + private fun getSmartCastSubType( + receiverParameterType: KotlinType, + smartCastTypes: Collection): KotlinType? { + val subTypes = Sets.newHashSet() + for (smartCastType in smartCastTypes) { if (ArgumentTypeResolver.isSubtypeOfForArgumentType(smartCastType, receiverParameterType)) { - subTypes.add(smartCastType); + subTypes.add(smartCastType) } } - if (subTypes.isEmpty()) return null; + if (subTypes.isEmpty()) return null - KotlinType intersection = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, subTypes); - if (intersection == null || !intersection.getConstructor().isDenotable()) { - return receiverParameterType; + val intersection = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, subTypes) + if (intersection == null || !intersection.constructor.isDenotable) { + return receiverParameterType } - return intersection; + return intersection } - private static void recordCastOrError( - @NotNull KtExpression expression, - @NotNull KotlinType type, - @NotNull BindingTrace trace, - @NotNull DataFlowValue dataFlowValue, - boolean recordExpressionType - ) { - if (KotlinBuiltIns.isNullableNothing(type)) return; - if (dataFlowValue.isPredictable()) { - trace.record(SMARTCAST, expression, type); - if (recordExpressionType) { - //TODO - //Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary? - trace.recordType(expression, type); - } - } - else { - trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.getText(), dataFlowValue.getKind().getDescription())); - } - } + companion object { - @Nullable - public static SmartCastResult checkAndRecordPossibleCast( - @NotNull DataFlowValue dataFlowValue, - @NotNull KotlinType expectedType, - @Nullable KtExpression expression, - @NotNull ResolutionContext c, - @Nullable KtExpression calleeExpression, - boolean recordExpressionType - ) { - return checkAndRecordPossibleCast( - dataFlowValue, expectedType, null, expression, c, calleeExpression, recordExpressionType); - } - - @Nullable - public static SmartCastResult checkAndRecordPossibleCast( - @NotNull DataFlowValue dataFlowValue, - @NotNull KotlinType expectedType, - @Nullable Function1 additionalPredicate, - @Nullable KtExpression expression, - @NotNull ResolutionContext c, - @Nullable KtExpression calleeExpression, - boolean recordExpressionType - ) { - for (KotlinType possibleType : c.dataFlowInfo.getCollectedTypes(dataFlowValue)) { - if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType) - && (additionalPredicate == null || additionalPredicate.invoke(possibleType))) { - if (expression != null) { - recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType); + private fun recordCastOrError( + expression: KtExpression, + type: KotlinType, + trace: BindingTrace, + dataFlowValue: DataFlowValue, + recordExpressionType: Boolean) { + if (KotlinBuiltIns.isNullableNothing(type)) return + if (dataFlowValue.isPredictable) { + trace.record(SMARTCAST, expression, type) + if (recordExpressionType) { + //TODO + //Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary? + trace.recordType(expression, type) } - else if (calleeExpression != null && dataFlowValue.isPredictable()) { - c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression, possibleType); - } - return new SmartCastResult(possibleType, dataFlowValue.isPredictable()); + } + else { + trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.text, dataFlowValue.kind.description)) } } - if (!c.dataFlowInfo.getCollectedNullability(dataFlowValue).canBeNull() && !expectedType.isMarkedNullable()) { - // Handling cases like: - // fun bar(x: Any) {} - // fun foo(x: T) { - // if (x != null) { - // bar(x) // Should be allowed with smart cast - // } - // } - // - // It doesn't handled by lower code with getPossibleTypes because smart cast of T after `x != null` is still has same type T. - // But at the same time we're sure that `x` can't be null and just check for such cases manually + fun checkAndRecordPossibleCast( + dataFlowValue: DataFlowValue, + expectedType: KotlinType, + expression: KtExpression?, + c: ResolutionContext<*>, + calleeExpression: KtExpression?, + recordExpressionType: Boolean): SmartCastResult? { + return checkAndRecordPossibleCast( + dataFlowValue, expectedType, null, expression, c, calleeExpression, recordExpressionType) + } - // E.g. in case x!! when x has type of T where T is type parameter with nullable upper bounds - // x!! is immanently not null (see DataFlowValueFactory.createDataFlowValue for expression) - boolean immanentlyNotNull = !dataFlowValue.getImmanentNullability().canBeNull(); - KotlinType nullableExpectedType = TypeUtils.makeNullable(expectedType); - - if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.getType(), nullableExpectedType) - && (additionalPredicate == null || additionalPredicate.invoke(dataFlowValue.getType()))) { - if (!immanentlyNotNull) { + fun checkAndRecordPossibleCast( + dataFlowValue: DataFlowValue, + expectedType: KotlinType, + additionalPredicate: Function1?, + expression: KtExpression?, + c: ResolutionContext<*>, + calleeExpression: KtExpression?, + recordExpressionType: Boolean): SmartCastResult? { + for (possibleType in c.dataFlowInfo.getCollectedTypes(dataFlowValue)) { + if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType) && (additionalPredicate == null || additionalPredicate.invoke(possibleType))) { if (expression != null) { - recordCastOrError(expression, dataFlowValue.getType(), c.trace, dataFlowValue, recordExpressionType); + recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType) } + else if (calleeExpression != null && dataFlowValue.isPredictable) { + c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression, possibleType) + } + return SmartCastResult(possibleType, dataFlowValue.isPredictable) } - - return new SmartCastResult(dataFlowValue.getType(), immanentlyNotNull || dataFlowValue.isPredictable()); } - return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, calleeExpression, recordExpressionType); - } - return null; + if (!c.dataFlowInfo.getCollectedNullability(dataFlowValue).canBeNull() && !expectedType.isMarkedNullable) { + // Handling cases like: + // fun bar(x: Any) {} + // fun foo(x: T) { + // if (x != null) { + // bar(x) // Should be allowed with smart cast + // } + // } + // + // It doesn't handled by lower code with getPossibleTypes because smart cast of T after `x != null` is still has same type T. + // But at the same time we're sure that `x` can't be null and just check for such cases manually + + // E.g. in case x!! when x has type of T where T is type parameter with nullable upper bounds + // x!! is immanently not null (see DataFlowValueFactory.createDataFlowValue for expression) + val immanentlyNotNull = !dataFlowValue.immanentNullability.canBeNull() + val nullableExpectedType = TypeUtils.makeNullable(expectedType) + + if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.type, nullableExpectedType) && (additionalPredicate == null || additionalPredicate.invoke(dataFlowValue.type))) { + if (!immanentlyNotNull) { + if (expression != null) { + recordCastOrError(expression, dataFlowValue.type, c.trace, dataFlowValue, recordExpressionType) + } + } + + return SmartCastResult(dataFlowValue.type, immanentlyNotNull || dataFlowValue.isPredictable) + } + return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, calleeExpression, recordExpressionType) + } + + return null + } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java index ef6a899e22a..d295c994976 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java @@ -304,7 +304,7 @@ public class DataFlowAnalyzer { ) { DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c); - return SmartCastManager.checkAndRecordPossibleCast(dataFlowValue, c.expectedType, expression, c, null, false); + return SmartCastManager.Companion.checkAndRecordPossibleCast(dataFlowValue, c.expectedType, expression, c, null, false); } public void recordExpectedType(@NotNull BindingTrace trace, @NotNull KtExpression expression, @NotNull KotlinType expectedType) {