diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java index 2e1fd4f146f..5bb0f18315d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.java @@ -446,7 +446,7 @@ public class CandidateResolver { if (deparenthesizedArgument == null || type == null) return type; DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(deparenthesizedArgument, type, context); - if (!dataFlowValue.isStableIdentifier()) return type; + if (!dataFlowValue.isStableIdentifier() && !dataFlowValue.isLocalVariable()) return type; Set possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue); if (possibleTypes.isEmpty()) return type; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java index 2bdd2726205..b317507514b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowInfo.java @@ -24,6 +24,10 @@ import org.jetbrains.kotlin.types.JetType; import java.util.Map; import java.util.Set; +/** + * This interface is intended to provide and edit information about value nullabilities and possible types. + * Data flow info is immutable so functions never change it. + */ public interface DataFlowInfo { DataFlowInfo EMPTY = new DelegatingDataFlowInfo(null, ImmutableMap.of(), DelegatingDataFlowInfo.newTypeInfo()); @@ -39,18 +43,43 @@ public interface DataFlowInfo { @NotNull Set getPossibleTypes(@NotNull DataFlowValue key); + /** + * Call this function to clear all data flow information about + * the given data flow value. + */ + @NotNull + DataFlowInfo clearValueInfo(@NotNull DataFlowValue value); + + /** + * Call this function when b is assigned to a + */ + @NotNull + DataFlowInfo assign(@NotNull DataFlowValue a, @NotNull DataFlowValue b); + + /** + * Call this function when it's known than a == b + */ @NotNull DataFlowInfo equate(@NotNull DataFlowValue a, @NotNull DataFlowValue b); + /** + * Call this function when it's known than a != b + */ @NotNull DataFlowInfo disequate(@NotNull DataFlowValue a, @NotNull DataFlowValue b); @NotNull DataFlowInfo establishSubtyping(@NotNull DataFlowValue value, @NotNull JetType type); + /** + * Call this function to add data flow information from other to this and return sum as the result + */ @NotNull DataFlowInfo and(@NotNull DataFlowInfo other); + /** + * Call this function to choose data flow information common for this and other and return it as the result + */ @NotNull DataFlowInfo or(@NotNull DataFlowInfo other); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.java index 2b7d8ecd7d1..b3d2e943657 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.java @@ -22,20 +22,26 @@ import org.jetbrains.kotlin.types.ErrorUtils; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; +/** + * This class describes an arbitrary object which has some value in data flow analysis. + * In general case it's some r-value. + */ public class DataFlowValue { - public static final DataFlowValue NULL = new DataFlowValue(new Object(), KotlinBuiltIns.getInstance().getNullableNothingType(), false, Nullability.NULL); - public static final DataFlowValue NULLABLE = new DataFlowValue(new Object(), KotlinBuiltIns.getInstance().getNullableAnyType(), false, Nullability.UNKNOWN); - public static final DataFlowValue ERROR = new DataFlowValue(new Object(), ErrorUtils.createErrorType("Error type for data flow"), false, Nullability.IMPOSSIBLE); + public static final DataFlowValue NULL = new DataFlowValue(new Object(), KotlinBuiltIns.getInstance().getNullableNothingType(), false, false, Nullability.NULL); + public static final DataFlowValue NULLABLE = new DataFlowValue(new Object(), KotlinBuiltIns.getInstance().getNullableAnyType(), false, false, Nullability.UNKNOWN); + public static final DataFlowValue ERROR = new DataFlowValue(new Object(), ErrorUtils.createErrorType("Error type for data flow"), false, false, Nullability.IMPOSSIBLE); private final boolean stableIdentifier; + private final boolean localVariable; private final JetType type; private final Object id; private final Nullability immanentNullability; // Use DataFlowValueFactory - /*package*/ DataFlowValue(Object id, JetType type, boolean stableIdentifier, Nullability immanentNullability) { + /*package*/ DataFlowValue(Object id, JetType type, boolean stableIdentifier, boolean localVariable, Nullability immanentNullability) { this.stableIdentifier = stableIdentifier; + this.localVariable = localVariable; this.type = type; this.id = id; this.immanentNullability = immanentNullability; @@ -58,6 +64,13 @@ public class DataFlowValue { return stableIdentifier; } + /** + * Identifier is considered a local variable here if it's mutable (var), local and not captured in a closure + */ + public boolean isLocalVariable() { + return localVariable; + } + @NotNull public JetType getType() { return type; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.java index ef1ea010499..f594536ee0c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueFactory.java @@ -22,8 +22,10 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.JetNodeTypes; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; +import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage; import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; @@ -68,13 +70,17 @@ public class DataFlowValueFactory { return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?' } IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule); - return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id, type, result.isStable, getImmanentNullability(type)); + return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id, + type, + result.isStable, + result.isLocal, + getImmanentNullability(type)); } @NotNull public static DataFlowValue createDataFlowValue(@NotNull ThisReceiver receiver) { JetType type = receiver.getType(); - return new DataFlowValue(receiver, type, true, getImmanentNullability(type)); + return new DataFlowValue(receiver, type, true, false, getImmanentNullability(type)); } @NotNull @@ -96,7 +102,7 @@ public class DataFlowValueFactory { // SCRIPT: smartcasts data flow JetType type = receiverValue.getType(); boolean nullable = type.isMarkedNullable() || TypeUtils.hasNullableSuperType(type); - return new DataFlowValue(receiverValue, type, nullable, Nullability.NOT_NULL); + return new DataFlowValue(receiverValue, type, nullable, false, Nullability.NOT_NULL); } else if (receiverValue instanceof ClassReceiver || receiverValue instanceof ExtensionReceiver) { return createDataFlowValue((ThisReceiver) receiverValue); @@ -118,11 +124,13 @@ public class DataFlowValueFactory { @NotNull public static DataFlowValue createDataFlowValue( @NotNull VariableDescriptor variableDescriptor, + @NotNull BindingContext bindingContext, @Nullable ModuleDescriptor usageContainingModule ) { JetType type = variableDescriptor.getType(); return new DataFlowValue(variableDescriptor, type, isStableVariable(variableDescriptor, usageContainingModule), + isLocalVariable(variableDescriptor, bindingContext), getImmanentNullability(type)); } @@ -134,16 +142,18 @@ public class DataFlowValueFactory { private static class IdentifierInfo { public final Object id; public final boolean isStable; + public final boolean isLocal; public final boolean isPackage; - private IdentifierInfo(Object id, boolean isStable, boolean isPackage) { + private IdentifierInfo(Object id, boolean isStable, boolean isLocal, boolean isPackage) { this.id = id; this.isStable = isStable; + this.isLocal = isLocal; this.isPackage = isPackage; } } - private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, false, false) { + private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, false, false, false) { @Override public String toString() { return "NO_IDENTIFIER_INFO"; @@ -151,13 +161,13 @@ public class DataFlowValueFactory { }; @NotNull - private static IdentifierInfo createInfo(Object id, boolean isStable) { - return new IdentifierInfo(id, isStable, false); + private static IdentifierInfo createInfo(Object id, boolean isStable, boolean isLocal) { + return new IdentifierInfo(id, isStable, isLocal, false); } @NotNull private static IdentifierInfo createPackageInfo(Object id) { - return new IdentifierInfo(id, true, true); + return new IdentifierInfo(id, true, false, true); } @NotNull @@ -168,7 +178,9 @@ public class DataFlowValueFactory { if (receiverInfo == null || receiverInfo == NO_IDENTIFIER_INFO || receiverInfo.isPackage) { return selectorInfo; } - return createInfo(Pair.create(receiverInfo.id, selectorInfo.id), receiverInfo.isStable && selectorInfo.isStable); + return createInfo(Pair.create(receiverInfo.id, selectorInfo.id), + receiverInfo.isStable && selectorInfo.isStable, + false); } @NotNull @@ -227,7 +239,8 @@ public class DataFlowValueFactory { VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor; return combineInfo(receiverInfo, createInfo(variableDescriptor, - isStableVariable(variableDescriptor, usageModuleDescriptor))); + isStableVariable(variableDescriptor, usageModuleDescriptor), + isLocalVariable(variableDescriptor, bindingContext))); } if (declarationDescriptor instanceof PackageViewDescriptor) { return createPackageInfo(declarationDescriptor); @@ -254,14 +267,23 @@ public class DataFlowValueFactory { ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getExtensionReceiverParameter(); assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " + descriptorOfThisReceiver; - return createInfo(receiverParameter.getValue(), true); + return createInfo(receiverParameter.getValue(), true, false); } if (descriptorOfThisReceiver instanceof ClassDescriptor) { - return createInfo(((ClassDescriptor) descriptorOfThisReceiver).getThisAsReceiverParameter().getValue(), true); + return createInfo(((ClassDescriptor) descriptorOfThisReceiver).getThisAsReceiverParameter().getValue(), true, false); } return NO_IDENTIFIER_INFO; } + public static boolean isLocalVariable(@NotNull VariableDescriptor variableDescriptor, @NotNull BindingContext bindingContext) { + if (variableDescriptor.isVar() && variableDescriptor instanceof LocalVariableDescriptor) { + if (BindingContextUtils.isVarCapturedInClosure(bindingContext, variableDescriptor)) + return false; + return true; + } + return false; + } + /** * Determines whether a variable with a given descriptor is stable or not at the given usage place. *

diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.java index 7fa20d8df2c..bd617b7fb8c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DelegatingDataFlowInfo.java @@ -22,6 +22,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.TypeUtils; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -29,7 +30,7 @@ import java.util.Set; import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL; /* package */ class DelegatingDataFlowInfo implements DataFlowInfo { - private static final ImmutableMap EMPTY_NULLABILITY_INFO = ImmutableMap.of(); + private static final ImmutableMap EMPTY_NULLABILITY_INFO = ImmutableMap.of(); private static final SetMultimap EMPTY_TYPE_INFO = newTypeInfo(); @Nullable @@ -38,18 +39,37 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL @NotNull private final ImmutableMap nullabilityInfo; - /** Also immutable */ + /** + * Also immutable + */ @NotNull private final SetMultimap typeInfo; + /** + * Value for which type info was cleared at this point + * so parent type info should not be in use + */ + @Nullable + private final DataFlowValue valueWithGivenTypeInfo; + /* package */ DelegatingDataFlowInfo( @Nullable DataFlowInfo parent, @NotNull ImmutableMap nullabilityInfo, @NotNull SetMultimap typeInfo + ) { + this(parent, nullabilityInfo, typeInfo, null); + } + + /* package */ DelegatingDataFlowInfo( + @Nullable DataFlowInfo parent, + @NotNull ImmutableMap nullabilityInfo, + @NotNull SetMultimap typeInfo, + @Nullable DataFlowValue valueWithGivenTypeInfo ) { this.parent = parent; this.nullabilityInfo = nullabilityInfo; this.typeInfo = typeInfo; + this.valueWithGivenTypeInfo = valueWithGivenTypeInfo; } @Override @@ -74,10 +94,16 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL @NotNull public SetMultimap getCompleteTypeInfo() { SetMultimap result = newTypeInfo(); + Set resultCompleted = new HashSet(); DelegatingDataFlowInfo info = this; while (info != null) { for (DataFlowValue key : info.typeInfo.keySet()) { - result.putAll(key, info.typeInfo.get(key)); + if (!resultCompleted.contains(key)) { + result.putAll(key, info.typeInfo.get(key)); + } + } + if (valueWithGivenTypeInfo != null) { + resultCompleted.add(valueWithGivenTypeInfo); } info = (DelegatingDataFlowInfo) info.parent; } @@ -87,15 +113,19 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL @Override @NotNull public Nullability getNullability(@NotNull DataFlowValue key) { - if (!key.isStableIdentifier()) return key.getImmanentNullability(); + if (!key.isStableIdentifier() && !key.isLocalVariable()) return key.getImmanentNullability(); Nullability nullability = nullabilityInfo.get(key); return nullability != null ? nullability : parent != null ? parent.getNullability(key) : key.getImmanentNullability(); } - private boolean putNullability(@NotNull Map map, @NotNull DataFlowValue value, @NotNull Nullability nullability) { - if (!value.isStableIdentifier()) return false; + private boolean putNullability( + @NotNull Map map, + @NotNull DataFlowValue value, + @NotNull Nullability nullability + ) { + if (!value.isStableIdentifier() && !value.isLocalVariable()) return false; map.put(value, nullability); return nullability != getNullability(value); } @@ -103,8 +133,7 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL @Override @NotNull public Set getPossibleTypes(@NotNull DataFlowValue key) { - Set theseTypes = typeInfo.get(key); - Set types = parent == null ? theseTypes : Sets.union(theseTypes, parent.getPossibleTypes(key)); + Set types = collectTypesFromMeAndParents(key); if (getNullability(key).canBeNull()) { return types; } @@ -121,6 +150,56 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL return enrichedTypes; } + /** + * Call this function to clear all data flow information about + * the given data flow value. + * + * @param value + */ + @Override + @NotNull + public DataFlowInfo clearValueInfo(@NotNull DataFlowValue value) { + Map builder = Maps.newHashMap(); + boolean changed = putNullability(builder, value, Nullability.UNKNOWN); + // We want to clear all these types + if (!changed) { + changed = !collectTypesFromMeAndParents(value).isEmpty(); + } + return !changed + ? this + : new DelegatingDataFlowInfo( + this, + ImmutableMap.copyOf(builder), + EMPTY_TYPE_INFO, + value + ); + } + + @Override + @NotNull + public DataFlowInfo assign(@NotNull DataFlowValue a, @NotNull DataFlowValue b) { + Map builder = Maps.newHashMap(); + Nullability nullabilityOfB = getNullability(b); + boolean changed = putNullability(builder, a, nullabilityOfB); + SetMultimap newTypeInfo = newTypeInfo(); + Set typesForA = collectTypesFromMeAndParents(a); + Set typesForB = collectTypesFromMeAndParents(b); + if (nullabilityOfB.canBeNonNull() && !a.getType().equals(b.getType())) { + typesForB.add(b.getType()); + } + newTypeInfo.putAll(a, typesForB); + changed |= !typesForA.equals(typesForB); + + return !changed + ? this + : new DelegatingDataFlowInfo( + this, + ImmutableMap.copyOf(builder), + newTypeInfo.isEmpty() ? EMPTY_TYPE_INFO : newTypeInfo, + a + ); + } + @Override @NotNull public DataFlowInfo equate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) { @@ -155,7 +234,12 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL if (current instanceof DelegatingDataFlowInfo) { DelegatingDataFlowInfo delegatingInfo = (DelegatingDataFlowInfo) current; types.addAll(delegatingInfo.typeInfo.get(value)); - current = delegatingInfo.parent; + if (value.equals(delegatingInfo.valueWithGivenTypeInfo)) { + current = null; + } + else { + current = delegatingInfo.parent; + } } else { types.addAll(current.getPossibleTypes(value)); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastUtils.java index e3f633b44a7..1ac8e36f94d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastUtils.java @@ -179,7 +179,11 @@ public class SmartCastUtils { JetExpression expression = ((ExpressionReceiver) receiver).getExpression(); DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context); - recordCastOrError(expression, smartCastSubType, context.trace, dataFlowValue.isStableIdentifier(), true); + recordCastOrError(expression, + smartCastSubType, + context.trace, + dataFlowValue.isStableIdentifier() || dataFlowValue.isLocalVariable(), + true); return true; } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java index 70724f4e5d6..d1f2c96e9ef 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -206,6 +206,11 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency( INDEPENDENT); + // Preliminary analysis + PreliminaryLoopVisitor loopVisitor = new PreliminaryLoopVisitor(expression); + loopVisitor.launch(); + context = context.replaceDataFlowInfo(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo)); + JetExpression condition = expression.getCondition(); // Extract data flow info from condition itself without taking value into account DataFlowInfo dataFlowInfo = checkCondition(context.scope, condition, context); @@ -230,7 +235,9 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { // .and it with entrance data flow information, because while body until break is executed at least once in this case // See KT-6284 if (bodyTypeInfo != null && isTrueConstant(condition)) { - dataFlowInfo = dataFlowInfo.and(bodyTypeInfo.getJumpFlowInfo()); + // We should take data flow info from the first jump point, + // but without affecting changing variables + dataFlowInfo = dataFlowInfo.and(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(bodyTypeInfo.getJumpFlowInfo())); } return DataFlowUtils.checkType(components.builtIns.getUnitType(), expression, contextWithExpectedType, dataFlowInfo); } @@ -286,6 +293,13 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT); JetExpression body = expression.getBody(); JetScope conditionScope = context.scope; + // Preliminary analysis + PreliminaryLoopVisitor loopVisitor = new PreliminaryLoopVisitor(expression); + loopVisitor.launch(); + context = context.replaceDataFlowInfo(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo)); + // Here we must record data flow information at the end of the body (or at the first jump, to be precise) and + // .and it with entrance data flow information, because do-while body is executed at least once + // See KT-6283 LoopTypeInfo bodyTypeInfo = null; if (body instanceof JetFunctionLiteralExpression) { JetFunctionLiteralExpression function = (JetFunctionLiteralExpression) body; @@ -328,7 +342,9 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { // .and it with entrance data flow information, because do-while body is executed at least once // See KT-6283 if (bodyTypeInfo != null) { - dataFlowInfo = dataFlowInfo.and(bodyTypeInfo.getJumpFlowInfo()); + // We should take data flow info from the first jump point, + // but without affecting changing variables + dataFlowInfo = dataFlowInfo.and(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(bodyTypeInfo.getJumpFlowInfo())); } return DataFlowUtils.checkType(components.builtIns.getUnitType(), expression, contextWithExpectedType, dataFlowInfo); } @@ -343,6 +359,11 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT); + // Preliminary analysis + PreliminaryLoopVisitor loopVisitor = new PreliminaryLoopVisitor(expression); + loopVisitor.launch(); + context = context.replaceDataFlowInfo(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo)); + JetExpression loopRange = expression.getLoopRange(); JetType expectedParameterType = null; DataFlowInfo dataFlowInfo = context.dataFlowInfo; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowUtils.java index 7d2cab0b3d3..d8dcbd64e22 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowUtils.java @@ -199,7 +199,11 @@ public class DataFlowUtils { for (JetType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) { if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, c.expectedType)) { - SmartCastUtils.recordCastOrError(expression, possibleType, c.trace, dataFlowValue.isStableIdentifier(), false); + SmartCastUtils.recordCastOrError(expression, + possibleType, + c.trace, + dataFlowValue.isStableIdentifier() || dataFlowValue.isLocalVariable(), + false); return possibleType; } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java index 43de648074a..1c3832a5fe9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorForStatements.java @@ -141,11 +141,15 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito JetTypeInfo typeInfo = facade.getTypeInfo(initializer, context.replaceExpectedType(outType)); dataFlowInfo = typeInfo.getDataFlowInfo(); JetType type = typeInfo.getType(); + // At this moment we do not take initializer value into account if type is given for a property + // We can comment first part of this condition to take them into account, like here: var s: String? = "xyz" + // In this case s will be not-nullable until it is changed if (property.getTypeReference() == null && type != null) { DataFlowValue variableDataFlowValue = DataFlowValueFactory.createDataFlowValue( - propertyDescriptor, DescriptorUtils.getContainingModuleOrNull(scope.getContainingDeclaration())); + propertyDescriptor, context.trace.getBindingContext(), + DescriptorUtils.getContainingModuleOrNull(scope.getContainingDeclaration())); DataFlowValue initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context); - dataFlowInfo = dataFlowInfo.equate(variableDataFlowValue, initializerDataFlowValue); + dataFlowInfo = dataFlowInfo.assign(variableDataFlowValue, initializerDataFlowValue); } } @@ -346,7 +350,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito if (left != null && leftType != null && rightType != null) { DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, leftType, context); DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context); - dataFlowInfo = dataFlowInfo.equate(leftValue, rightValue); + dataFlowInfo = dataFlowInfo.assign(leftValue, rightValue); } } if (leftType != null && leftOperand != null) { //if leftType == null, some other error has been generated diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.java new file mode 100644 index 00000000000..c89ea9ec625 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.java @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2015 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 org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; +import org.jetbrains.kotlin.lexer.JetTokens; +import org.jetbrains.kotlin.name.Name; +import org.jetbrains.kotlin.psi.*; +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue; +import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability; + +import java.util.*; + +/** + * The purpose of this class is to find all variable assignments + * before loop analysis + */ +class PreliminaryLoopVisitor extends JetTreeVisitor { + + // loop under analysis + private final JetLoopExpression loopExpression; + + private Set assignedNames = new LinkedHashSet(); + + public PreliminaryLoopVisitor(JetLoopExpression loopExpression) { + this.loopExpression = loopExpression; + } + + public void launch() { + loopExpression.accept(this, null); + } + + public DataFlowInfo clearDataFlowInfoForAssignedLocalVariables(DataFlowInfo dataFlowInfo) { + Map nullabilityMap = dataFlowInfo.getCompleteNullabilityInfo(); + Set valueSetToClear = new LinkedHashSet(); + for (DataFlowValue value: nullabilityMap.keySet()) { + // Only local variables which are not stable are under interest + if (value.isStableIdentifier() || !value.isLocalVariable()) + continue; + if (value.getId() instanceof LocalVariableDescriptor) { + LocalVariableDescriptor descriptor = (LocalVariableDescriptor)value.getId(); + if (assignedNames.contains(descriptor.getName())) { + valueSetToClear.add(value); + } + } + } + for (DataFlowValue valueToClear: valueSetToClear) { + dataFlowInfo = dataFlowInfo.clearValueInfo(valueToClear); + } + return dataFlowInfo; + } + + @Override + public Void visitLoopExpression(@NotNull JetLoopExpression loopExpression, Void arg) { + return super.visitLoopExpression(loopExpression, arg); + } + + @Override + public Void visitBinaryExpression(@NotNull JetBinaryExpression binaryExpression, Void arg) { + if (binaryExpression.getOperationToken() == JetTokens.EQ && binaryExpression.getLeft() instanceof JetNameReferenceExpression) { + assignedNames.add(((JetNameReferenceExpression) binaryExpression.getLeft()).getReferencedNameAsName()); + } + return null; + } + +} diff --git a/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.kt b/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.kt index 3e1c08355c0..333670762e6 100644 --- a/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.kt +++ b/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.kt @@ -4,7 +4,7 @@ class A() { fun f(): Unit { var x: Int? = 1 - x = 1 + x = null x + 1 x plus 1 x < 1 diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt2845.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt2845.kt index 82582f733a3..957ca6e3b2f 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt2845.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt2845.kt @@ -7,7 +7,8 @@ private fun doTest() : Int { var list : MutableList? ; try { list = ArrayList() - list?.add(3) + // Not-null was just assigned to the list + list.add(3) return 0 ; } finally { diff --git a/compiler/testData/diagnostics/tests/infos/SmartCasts.kt b/compiler/testData/diagnostics/tests/infos/SmartCasts.kt index 8be5836d093..c4a76ffda9b 100644 --- a/compiler/testData/diagnostics/tests/infos/SmartCasts.kt +++ b/compiler/testData/diagnostics/tests/infos/SmartCasts.kt @@ -202,18 +202,20 @@ fun mergeSmartCasts(a: Any?) { fun f(): String { var a: Any = 11 if (a is String) { - val i: String = a - a.compareTo("f") + // a is a string, despite of being a variable + val i: String = a + a.compareTo("f") + // Beginning from here a is captured in a closure so we have to be cautious val f: Function0 = { a } return a } return "" } -fun foo(aa: Any): Int { +fun foo(aa: Any?): Int { var a = aa - if (a is Int) { - return a + if (a is Int?) { + return a } return 1 } diff --git a/compiler/testData/diagnostics/tests/infos/SmartCasts.txt b/compiler/testData/diagnostics/tests/infos/SmartCasts.txt index 8ad7671cf0e..288f80c9021 100644 --- a/compiler/testData/diagnostics/tests/infos/SmartCasts.txt +++ b/compiler/testData/diagnostics/tests/infos/SmartCasts.txt @@ -10,7 +10,7 @@ internal fun f13(/*0*/ a: A?): kotlin.Unit internal fun f14(/*0*/ a: A?): kotlin.Unit internal fun f15(/*0*/ a: A?): kotlin.Unit internal fun f9(/*0*/ init: A?): kotlin.Unit -internal fun foo(/*0*/ aa: kotlin.Any): kotlin.Int +internal fun foo(/*0*/ aa: kotlin.Any?): kotlin.Int internal fun getStringLength(/*0*/ obj: kotlin.Any): kotlin.Char? internal fun illegalWhenBlock(/*0*/ a: kotlin.Any): kotlin.Int internal fun illegalWhenBody(/*0*/ a: kotlin.Any): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/inference/kt4009.kt b/compiler/testData/diagnostics/tests/smartCasts/inference/kt4009.kt index 6fef9f3402e..98f3a0c5c9a 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/inference/kt4009.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/inference/kt4009.kt @@ -10,10 +10,11 @@ fun foo1(e: PsiElement) { var first = true while (current != null) { if (current is JetExpression && first) { - println(current!!.getText()) // error: smart cast not possible. But it's not needed in fact! + // Smartcast is possible here + println(current.getText()) } - current = current?.getParent() + current = current.getParent() } } diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/assignment.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/assignment.kt new file mode 100644 index 00000000000..2d4fbf48c3b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/assignment.kt @@ -0,0 +1,10 @@ +fun foo() { + var v: Any = 42 + v.length() + v = "abc" + v.length() + v = 42 + v.length() + v = "abc" + v.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/assignment.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/assignment.txt new file mode 100644 index 00000000000..093bfc34070 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/assignment.txt @@ -0,0 +1,3 @@ +package + +internal fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/doWhileWithMiddleBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/doWhileWithMiddleBreak.kt new file mode 100644 index 00000000000..603f5f77f34 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/doWhileWithMiddleBreak.kt @@ -0,0 +1,12 @@ +fun x(): Boolean { return true } + +public fun foo(pp: Any): Int { + var p = pp + do { + (p as String).length() + if (p == "abc") break + p = 42 + } while (!x()) + // Smart cast is NOT possible here + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/doWhileWithMiddleBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/doWhileWithMiddleBreak.txt new file mode 100644 index 00000000000..137acd4e2f9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/doWhileWithMiddleBreak.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ pp: kotlin.Any): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhile.kt new file mode 100644 index 00000000000..198103b38ea --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhile.kt @@ -0,0 +1,15 @@ +public fun foo(xx: Any): Int { + var x = xx + do { + var y: Any + // After the check, smart cast should work + if (x is String) { + y = "xyz" + } else { + y = "abc" + } + // y!! in both branches + y.length() + } while (!(x is String)) + return x.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhile.txt new file mode 100644 index 00000000000..85bcd871133 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhile.txt @@ -0,0 +1,3 @@ +package + +public fun foo(/*0*/ xx: kotlin.Any): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhileWithBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhileWithBreak.kt new file mode 100644 index 00000000000..57226d4b288 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhileWithBreak.kt @@ -0,0 +1,16 @@ +public fun foo(xx: Any): Int { + var x = xx + do { + var y: Any + // After the check, smart cast should work + if (x is String) { + break + } else { + y = "abc" + } + // y!! in both branches + y.length() + } while (true) + // We could have smart cast here but with break it's hard to detect + return x.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhileWithBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhileWithBreak.txt new file mode 100644 index 00000000000..85bcd871133 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhileWithBreak.txt @@ -0,0 +1,3 @@ +package + +public fun foo(/*0*/ xx: kotlin.Any): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIs.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIs.kt new file mode 100644 index 00000000000..dec7ee43d4c --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIs.kt @@ -0,0 +1,9 @@ +public fun bar(s: String) { + System.out.println("Length of $s is ${s.length()}") +} + +public fun foo() { + var s: Any = "not null" + if (s is String) + bar(s) +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIs.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIs.txt new file mode 100644 index 00000000000..fe5597d0052 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIs.txt @@ -0,0 +1,4 @@ +package + +public fun bar(/*0*/ s: kotlin.String): kotlin.Unit +public fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsAnd.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsAnd.kt new file mode 100644 index 00000000000..a6c2d839660 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsAnd.kt @@ -0,0 +1,13 @@ +// See KT-5737 +fun get(): Any { + return "abc" +} + +fun foo(): Int { + var ss: Any = get() + + return if (ss is String && ss.length() > 0) + 1 + else + 0 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsAnd.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsAnd.txt new file mode 100644 index 00000000000..58090ddd69f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsAnd.txt @@ -0,0 +1,4 @@ +package + +internal fun foo(): kotlin.Int +internal fun get(): kotlin.Any diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsChanged.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsChanged.kt new file mode 100644 index 00000000000..89bb8a61ffc --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsChanged.kt @@ -0,0 +1,11 @@ +public fun bar(s: String) { + System.out.println("Length of $s is ${s.length()}") +} + +public fun foo() { + var s: Any = "not null" + if (s is String) { + s = 42 + bar(s) + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsChanged.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsChanged.txt new file mode 100644 index 00000000000..fe5597d0052 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsChanged.txt @@ -0,0 +1,4 @@ +package + +public fun bar(/*0*/ s: kotlin.String): kotlin.Unit +public fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/infix.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/infix.kt new file mode 100644 index 00000000000..9962911e8cb --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/infix.kt @@ -0,0 +1,11 @@ +// See KT-774 +fun box() : Int { + var a : Any = 1 + var d = 1 + + if (a is Int) { + return a + d + } else { + return 2 + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/infix.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/infix.txt new file mode 100644 index 00000000000..e9d4ded0afc --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/infix.txt @@ -0,0 +1,3 @@ +package + +internal fun box(): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/initialization.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/initialization.kt new file mode 100644 index 00000000000..3c3b0d72337 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/initialization.kt @@ -0,0 +1,8 @@ +fun foo() { + var v: Any = "xyz" + // It is possible in principle to provide smart cast here + // but now we decide that v is Any + v.length() + v = 42 + v.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/initialization.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/initialization.txt new file mode 100644 index 00000000000..093bfc34070 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/initialization.txt @@ -0,0 +1,3 @@ +package + +internal fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/property.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/property.kt new file mode 100644 index 00000000000..c0b560d3c28 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/property.kt @@ -0,0 +1,10 @@ +class MyClass(var p: String?) + +fun bar(s: String): Int { + return s.length() +} + +fun foo(m: MyClass): Int { + m.p = "xyz" + return bar(m.p) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/property.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/property.txt new file mode 100644 index 00000000000..3902b8ddebc --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/property.txt @@ -0,0 +1,12 @@ +package + +internal fun bar(/*0*/ s: kotlin.String): kotlin.Int +internal fun foo(/*0*/ m: MyClass): kotlin.Int + +internal final class MyClass { + public constructor MyClass(/*0*/ p: kotlin.String?) + internal final var p: kotlin.String? + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/propertyNotNeeded.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/propertyNotNeeded.kt new file mode 100644 index 00000000000..4ee8c235c41 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/propertyNotNeeded.kt @@ -0,0 +1,10 @@ +class MyClass(var p: String?) + +fun bar(s: String?): Int { + return s?.length() ?: -1 +} + +fun foo(m: MyClass): Int { + m.p = "xyz" + return bar(m.p) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/propertyNotNeeded.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/propertyNotNeeded.txt new file mode 100644 index 00000000000..72ea1b5f8b2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/propertyNotNeeded.txt @@ -0,0 +1,12 @@ +package + +internal fun bar(/*0*/ s: kotlin.String?): kotlin.Int +internal fun foo(/*0*/ m: MyClass): kotlin.Int + +internal final class MyClass { + public constructor MyClass(/*0*/ p: kotlin.String?) + internal final var p: kotlin.String? + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtype.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtype.kt new file mode 100644 index 00000000000..2563e8c449f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtype.kt @@ -0,0 +1,10 @@ +class MyClass(var p: Any) + +fun bar(s: Any): Int { + return s.hashCode() +} + +fun foo(m: MyClass): Int { + m.p = "xyz" + return bar(m.p) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtype.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtype.txt new file mode 100644 index 00000000000..16fc32372aa --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtype.txt @@ -0,0 +1,12 @@ +package + +internal fun bar(/*0*/ s: kotlin.Any): kotlin.Int +internal fun foo(/*0*/ m: MyClass): kotlin.Int + +internal final class MyClass { + public constructor MyClass(/*0*/ p: kotlin.Any) + internal final var p: kotlin.Any + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMember.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMember.kt new file mode 100644 index 00000000000..a47fce835c2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMember.kt @@ -0,0 +1,10 @@ +fun bar(s: Any): Int { + return s.hashCode() +} + +class MyClass(var p: Any) { + fun foo(): Int { + p = "xyz" + return bar(p) + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMember.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMember.txt new file mode 100644 index 00000000000..498b537a14d --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMember.txt @@ -0,0 +1,12 @@ +package + +internal fun bar(/*0*/ s: kotlin.Any): kotlin.Int + +internal final class MyClass { + public constructor MyClass(/*0*/ p: kotlin.Any) + internal final var p: kotlin.Any + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final fun foo(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMemberCheck.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMemberCheck.kt new file mode 100644 index 00000000000..1575866e196 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMemberCheck.kt @@ -0,0 +1,13 @@ +fun bar(s: Any): Int { + return s.hashCode() +} + +class MyClass(var p: Any) { + fun foo(): Int { + p = "xyz" + if (p is String) { + return bar(p) + } + return -1 + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMemberCheck.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMemberCheck.txt new file mode 100644 index 00000000000..498b537a14d --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMemberCheck.txt @@ -0,0 +1,12 @@ +package + +internal fun bar(/*0*/ s: kotlin.Any): kotlin.Int + +internal final class MyClass { + public constructor MyClass(/*0*/ p: kotlin.Any) + internal final var p: kotlin.Any + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final fun foo(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/varAsUse.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/varAsUse.kt new file mode 100644 index 00000000000..3d1c084c500 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/varAsUse.kt @@ -0,0 +1,9 @@ +fun get(): Any { + return "" +} + +fun foo(): Int { + var c: Any = get() + (c as String).length() + return c.length() // Previous line should make as unnecessary here. +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/varAsUse.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/varAsUse.txt new file mode 100644 index 00000000000..58090ddd69f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/varAsUse.txt @@ -0,0 +1,4 @@ +package + +internal fun foo(): kotlin.Int +internal fun get(): kotlin.Any diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/varChangedInLoop.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/varChangedInLoop.kt new file mode 100644 index 00000000000..0a128a4bdb1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/varChangedInLoop.kt @@ -0,0 +1,9 @@ +public fun foo() { + var i: Any = 1 + if (i is Int) { + while (i != 10) { + i++ // Here smart cast should not be performed due to a successor + i = "" + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/varChangedInLoop.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/varChangedInLoop.txt new file mode 100644 index 00000000000..65a6ac47e1f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/varChangedInLoop.txt @@ -0,0 +1,3 @@ +package + +public fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/varNotChangedInLoop.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/varNotChangedInLoop.kt new file mode 100644 index 00000000000..91a70226b9e --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/varNotChangedInLoop.kt @@ -0,0 +1,8 @@ +public fun foo() { + var i: Any = 1 + if (i is Int) { + while (i != 10) { + i++ + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/varNotChangedInLoop.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/varNotChangedInLoop.txt new file mode 100644 index 00000000000..65a6ac47e1f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/varNotChangedInLoop.txt @@ -0,0 +1,3 @@ +package + +public fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/whileTrue.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/whileTrue.kt new file mode 100644 index 00000000000..9333c060982 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/whileTrue.kt @@ -0,0 +1,13 @@ +fun x(): Boolean { return true } + +public fun foo(pp: Any): Int { + var p = pp + while(true) { + (p as String).length() + if (x()) break + p = 42 + } + // Smart cast is NOT possible here + // (we could provide it but p = 42 makes it difficult to understand) + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/whileTrue.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/whileTrue.txt new file mode 100644 index 00000000000..137acd4e2f9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/whileTrue.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ pp: kotlin.Any): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/whileWithBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/variables/whileWithBreak.kt new file mode 100644 index 00000000000..5babe4a7f82 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/whileWithBreak.kt @@ -0,0 +1,17 @@ +fun String.next(): String { + return "abc" +} + +fun list(start: String) { + var e: Any? = start + if (e==null) return + while (e is String) { + // Smart cast due to the loop condition + if (e.length() == 0) + break + // We still have smart cast here despite of a break + e = e.next() + } + // e can never be null but we do not know it + e.hashCode() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/variables/whileWithBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/variables/whileWithBreak.txt new file mode 100644 index 00000000000..92bffdbe76b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/variables/whileWithBreak.txt @@ -0,0 +1,4 @@ +package + +internal fun list(/*0*/ start: kotlin.String): kotlin.Unit +internal fun kotlin.String.next(): kotlin.String diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/assignment.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/assignment.kt new file mode 100644 index 00000000000..b1f7ef5f918 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/assignment.kt @@ -0,0 +1,10 @@ +fun foo() { + var v: String? = null + v.length() + v = "abc" + v.length() + v = null + v.length() + v = "abc" + v.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/assignment.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/assignment.txt new file mode 100644 index 00000000000..093bfc34070 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/assignment.txt @@ -0,0 +1,3 @@ +package + +internal fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithBreak.kt new file mode 100644 index 00000000000..aab92ac48e5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithBreak.kt @@ -0,0 +1,21 @@ +data class SomeObject(val n: SomeObject?) { + fun doSomething(): Boolean = true + fun next(): SomeObject? = n +} + + +fun list(start: SomeObject) { + var e: SomeObject? + e = start + do { + // In theory smart cast is possible here + // But in practice we have a loop with changing e + // ?: should we "or" entrance type info with condition type info? + if (!e.doSomething()) + break + // Smart cast here is still not possible + e = e.next() + } while (e != null) + // e can be null because of next() + e.doSomething() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithBreak.txt new file mode 100644 index 00000000000..2472f883754 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithBreak.txt @@ -0,0 +1,15 @@ +package + +internal fun list(/*0*/ start: SomeObject): kotlin.Unit + +kotlin.data() internal final class SomeObject { + public constructor SomeObject(/*0*/ n: SomeObject?) + internal final val n: SomeObject? + internal final /*synthesized*/ fun component1(): SomeObject? + public final /*synthesized*/ fun copy(/*0*/ n: SomeObject? = ...): SomeObject + internal final fun doSomething(): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final fun next(): SomeObject? + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithMiddleBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithMiddleBreak.kt new file mode 100644 index 00000000000..f22c5f70fd0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithMiddleBreak.kt @@ -0,0 +1,12 @@ +fun x(): Boolean { return true } + +public fun foo(pp: String?): Int { + var p = pp + do { + p!!.length() + if (p == "abc") break + p = null + } while (!x()) + // Smart cast is NOT possible here + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithMiddleBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithMiddleBreak.txt new file mode 100644 index 00000000000..23bf0a2ca9a --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithMiddleBreak.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ pp: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEach.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEach.kt new file mode 100644 index 00000000000..99460ce9520 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEach.kt @@ -0,0 +1,16 @@ +data class SomeObject(val n: SomeObject?) { + fun doSomething() {} + fun next(): SomeObject? = n +} + + +fun list(start: SomeObject): SomeObject { + var e: SomeObject? = start + for (i in 0..42) { + // Unsafe calls because of nullable e at the beginning + e.doSomething() + e = e.next() + } + // Smart cast is not possible here due to next() + return e +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEach.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEach.txt new file mode 100644 index 00000000000..a3991447657 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEach.txt @@ -0,0 +1,15 @@ +package + +internal fun list(/*0*/ start: SomeObject): SomeObject + +kotlin.data() internal final class SomeObject { + public constructor SomeObject(/*0*/ n: SomeObject?) + internal final val n: SomeObject? + internal final /*synthesized*/ fun component1(): SomeObject? + public final /*synthesized*/ fun copy(/*0*/ n: SomeObject? = ...): SomeObject + internal final fun doSomething(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final fun next(): SomeObject? + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithBreak.kt new file mode 100644 index 00000000000..89510103255 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithBreak.kt @@ -0,0 +1,17 @@ +data class SomeObject(val n: SomeObject?) { + fun doSomething() {} + fun next(): SomeObject? = n +} + + +fun list(start: SomeObject): SomeObject { + var e: SomeObject? = start + for (i in 0..42) { + if (e == null) + break + // Smart casts are possible because of the break before + e.doSomething() + e = e.next() + } + return e +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithBreak.txt new file mode 100644 index 00000000000..a3991447657 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithBreak.txt @@ -0,0 +1,15 @@ +package + +internal fun list(/*0*/ start: SomeObject): SomeObject + +kotlin.data() internal final class SomeObject { + public constructor SomeObject(/*0*/ n: SomeObject?) + internal final val n: SomeObject? + internal final /*synthesized*/ fun component1(): SomeObject? + public final /*synthesized*/ fun copy(/*0*/ n: SomeObject? = ...): SomeObject + internal final fun doSomething(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final fun next(): SomeObject? + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithContinue.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithContinue.kt new file mode 100644 index 00000000000..540dc379650 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithContinue.kt @@ -0,0 +1,17 @@ +data class SomeObject(val n: SomeObject?) { + fun doSomething() {} + fun next(): SomeObject? = n +} + + +fun list(start: SomeObject): SomeObject { + var e: SomeObject? = start + for (i in 0..42) { + if (e == null) + continue + // Smart casts are possible because of the continue before + e.doSomething() + e = e.next() + } + return e +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithContinue.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithContinue.txt new file mode 100644 index 00000000000..a3991447657 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithContinue.txt @@ -0,0 +1,15 @@ +package + +internal fun list(/*0*/ start: SomeObject): SomeObject + +kotlin.data() internal final class SomeObject { + public constructor SomeObject(/*0*/ n: SomeObject?) + internal final val n: SomeObject? + internal final /*synthesized*/ fun component1(): SomeObject? + public final /*synthesized*/ fun copy(/*0*/ n: SomeObject? = ...): SomeObject + internal final fun doSomething(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final fun next(): SomeObject? + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNull.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNull.kt new file mode 100644 index 00000000000..df0d175e361 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNull.kt @@ -0,0 +1,9 @@ +public fun fooNotNull(s: String) { + System.out.println("Length of $s is ${s.length()}") +} + +public fun foo() { + var s: String? = "not null" + if (s != null) + fooNotNull(s) +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNull.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNull.txt new file mode 100644 index 00000000000..a861c1aed56 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNull.txt @@ -0,0 +1,4 @@ +package + +public fun foo(): kotlin.Unit +public fun fooNotNull(/*0*/ s: kotlin.String): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNullAnd.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNullAnd.kt new file mode 100644 index 00000000000..be1b1d8a7d5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNullAnd.kt @@ -0,0 +1,13 @@ +// See KT-5737 +fun get(): String? { + return "abc" +} + +fun foo(): Int { + var ss:String? = get() + + return if (ss != null && ss.length() > 0) + 1 + else + 0 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNullAnd.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNullAnd.txt new file mode 100644 index 00000000000..3c9a108493d --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNullAnd.txt @@ -0,0 +1,4 @@ +package + +internal fun foo(): kotlin.Int +internal fun get(): kotlin.String? diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullElse.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullElse.kt new file mode 100644 index 00000000000..c9c82755282 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullElse.kt @@ -0,0 +1,12 @@ +public fun fooNotNull(s: String) { + System.out.println("Length of $s is ${s.length()}") +} + +public fun foo() { + var s: String? = "not null" + if (s == null) { + // Coming soon + } else { + fooNotNull(s) + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullElse.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullElse.txt new file mode 100644 index 00000000000..a861c1aed56 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullElse.txt @@ -0,0 +1,4 @@ +package + +public fun foo(): kotlin.Unit +public fun fooNotNull(/*0*/ s: kotlin.String): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullReturn.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullReturn.kt new file mode 100644 index 00000000000..8264a48eafb --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullReturn.kt @@ -0,0 +1,11 @@ +public fun fooNotNull(s: String) { + System.out.println("Length of $s is ${s.length()}") +} + +public fun foo() { + var s: String? = "not null" + if (s == null) { + return + } + fooNotNull(s) +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullReturn.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullReturn.txt new file mode 100644 index 00000000000..a861c1aed56 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullReturn.txt @@ -0,0 +1,4 @@ +package + +public fun foo(): kotlin.Unit +public fun fooNotNull(/*0*/ s: kotlin.String): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/inference.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/inference.kt new file mode 100644 index 00000000000..6620e89f9b4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/inference.kt @@ -0,0 +1,21 @@ +// See KT-969 +fun f() { + var s: String? + s = "a" + var s1 = "" // String – ? + if (s != null) { // Redundant + s1.length() + // We can do smartcast here and below + s1 = s.toString() // return String? + s1.length() + s1 = s + s1.length() + // It's just an assignment without smartcast + val s2 = s + // But smartcast can be done here + s2.length() + // And also here + val s3 = s.toString() + s3.length() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/inference.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/inference.txt new file mode 100644 index 00000000000..f8a8ce9c080 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/inference.txt @@ -0,0 +1,3 @@ +package + +internal fun f(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infiniteWhileWithBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infiniteWhileWithBreak.kt new file mode 100644 index 00000000000..9cc6c3b9fef --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infiniteWhileWithBreak.kt @@ -0,0 +1,20 @@ +data class SomeObject(val n: SomeObject?) { + fun doSomething(): Boolean = true + fun next(): SomeObject? = n +} + + +fun list(start: SomeObject) { + var e: SomeObject? + e = start + // This comparison is senseless + while (e != null) { + // Smart cast because of the loop condition + if (!e.doSomething()) + break + // We still have smart cast here despite of a break + e = e.next() + } + // e can be null because of next() + e.doSomething() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infiniteWhileWithBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infiniteWhileWithBreak.txt new file mode 100644 index 00000000000..2472f883754 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infiniteWhileWithBreak.txt @@ -0,0 +1,15 @@ +package + +internal fun list(/*0*/ start: SomeObject): kotlin.Unit + +kotlin.data() internal final class SomeObject { + public constructor SomeObject(/*0*/ n: SomeObject?) + internal final val n: SomeObject? + internal final /*synthesized*/ fun component1(): SomeObject? + public final /*synthesized*/ fun copy(/*0*/ n: SomeObject? = ...): SomeObject + internal final fun doSomething(): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final fun next(): SomeObject? + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infix.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infix.kt new file mode 100644 index 00000000000..eb6f0051838 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infix.kt @@ -0,0 +1,11 @@ +// See KT-774 +fun box() : Int { + var a : Int? = 1 + var d = 1 + + if (a == null) { + return 2 + } else { + return a + d + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infix.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infix.txt new file mode 100644 index 00000000000..e9d4ded0afc --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/infix.txt @@ -0,0 +1,3 @@ +package + +internal fun box(): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initialization.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initialization.kt new file mode 100644 index 00000000000..ea52b2ba131 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initialization.kt @@ -0,0 +1,7 @@ +fun foo() { + var v: String? = "xyz" + // It is possible in principle to provide smart cast here + v.length() + v = null + v.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initialization.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initialization.txt new file mode 100644 index 00000000000..093bfc34070 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/initialization.txt @@ -0,0 +1,3 @@ +package + +internal fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/iterations.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/iterations.kt new file mode 100644 index 00000000000..c061e951132 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/iterations.kt @@ -0,0 +1,14 @@ +data class SomeObject(val n: SomeObject?) { + fun doSomething() {} + fun next(): SomeObject? = n +} + + +fun list(start: SomeObject) { + var e: SomeObject? = start + while (e != null) { + // While condition makes both smart casts possible + e.doSomething() + e = e.next() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/iterations.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/iterations.txt new file mode 100644 index 00000000000..929bc957451 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/iterations.txt @@ -0,0 +1,15 @@ +package + +internal fun list(/*0*/ start: SomeObject): kotlin.Unit + +kotlin.data() internal final class SomeObject { + public constructor SomeObject(/*0*/ n: SomeObject?) + internal final val n: SomeObject? + internal final /*synthesized*/ fun component1(): SomeObject? + public final /*synthesized*/ fun copy(/*0*/ n: SomeObject? = ...): SomeObject + internal final fun doSomething(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final fun next(): SomeObject? + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedDoWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedDoWhile.kt new file mode 100644 index 00000000000..b89489ce0fb --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedDoWhile.kt @@ -0,0 +1,15 @@ +fun x(): Boolean { return true } + +public fun foo(pp: String?, rr: String?): Int { + var p = pp + var r = rr + do { + do { + p!!.length() + } while (r == null) + } while (!x()) + // Auto cast possible + r.length() + // Auto cast possible + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedDoWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedDoWhile.txt new file mode 100644 index 00000000000..df2549b974e --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedDoWhile.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ pp: kotlin.String?, /*1*/ rr: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedLoops.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedLoops.kt new file mode 100644 index 00000000000..5a33c314e6a --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedLoops.kt @@ -0,0 +1,19 @@ +fun x(): Boolean { return true } + +public fun foo(qq: String?): Int { + var q = qq + while(true) { + q!!.length() + var r = q + do { + var p = r + do { + // p = r, r = q and q is not null + p.length() + } while (!x()) + } while (r == null) // r = q and q is not null + if (!x()) break + } + // Smart cast is possible + return q.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedLoops.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedLoops.txt new file mode 100644 index 00000000000..d5645c4cba7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedLoops.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ qq: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedWhile.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedWhile.kt new file mode 100644 index 00000000000..474dd78daca --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedWhile.kt @@ -0,0 +1,27 @@ +class Bar { + fun next(): Bar? { + if (2 == 4) + return this + else + return null + } +} + +fun foo(): Bar { + var x: Bar? = Bar() + var y: Bar? = Bar() + while (x != null) { + // Here call is unsafe because of initialization and also inner loop + y.next() + while (y != null) { + if (x == y) + // x is not null because of outer while + return x + // y is not null because of inner while + y = y.next() + } + // x is not null because of outer while + x = x.next() + } + return Bar() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedWhile.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedWhile.txt new file mode 100644 index 00000000000..765e649a5f1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedWhile.txt @@ -0,0 +1,11 @@ +package + +internal fun foo(): Bar + +internal final class Bar { + public constructor Bar() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final fun next(): Bar? + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessary.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessary.kt new file mode 100644 index 00000000000..d996a398daa --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessary.kt @@ -0,0 +1,12 @@ +fun String?.foo(): String { + return this ?: "" +} + +class MyClass { + private var s: String? = null + + fun bar(): String { + s = "42" + return s.foo() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessary.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessary.txt new file mode 100644 index 00000000000..d7b69f54d74 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessary.txt @@ -0,0 +1,12 @@ +package + +internal fun kotlin.String?.foo(): kotlin.String + +internal final class MyClass { + public constructor MyClass() + private final var s: kotlin.String? + internal final fun bar(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithBranch.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithBranch.kt new file mode 100644 index 00000000000..5827bba34e2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithBranch.kt @@ -0,0 +1,12 @@ +fun String?.foo(): String { + return this ?: "" +} + +class MyClass { + fun bar(): String { + var s: String? = null + if (4 < 2) + s = "42" + return s.foo() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithBranch.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithBranch.txt new file mode 100644 index 00000000000..41108509ec0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithBranch.txt @@ -0,0 +1,11 @@ +package + +internal fun kotlin.String?.foo(): kotlin.String + +internal final class MyClass { + public constructor MyClass() + internal final fun bar(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithMap.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithMap.kt new file mode 100644 index 00000000000..e876420dc28 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithMap.kt @@ -0,0 +1,19 @@ +fun create(): Map = null!! + +fun Map.iterator(): Iterator> = null!! + +fun Map.Entry.component1() = getKey() + +fun Map.Entry.component2() = getValue() + +class MyClass { + private var m: Map? = null + fun foo(): Int { + var res = 0 + m = create() + // See KT-7428 + for ((k, v) in m) + res += (k.length() + v.length()) + return res + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithMap.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithMap.txt new file mode 100644 index 00000000000..c543c07871d --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithMap.txt @@ -0,0 +1,15 @@ +package + +internal fun create(): kotlin.Map +internal fun kotlin.Map.Entry.component1(): K +internal fun kotlin.Map.Entry.component2(): V +internal fun kotlin.Map.iterator(): kotlin.Iterator> + +internal final class MyClass { + public constructor MyClass() + private final var m: kotlin.Map? + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final fun foo(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInClosure.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInClosure.kt new file mode 100644 index 00000000000..7ca8c6d0e19 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInClosure.kt @@ -0,0 +1,17 @@ +public fun foo() { + var s: String? = "" + fun closure(): Int { + if (s == "") { + s = null + return -1 + } else if (s == null) { + return -2 + } else { + return s.length() // Here smartcast is possible, at least in principle + } + } + if (s != null) { + System.out.println(closure()) + System.out.println(s.length()) // Here smartcast is not possible due to a closure predecessor + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInClosure.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInClosure.txt new file mode 100644 index 00000000000..65a6ac47e1f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInClosure.txt @@ -0,0 +1,3 @@ +package + +public fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInInlineClosure.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInInlineClosure.kt new file mode 100644 index 00000000000..5f992909d43 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInInlineClosure.kt @@ -0,0 +1,15 @@ +// See also KT-7186 + +fun IntArray.forEachIndexed( op: (i: Int, value: Int) -> Unit) { + for (i in 0..this.size()-1) + op(i, this[i]) +} + +fun max(a: IntArray): Int? { + var maxI: Int? = null + a.forEachIndexed { i, value -> + if (maxI == null || value >= a[maxI]) + maxI = i + } + return maxI +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInInlineClosure.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInInlineClosure.txt new file mode 100644 index 00000000000..74a7b372abf --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInInlineClosure.txt @@ -0,0 +1,4 @@ +package + +internal fun max(/*0*/ a: kotlin.IntArray): kotlin.Int? +internal fun kotlin.IntArray.forEachIndexed(/*0*/ op: (kotlin.Int, kotlin.Int) -> kotlin.Unit): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInSafeClosure.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInSafeClosure.kt new file mode 100644 index 00000000000..9f1c937904b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInSafeClosure.kt @@ -0,0 +1,15 @@ +public fun foo() { + var s: String? = "" + fun closure(): Int { + if (s == null) { + return -1 + } else { + return 0 + } + } + if (s != null) { + System.out.println(closure()) + // Smart cast is possible but closure makes it harder to understand + System.out.println(s.length()) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInSafeClosure.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInSafeClosure.txt new file mode 100644 index 00000000000..65a6ac47e1f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInSafeClosure.txt @@ -0,0 +1,3 @@ +package + +public fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varChangedInLoop.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varChangedInLoop.kt new file mode 100644 index 00000000000..93b716800fd --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varChangedInLoop.kt @@ -0,0 +1,9 @@ +public fun foo() { + var i: Int? = 1 + if (i != null) { + while (i != 10) { + i++ // Here smart cast should not be performed due to a successor + i = null + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varChangedInLoop.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varChangedInLoop.txt new file mode 100644 index 00000000000..65a6ac47e1f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varChangedInLoop.txt @@ -0,0 +1,3 @@ +package + +public fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCheck.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCheck.kt new file mode 100644 index 00000000000..4dd10de19e1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCheck.kt @@ -0,0 +1,9 @@ +fun get(): String? { + return "" +} + +fun foo(): Int { + var c: String? = get() + c!!.length() + return c.length() // Previous line should make !! unnecessary here. +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCheck.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCheck.txt new file mode 100644 index 00000000000..3c9a108493d --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCheck.txt @@ -0,0 +1,4 @@ +package + +internal fun foo(): kotlin.Int +internal fun get(): kotlin.String? diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.kt new file mode 100644 index 00000000000..adccbb76fc1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.kt @@ -0,0 +1,5 @@ +fun foo(): Int { + var i: Int? = 42 + i = null + return i + 1 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.txt new file mode 100644 index 00000000000..de1d6f17577 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.txt @@ -0,0 +1,3 @@ +package + +internal fun foo(): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNotChangedInLoop.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNotChangedInLoop.kt new file mode 100644 index 00000000000..8b760fa99be --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNotChangedInLoop.kt @@ -0,0 +1,8 @@ +public fun foo() { + var i: Int? = 1 + if (i != null) { + while (i != 10) { + i++ + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNotChangedInLoop.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNotChangedInLoop.txt new file mode 100644 index 00000000000..65a6ac47e1f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNotChangedInLoop.txt @@ -0,0 +1,3 @@ +package + +public fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNull.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNull.kt new file mode 100644 index 00000000000..6485a49b3c0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNull.kt @@ -0,0 +1,5 @@ +fun foo(): Int { + var s: String? = "abc" + s = null + return s.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNull.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNull.txt new file mode 100644 index 00000000000..de1d6f17577 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNull.txt @@ -0,0 +1,3 @@ +package + +internal fun foo(): kotlin.Int diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrue.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrue.kt new file mode 100644 index 00000000000..c1badf92700 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrue.kt @@ -0,0 +1,13 @@ +fun x(): Boolean { return true } + +public fun foo(pp: String?): Int { + var p = pp + while(true) { + p!!.length() + if (x()) break + p = null + } + // Smart cast is NOT possible here + // (we could provide it but p = null makes it much harder) + return p.length() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrue.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrue.txt new file mode 100644 index 00000000000..23bf0a2ca9a --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrue.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ pp: kotlin.String?): kotlin.Int +internal fun x(): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.kt new file mode 100644 index 00000000000..87119c161be --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.kt @@ -0,0 +1,17 @@ +data class SomeObject(val n: SomeObject?) { + fun doSomething(): Boolean = true + fun next(): SomeObject? = n +} + +fun list(start: SomeObject) { + var e: SomeObject? = start + while (e != null) { + // Smart cast due to the loop condition + if (!e.doSomething()) + break + // We still have smart cast here despite of a break + e = e.next() + } + // e can be null because of next() + e.doSomething() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.txt new file mode 100644 index 00000000000..2472f883754 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.txt @@ -0,0 +1,15 @@ +package + +internal fun list(/*0*/ start: SomeObject): kotlin.Unit + +kotlin.data() internal final class SomeObject { + public constructor SomeObject(/*0*/ n: SomeObject?) + internal final val n: SomeObject? + internal final /*synthesized*/ fun component1(): SomeObject? + public final /*synthesized*/ fun copy(/*0*/ n: SomeObject? = ...): SomeObject + internal final fun doSomething(): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final fun next(): SomeObject? + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 6d6a3f95f09..12b9431f402 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -11450,6 +11450,324 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } } + + @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/variables") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Variables extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInVariables() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/variables"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("assignment.kt") + public void testAssignment() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/assignment.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileWithMiddleBreak.kt") + public void testDoWhileWithMiddleBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/doWhileWithMiddleBreak.kt"); + doTest(fileName); + } + + @TestMetadata("ifElseBlockInsideDoWhile.kt") + public void testIfElseBlockInsideDoWhile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhile.kt"); + doTest(fileName); + } + + @TestMetadata("ifElseBlockInsideDoWhileWithBreak.kt") + public void testIfElseBlockInsideDoWhileWithBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/ifElseBlockInsideDoWhileWithBreak.kt"); + doTest(fileName); + } + + @TestMetadata("ifVarIs.kt") + public void testIfVarIs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIs.kt"); + doTest(fileName); + } + + @TestMetadata("ifVarIsAnd.kt") + public void testIfVarIsAnd() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsAnd.kt"); + doTest(fileName); + } + + @TestMetadata("ifVarIsChanged.kt") + public void testIfVarIsChanged() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/ifVarIsChanged.kt"); + doTest(fileName); + } + + @TestMetadata("infix.kt") + public void testInfix() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/infix.kt"); + doTest(fileName); + } + + @TestMetadata("initialization.kt") + public void testInitialization() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/initialization.kt"); + doTest(fileName); + } + + @TestMetadata("property.kt") + public void testProperty() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/property.kt"); + doTest(fileName); + } + + @TestMetadata("propertyNotNeeded.kt") + public void testPropertyNotNeeded() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/propertyNotNeeded.kt"); + doTest(fileName); + } + + @TestMetadata("propertySubtype.kt") + public void testPropertySubtype() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtype.kt"); + doTest(fileName); + } + + @TestMetadata("propertySubtypeInMember.kt") + public void testPropertySubtypeInMember() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMember.kt"); + doTest(fileName); + } + + @TestMetadata("propertySubtypeInMemberCheck.kt") + public void testPropertySubtypeInMemberCheck() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/propertySubtypeInMemberCheck.kt"); + doTest(fileName); + } + + @TestMetadata("varAsUse.kt") + public void testVarAsUse() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/varAsUse.kt"); + doTest(fileName); + } + + @TestMetadata("varChangedInLoop.kt") + public void testVarChangedInLoop() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/varChangedInLoop.kt"); + doTest(fileName); + } + + @TestMetadata("varNotChangedInLoop.kt") + public void testVarNotChangedInLoop() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/varNotChangedInLoop.kt"); + doTest(fileName); + } + + @TestMetadata("whileTrue.kt") + public void testWhileTrue() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/whileTrue.kt"); + doTest(fileName); + } + + @TestMetadata("whileWithBreak.kt") + public void testWhileWithBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/variables/whileWithBreak.kt"); + doTest(fileName); + } + } + + @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Varnotnull extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInVarnotnull() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/varnotnull"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("assignment.kt") + public void testAssignment() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/assignment.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileWithBreak.kt") + public void testDoWhileWithBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithBreak.kt"); + doTest(fileName); + } + + @TestMetadata("doWhileWithMiddleBreak.kt") + public void testDoWhileWithMiddleBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/doWhileWithMiddleBreak.kt"); + doTest(fileName); + } + + @TestMetadata("forEach.kt") + public void testForEach() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEach.kt"); + doTest(fileName); + } + + @TestMetadata("forEachWithBreak.kt") + public void testForEachWithBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithBreak.kt"); + doTest(fileName); + } + + @TestMetadata("forEachWithContinue.kt") + public void testForEachWithContinue() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/forEachWithContinue.kt"); + doTest(fileName); + } + + @TestMetadata("ifVarNotNull.kt") + public void testIfVarNotNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNull.kt"); + doTest(fileName); + } + + @TestMetadata("ifVarNotNullAnd.kt") + public void testIfVarNotNullAnd() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNotNullAnd.kt"); + doTest(fileName); + } + + @TestMetadata("ifVarNullElse.kt") + public void testIfVarNullElse() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullElse.kt"); + doTest(fileName); + } + + @TestMetadata("ifVarNullReturn.kt") + public void testIfVarNullReturn() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/ifVarNullReturn.kt"); + doTest(fileName); + } + + @TestMetadata("inference.kt") + public void testInference() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/inference.kt"); + doTest(fileName); + } + + @TestMetadata("infiniteWhileWithBreak.kt") + public void testInfiniteWhileWithBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/infiniteWhileWithBreak.kt"); + doTest(fileName); + } + + @TestMetadata("infix.kt") + public void testInfix() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/infix.kt"); + doTest(fileName); + } + + @TestMetadata("initialization.kt") + public void testInitialization() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/initialization.kt"); + doTest(fileName); + } + + @TestMetadata("iterations.kt") + public void testIterations() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/iterations.kt"); + doTest(fileName); + } + + @TestMetadata("nestedDoWhile.kt") + public void testNestedDoWhile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedDoWhile.kt"); + doTest(fileName); + } + + @TestMetadata("nestedLoops.kt") + public void testNestedLoops() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedLoops.kt"); + doTest(fileName); + } + + @TestMetadata("nestedWhile.kt") + public void testNestedWhile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/nestedWhile.kt"); + doTest(fileName); + } + + @TestMetadata("unnecessary.kt") + public void testUnnecessary() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessary.kt"); + doTest(fileName); + } + + @TestMetadata("unnecessaryWithBranch.kt") + public void testUnnecessaryWithBranch() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithBranch.kt"); + doTest(fileName); + } + + @TestMetadata("unnecessaryWithMap.kt") + public void testUnnecessaryWithMap() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithMap.kt"); + doTest(fileName); + } + + @TestMetadata("varCapturedInClosure.kt") + public void testVarCapturedInClosure() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInClosure.kt"); + doTest(fileName); + } + + @TestMetadata("varCapturedInInlineClosure.kt") + public void testVarCapturedInInlineClosure() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInInlineClosure.kt"); + doTest(fileName); + } + + @TestMetadata("varCapturedInSafeClosure.kt") + public void testVarCapturedInSafeClosure() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCapturedInSafeClosure.kt"); + doTest(fileName); + } + + @TestMetadata("varChangedInLoop.kt") + public void testVarChangedInLoop() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/varChangedInLoop.kt"); + doTest(fileName); + } + + @TestMetadata("varCheck.kt") + public void testVarCheck() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/varCheck.kt"); + doTest(fileName); + } + + @TestMetadata("varIntNull.kt") + public void testVarIntNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/varIntNull.kt"); + doTest(fileName); + } + + @TestMetadata("varNotChangedInLoop.kt") + public void testVarNotChangedInLoop() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNotChangedInLoop.kt"); + doTest(fileName); + } + + @TestMetadata("varNull.kt") + public void testVarNull() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/varNull.kt"); + doTest(fileName); + } + + @TestMetadata("whileTrue.kt") + public void testWhileTrue() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileTrue.kt"); + doTest(fileName); + } + + @TestMetadata("whileWithBreak.kt") + public void testWhileWithBreak() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/whileWithBreak.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/substitutions") diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/DataFlowInfoUtilForCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/DataFlowInfoUtilForCompletion.kt index 6f989e65ad0..5d8943c50a4 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/DataFlowInfoUtilForCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/DataFlowInfoUtilForCompletion.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.descriptors.PackageViewDescriptor fun renderDataFlowValue(value: DataFlowValue): String? { // If it is not a stable identifier, there's no point in rendering it - if (!value.isStableIdentifier()) return null + if (!value.isStableIdentifier() && !value.isLocalVariable()) return null fun renderId(id: Any?): String? { return when (id) { diff --git a/idea/testData/checker/BinaryCallsOnNullableValues.kt b/idea/testData/checker/BinaryCallsOnNullableValues.kt index 6b1cd9b8c20..4342a082341 100644 --- a/idea/testData/checker/BinaryCallsOnNullableValues.kt +++ b/idea/testData/checker/BinaryCallsOnNullableValues.kt @@ -5,10 +5,10 @@ class A() { fun f(): Unit { var x: Int? = 1 x = 1 - x + 1 - x plus 1 - x < 1 - x += 1 + x + 1 + x plus 1 + x < 1 + x += 1 x == 1 x != 1 @@ -21,8 +21,8 @@ fun f(): Unit { x === 1 x !== 1 - x..2 - x in 1..2 + x..2 + x in 1..2 val y : Boolean? = true false || y diff --git a/idea/testData/checker/infos/SmartCasts.kt b/idea/testData/checker/infos/SmartCasts.kt index d220bf47019..a0945641470 100644 --- a/idea/testData/checker/infos/SmartCasts.kt +++ b/idea/testData/checker/infos/SmartCasts.kt @@ -198,8 +198,8 @@ fun mergeSmartCasts(a: Any?) { fun f(): String { var a: Any = 11 if (a is String) { - val i: String = a - a.compareTo("f") + val i: String = a + a.compareTo("f") val f: Function0 = { a } return a } @@ -209,7 +209,7 @@ fun f(): String { fun foo(aa: Any): Int { var a = aa if (a is Int) { - return a + return a } return 1 } diff --git a/idea/testData/quickfix/typeMismatch/casts/afterSmartcastImpossible1.kt b/idea/testData/quickfix/typeMismatch/casts/afterSmartcastImpossible1.kt index 2b25f84c063..9dba0503b32 100644 --- a/idea/testData/quickfix/typeMismatch/casts/afterSmartcastImpossible1.kt +++ b/idea/testData/quickfix/typeMismatch/casts/afterSmartcastImpossible1.kt @@ -1,10 +1,14 @@ // "Cast expression 'a' to 'Foo'" "true" + trait Foo { fun plus(x: Any) : Foo } -fun foo(_a: Any): Any { - var a = _a +open class MyClass { + public open val a: Any = "42" +} + +fun MyClass.foo(): Any { if (a is Foo) { return a as Foo + a } diff --git a/idea/testData/quickfix/typeMismatch/casts/afterSmartcastImpossible2.kt b/idea/testData/quickfix/typeMismatch/casts/afterSmartcastImpossible2.kt index 07c07301d94..bb09f214a35 100644 --- a/idea/testData/quickfix/typeMismatch/casts/afterSmartcastImpossible2.kt +++ b/idea/testData/quickfix/typeMismatch/casts/afterSmartcastImpossible2.kt @@ -1,10 +1,14 @@ // "Cast expression 'a' to 'Foo'" "true" + trait Foo { fun not() : Foo } -fun foo(_a: Any): Any { - var a = _a +open class MyClass { + public open val a: Any = "42" +} + +fun MyClass.foo(): Any { if (a is Foo) { return !(a as Foo) } diff --git a/idea/testData/quickfix/typeMismatch/casts/afterSmartcastImpossible3.kt b/idea/testData/quickfix/typeMismatch/casts/afterSmartcastImpossible3.kt index 531bfb4eb5b..3b08a6b2149 100644 --- a/idea/testData/quickfix/typeMismatch/casts/afterSmartcastImpossible3.kt +++ b/idea/testData/quickfix/typeMismatch/casts/afterSmartcastImpossible3.kt @@ -1,11 +1,15 @@ // "Cast expression 'x' to 'Foo<*>'" "true" + trait Foo { - fun foo() + fun bar() } -fun bar(_x: Any) { - var x = _x +open class MyClass { + public open val x: Any = "42" +} + +fun MyClass.bar() { if (x is Foo<*>) { - (x as Foo<*>).foo() + (x as Foo<*>).bar() } } \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/casts/beforeSmartcastImpossible1.kt b/idea/testData/quickfix/typeMismatch/casts/beforeSmartcastImpossible1.kt index cb232a56d17..851403a695d 100644 --- a/idea/testData/quickfix/typeMismatch/casts/beforeSmartcastImpossible1.kt +++ b/idea/testData/quickfix/typeMismatch/casts/beforeSmartcastImpossible1.kt @@ -1,10 +1,14 @@ // "Cast expression 'a' to 'Foo'" "true" + trait Foo { fun plus(x: Any) : Foo } -fun foo(_a: Any): Any { - var a = _a +open class MyClass { + public open val a: Any = "42" +} + +fun MyClass.foo(): Any { if (a is Foo) { return a + a } diff --git a/idea/testData/quickfix/typeMismatch/casts/beforeSmartcastImpossible2.kt b/idea/testData/quickfix/typeMismatch/casts/beforeSmartcastImpossible2.kt index 55cec269a71..3ac120e2310 100644 --- a/idea/testData/quickfix/typeMismatch/casts/beforeSmartcastImpossible2.kt +++ b/idea/testData/quickfix/typeMismatch/casts/beforeSmartcastImpossible2.kt @@ -1,10 +1,14 @@ // "Cast expression 'a' to 'Foo'" "true" + trait Foo { fun not() : Foo } -fun foo(_a: Any): Any { - var a = _a +open class MyClass { + public open val a: Any = "42" +} + +fun MyClass.foo(): Any { if (a is Foo) { return !a } diff --git a/idea/testData/quickfix/typeMismatch/casts/beforeSmartcastImpossible3.kt b/idea/testData/quickfix/typeMismatch/casts/beforeSmartcastImpossible3.kt index a3f1be7039e..6052755b029 100644 --- a/idea/testData/quickfix/typeMismatch/casts/beforeSmartcastImpossible3.kt +++ b/idea/testData/quickfix/typeMismatch/casts/beforeSmartcastImpossible3.kt @@ -1,11 +1,15 @@ // "Cast expression 'x' to 'Foo<*>'" "true" + trait Foo { - fun foo() + fun bar() } -fun bar(_x: Any) { - var x = _x +open class MyClass { + public open val x: Any = "42" +} + +fun MyClass.bar() { if (x is Foo<*>) { - x.foo() + x.bar() } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/boxedType/Boxing.kt b/j2k/testData/fileOrElement/boxedType/Boxing.kt index b2d12d0e35d..eac6c023ad9 100644 --- a/j2k/testData/fileOrElement/boxedType/Boxing.kt +++ b/j2k/testData/fileOrElement/boxedType/Boxing.kt @@ -5,8 +5,8 @@ class Boxing { var i: Int? = 0 val n = 0.0f i = 1 - var j = i!! - val k = i!! + 2 + var j = i + val k = i + 2 i = null j = i!! }