Smart casts for local variables not captured in a closure and not changed in a loop, see #KT-3175.

isLocalVariable added. Assignment / initialization analysis.
Control whether a variable is changed in a loop at the beginning and at the end of the loop.
Control whether a variable is captured in a closure. #KT-3175  Fixed. #KT-2266  Fixed.
Tests for variable null safety and for variables is/as operations.
Loop / closure / variable property tests are included.
Old tests changed in accordance with KT-3175. In particular, all three of testSmartcastImpossible were fixed.
This commit is contained in:
Mikhail Glukhikh
2015-03-24 17:03:01 +03:00
parent e4b1046a54
commit d5aed62410
126 changed files with 1655 additions and 71 deletions
@@ -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<JetType> possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue);
if (possibleTypes.isEmpty()) return type;
@@ -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.<DataFlowValue, Nullability>of(), DelegatingDataFlowInfo.newTypeInfo());
@@ -39,18 +43,43 @@ public interface DataFlowInfo {
@NotNull
Set<JetType> 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);
}
@@ -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;
@@ -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.
* <p/>
@@ -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<DataFlowValue,Nullability> EMPTY_NULLABILITY_INFO = ImmutableMap.of();
private static final ImmutableMap<DataFlowValue, Nullability> EMPTY_NULLABILITY_INFO = ImmutableMap.of();
private static final SetMultimap<DataFlowValue, JetType> EMPTY_TYPE_INFO = newTypeInfo();
@Nullable
@@ -38,18 +39,37 @@ import static org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability.NOT_NULL
@NotNull
private final ImmutableMap<DataFlowValue, Nullability> nullabilityInfo;
/** Also immutable */
/**
* Also immutable
*/
@NotNull
private final SetMultimap<DataFlowValue, JetType> 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<DataFlowValue, Nullability> nullabilityInfo,
@NotNull SetMultimap<DataFlowValue, JetType> typeInfo
) {
this(parent, nullabilityInfo, typeInfo, null);
}
/* package */ DelegatingDataFlowInfo(
@Nullable DataFlowInfo parent,
@NotNull ImmutableMap<DataFlowValue, Nullability> nullabilityInfo,
@NotNull SetMultimap<DataFlowValue, JetType> 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<DataFlowValue, JetType> getCompleteTypeInfo() {
SetMultimap<DataFlowValue, JetType> result = newTypeInfo();
Set<DataFlowValue> resultCompleted = new HashSet<DataFlowValue>();
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<DataFlowValue, Nullability> map, @NotNull DataFlowValue value, @NotNull Nullability nullability) {
if (!value.isStableIdentifier()) return false;
private boolean putNullability(
@NotNull Map<DataFlowValue, Nullability> 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<JetType> getPossibleTypes(@NotNull DataFlowValue key) {
Set<JetType> theseTypes = typeInfo.get(key);
Set<JetType> types = parent == null ? theseTypes : Sets.union(theseTypes, parent.getPossibleTypes(key));
Set<JetType> 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<DataFlowValue, Nullability> 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<DataFlowValue, Nullability> builder = Maps.newHashMap();
Nullability nullabilityOfB = getNullability(b);
boolean changed = putNullability(builder, a, nullabilityOfB);
SetMultimap<DataFlowValue, JetType> newTypeInfo = newTypeInfo();
Set<JetType> typesForA = collectTypesFromMeAndParents(a);
Set<JetType> 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));
@@ -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;
}
@@ -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;
@@ -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;
}
}
@@ -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
@@ -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
* <b>before</b> loop analysis
*/
class PreliminaryLoopVisitor extends JetTreeVisitor<Void> {
// loop under analysis
private final JetLoopExpression loopExpression;
private Set<Name> assignedNames = new LinkedHashSet<Name>();
public PreliminaryLoopVisitor(JetLoopExpression loopExpression) {
this.loopExpression = loopExpression;
}
public void launch() {
loopExpression.accept(this, null);
}
public DataFlowInfo clearDataFlowInfoForAssignedLocalVariables(DataFlowInfo dataFlowInfo) {
Map<DataFlowValue, Nullability> nullabilityMap = dataFlowInfo.getCompleteNullabilityInfo();
Set<DataFlowValue> valueSetToClear = new LinkedHashSet<DataFlowValue>();
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;
}
}
@@ -4,7 +4,7 @@ class A() {
fun f(): Unit {
var x: Int? = <!VARIABLE_WITH_REDUNDANT_INITIALIZER!>1<!>
x = 1
x = null
x <!UNSAFE_INFIX_CALL!>+<!> 1
x <!UNSAFE_INFIX_CALL!>plus<!> 1
x <!UNSAFE_INFIX_CALL!><<!> 1
@@ -7,7 +7,8 @@ private fun doTest() : Int {
var list : MutableList<Int>? ;
try {
list = ArrayList()
list?.add(3)
// Not-null was just assigned to the list
<!DEBUG_INFO_SMARTCAST!>list<!>.add(3)
return 0 ;
}
finally {
@@ -202,18 +202,20 @@ fun mergeSmartCasts(a: Any?) {
fun f(): String {
var a: Any = 11
if (a is String) {
val <!UNUSED_VARIABLE!>i<!>: String = <!SMARTCAST_IMPOSSIBLE!>a<!>
<!SMARTCAST_IMPOSSIBLE!>a<!>.compareTo("f")
// a is a string, despite of being a variable
val <!UNUSED_VARIABLE!>i<!>: String = <!DEBUG_INFO_SMARTCAST!>a<!>
<!DEBUG_INFO_SMARTCAST!>a<!>.compareTo("f")
// Beginning from here a is captured in a closure so we have to be cautious
val <!UNUSED_VARIABLE!>f<!>: Function0<String> = { <!SMARTCAST_IMPOSSIBLE!>a<!> }
return <!SMARTCAST_IMPOSSIBLE!>a<!>
}
return ""
}
fun foo(aa: Any): Int {
fun foo(aa: Any?): Int {
var a = aa
if (a is Int) {
return <!SMARTCAST_IMPOSSIBLE!>a<!>
if (a is Int?) {
return <!TYPE_MISMATCH!>a<!>
}
return 1
}
@@ -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
@@ -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(<!DEBUG_INFO_SMARTCAST!>current<!>.getText())
}
current = current?.getParent()
current = <!DEBUG_INFO_SMARTCAST!>current<!>.getParent()
}
}
@@ -0,0 +1,10 @@
fun foo() {
var v: Any = 42
v.<!UNRESOLVED_REFERENCE!>length<!>()
v = "abc"
<!DEBUG_INFO_SMARTCAST!>v<!>.length()
v = 42
v.<!UNRESOLVED_REFERENCE!>length<!>()
v = "abc"
<!DEBUG_INFO_SMARTCAST!>v<!>.length()
}
@@ -0,0 +1,3 @@
package
internal fun foo(): kotlin.Unit
@@ -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.<!UNRESOLVED_REFERENCE!>length<!>()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ pp: kotlin.Any): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -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
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
} while (!(x is String))
return <!DEBUG_INFO_SMARTCAST!>x<!>.length()
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ xx: kotlin.Any): kotlin.Int
@@ -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
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
} while (true)
// We could have smart cast here but with break it's hard to detect
return x.<!UNRESOLVED_REFERENCE!>length<!>()
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ xx: kotlin.Any): kotlin.Int
@@ -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(<!DEBUG_INFO_SMARTCAST!>s<!>)
}
@@ -0,0 +1,4 @@
package
public fun bar(/*0*/ s: kotlin.String): kotlin.Unit
public fun foo(): kotlin.Unit
@@ -0,0 +1,13 @@
// See KT-5737
fun get(): Any {
return "abc"
}
fun foo(): Int {
var ss: Any = get()
return if (ss is String && <!DEBUG_INFO_SMARTCAST!>ss<!>.length() > 0)
1
else
0
}
@@ -0,0 +1,4 @@
package
internal fun foo(): kotlin.Int
internal fun get(): kotlin.Any
@@ -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(<!TYPE_MISMATCH!>s<!>)
}
}
@@ -0,0 +1,4 @@
package
public fun bar(/*0*/ s: kotlin.String): kotlin.Unit
public fun foo(): kotlin.Unit
@@ -0,0 +1,11 @@
// See KT-774
fun box() : Int {
var a : Any = 1
var d = 1
if (a is Int) {
return <!DEBUG_INFO_SMARTCAST!>a<!> + d
} else {
return 2
}
}
@@ -0,0 +1,3 @@
package
internal fun box(): kotlin.Int
@@ -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.<!UNRESOLVED_REFERENCE!>length<!>()
v = 42
v.<!UNRESOLVED_REFERENCE!>length<!>()
}
@@ -0,0 +1,3 @@
package
internal fun foo(): kotlin.Unit
@@ -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(<!SMARTCAST_IMPOSSIBLE!>m.p<!>)
}
@@ -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
}
@@ -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)
}
@@ -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
}
@@ -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)
}
@@ -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
}
@@ -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)
}
}
@@ -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
}
@@ -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
}
}
@@ -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
}
@@ -0,0 +1,9 @@
fun get(): Any {
return ""
}
fun foo(): Int {
var c: Any = get()
(c as String).length()
return <!DEBUG_INFO_SMARTCAST!>c<!>.length() // Previous line should make as unnecessary here.
}
@@ -0,0 +1,4 @@
package
internal fun foo(): kotlin.Int
internal fun get(): kotlin.Any
@@ -0,0 +1,9 @@
public fun foo() {
var i: Any = 1
if (i is Int) {
while (i != 10) {
<!UNUSED_CHANGED_VALUE!>i<!UNRESOLVED_REFERENCE!>++<!><!> // Here smart cast should not be performed due to a successor
i = ""
}
}
}
@@ -0,0 +1,3 @@
package
public fun foo(): kotlin.Unit
@@ -0,0 +1,8 @@
public fun foo() {
var i: Any = 1
if (i is Int) {
while (i != 10) {
<!DEBUG_INFO_SMARTCAST!>i<!>++
}
}
}
@@ -0,0 +1,3 @@
package
public fun foo(): kotlin.Unit
@@ -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.<!UNRESOLVED_REFERENCE!>length<!>()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ pp: kotlin.Any): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -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 (<!DEBUG_INFO_SMARTCAST!>e<!>.length() == 0)
break
// We still have smart cast here despite of a break
e = <!DEBUG_INFO_SMARTCAST!>e<!>.next()
}
// e can never be null but we do not know it
e<!UNSAFE_CALL!>.<!>hashCode()
}
@@ -0,0 +1,4 @@
package
internal fun list(/*0*/ start: kotlin.String): kotlin.Unit
internal fun kotlin.String.next(): kotlin.String
@@ -0,0 +1,10 @@
fun foo() {
var v: String? = null
v<!UNSAFE_CALL!>.<!>length()
v = "abc"
<!DEBUG_INFO_SMARTCAST!>v<!>.length()
v = null
v<!UNSAFE_CALL!>.<!>length()
v = "abc"
<!DEBUG_INFO_SMARTCAST!>v<!>.length()
}
@@ -0,0 +1,3 @@
package
internal fun foo(): kotlin.Unit
@@ -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<!UNSAFE_CALL!>.<!>doSomething())
break
// Smart cast here is still not possible
e = e<!UNSAFE_CALL!>.<!>next()
} while (e != null)
// e can be null because of next()
e<!UNSAFE_CALL!>.<!>doSomething()
}
@@ -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
}
@@ -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<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ pp: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -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<!UNSAFE_CALL!>.<!>doSomething()
e = e<!UNSAFE_CALL!>.<!>next()
}
// Smart cast is not possible here due to next()
return <!TYPE_MISMATCH!>e<!>
}
@@ -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
}
@@ -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
<!DEBUG_INFO_SMARTCAST!>e<!>.doSomething()
e = <!DEBUG_INFO_SMARTCAST!>e<!>.next()
}
return <!TYPE_MISMATCH!>e<!>
}
@@ -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
}
@@ -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
<!DEBUG_INFO_SMARTCAST!>e<!>.doSomething()
e = <!DEBUG_INFO_SMARTCAST!>e<!>.next()
}
return <!TYPE_MISMATCH!>e<!>
}
@@ -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
}
@@ -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(<!DEBUG_INFO_SMARTCAST!>s<!>)
}
@@ -0,0 +1,4 @@
package
public fun foo(): kotlin.Unit
public fun fooNotNull(/*0*/ s: kotlin.String): kotlin.Unit
@@ -0,0 +1,13 @@
// See KT-5737
fun get(): String? {
return "abc"
}
fun foo(): Int {
var ss:String? = get()
return if (ss != null && <!DEBUG_INFO_SMARTCAST!>ss<!>.length() > 0)
1
else
0
}
@@ -0,0 +1,4 @@
package
internal fun foo(): kotlin.Int
internal fun get(): kotlin.String?
@@ -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(<!DEBUG_INFO_SMARTCAST!>s<!>)
}
}
@@ -0,0 +1,4 @@
package
public fun foo(): kotlin.Unit
public fun fooNotNull(/*0*/ s: kotlin.String): kotlin.Unit
@@ -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(<!DEBUG_INFO_SMARTCAST!>s<!>)
}
@@ -0,0 +1,4 @@
package
public fun foo(): kotlin.Unit
public fun fooNotNull(/*0*/ s: kotlin.String): kotlin.Unit
@@ -0,0 +1,21 @@
// See KT-969
fun f() {
var s: String?
s = "a"
var s1 = "" // String ?
if (<!SENSELESS_COMPARISON!>s != null<!>) { // Redundant
s1.length()
// We can do smartcast here and below
s1 = <!DEBUG_INFO_SMARTCAST!>s<!>.toString() // return String?
s1.length()
s1 = <!DEBUG_INFO_SMARTCAST!>s<!>
s1.length()
// It's just an assignment without smartcast
val s2 = s
// But smartcast can be done here
<!DEBUG_INFO_SMARTCAST!>s2<!>.length()
// And also here
val s3 = <!DEBUG_INFO_SMARTCAST!>s<!>.toString()
s3.length()
}
}
@@ -0,0 +1,3 @@
package
internal fun f(): kotlin.Unit
@@ -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 (!<!DEBUG_INFO_SMARTCAST!>e<!>.doSomething())
break
// We still have smart cast here despite of a break
e = <!DEBUG_INFO_SMARTCAST!>e<!>.next()
}
// e can be null because of next()
e<!UNSAFE_CALL!>.<!>doSomething()
}
@@ -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
}
@@ -0,0 +1,11 @@
// See KT-774
fun box() : Int {
var a : Int? = 1
var d = 1
if (a == null) {
return 2
} else {
return <!DEBUG_INFO_SMARTCAST!>a<!> + d
}
}
@@ -0,0 +1,3 @@
package
internal fun box(): kotlin.Int
@@ -0,0 +1,7 @@
fun foo() {
var v: String? = "xyz"
// It is possible in principle to provide smart cast here
v<!UNSAFE_CALL!>.<!>length()
v = null
v<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,3 @@
package
internal fun foo(): kotlin.Unit
@@ -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
<!DEBUG_INFO_SMARTCAST!>e<!>.doSomething()
e = <!DEBUG_INFO_SMARTCAST!>e<!>.next()
}
}
@@ -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
}
@@ -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
<!DEBUG_INFO_SMARTCAST!>r<!>.length()
// Auto cast possible
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ pp: kotlin.String?, /*1*/ rr: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -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
<!DEBUG_INFO_SMARTCAST!>p<!>.length()
} while (!x())
} while (<!SENSELESS_COMPARISON!>r == null<!>) // r = q and q is not null
if (!x()) break
}
// Smart cast is possible
return <!DEBUG_INFO_SMARTCAST!>q<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ qq: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -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<!UNSAFE_CALL!>.<!>next()
while (y != null) {
if (x == y)
// x is not null because of outer while
return <!DEBUG_INFO_SMARTCAST!>x<!>
// y is not null because of inner while
y = <!DEBUG_INFO_SMARTCAST!>y<!>.next()
}
// x is not null because of outer while
x = <!DEBUG_INFO_SMARTCAST!>x<!>.next()
}
return Bar()
}
@@ -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
}
@@ -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()
}
}
@@ -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
}
@@ -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()
}
}
@@ -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
}
@@ -0,0 +1,19 @@
fun create(): Map<String, String> = null!!
fun <K, V> Map<K, V>.iterator(): Iterator<Map.Entry<K, V>> = null!!
fun <K, V> Map.Entry<K, V>.component1() = getKey()
fun <K, V> Map.Entry<K, V>.component2() = getValue()
class MyClass {
private var m: Map<String, String>? = null
fun foo(): Int {
var res = 0
m = create()
// See KT-7428
for ((k, v) in <!SMARTCAST_IMPOSSIBLE!>m<!>)
res += (k.length() + v.length())
return res
}
}
@@ -0,0 +1,15 @@
package
internal fun create(): kotlin.Map<kotlin.String, kotlin.String>
internal fun </*0*/ K, /*1*/ V> kotlin.Map.Entry<K, V>.component1(): K
internal fun </*0*/ K, /*1*/ V> kotlin.Map.Entry<K, V>.component2(): V
internal fun </*0*/ K, /*1*/ V> kotlin.Map<K, V>.iterator(): kotlin.Iterator<kotlin.Map.Entry<K, V>>
internal final class MyClass {
public constructor MyClass()
private final var m: kotlin.Map<kotlin.String, kotlin.String>?
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
}
@@ -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<!UNSAFE_CALL!>.<!>length() // Here smartcast is possible, at least in principle
}
}
if (s != null) {
System.out.println(closure())
System.out.println(s<!UNSAFE_CALL!>.<!>length()) // Here smartcast is not possible due to a closure predecessor
}
}
@@ -0,0 +1,3 @@
package
public fun foo(): kotlin.Unit
@@ -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[<!TYPE_MISMATCH!>maxI<!>])
maxI = i
}
return maxI
}
@@ -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
@@ -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<!UNSAFE_CALL!>.<!>length())
}
}

Some files were not shown because too many files have changed in this diff Show More