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:
@@ -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);
|
||||
}
|
||||
|
||||
+17
-4
@@ -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;
|
||||
|
||||
+34
-12
@@ -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/>
|
||||
|
||||
+93
-9
@@ -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));
|
||||
|
||||
+5
-1
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+23
-2
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -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
|
||||
|
||||
+82
@@ -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
|
||||
+16
@@ -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<!>()
|
||||
}
|
||||
+3
@@ -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
|
||||
}
|
||||
+13
@@ -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
|
||||
}
|
||||
}
|
||||
+12
@@ -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
|
||||
+15
@@ -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
|
||||
}
|
||||
+4
@@ -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
Reference in New Issue
Block a user