added expected type for assignment and elvis operator
changed return type for safe operator (now it works as a?.b is Nullable type, constructions like a?.b.c need another parsing)
This commit is contained in:
@@ -757,29 +757,6 @@ public class JetTypeInferrer {
|
||||
return expressionType;
|
||||
}
|
||||
|
||||
// @Nullable
|
||||
// private JetType enrichType(@NotNull JetExpression expression, @Nullable JetType initialType, @NotNull TypeInferenceContext context) {
|
||||
// if (initialType == null || context.expectedType == null || context.expectedType == NO_EXPECTED_TYPE) {
|
||||
// return initialType;
|
||||
// }
|
||||
// VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(expression, context);
|
||||
// if (variableDescriptor == null) return initialType;
|
||||
// JetType enrichedType = null;
|
||||
// List<JetType> possibleTypes = Lists.newArrayList(context.dataFlowInfo.getPossibleTypes(variableDescriptor));
|
||||
// Collections.reverse(possibleTypes);
|
||||
// for (JetType possibleType: possibleTypes) {
|
||||
// if (semanticServices.getTypeChecker().isSubtypeOf(possibleType, context.expectedType)) {
|
||||
// enrichedType = possibleType;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (enrichedType == null) {
|
||||
// enrichedType = context.dataFlowInfo.getOutType(variableDescriptor);
|
||||
// }
|
||||
// if (enrichedType == null) return initialType;
|
||||
// return enrichedType;
|
||||
// }
|
||||
|
||||
@Nullable
|
||||
private JetType checkEnrichedType(@Nullable JetType expressionType, @NotNull JetExpression expression, @NotNull TypeInferenceContext context) {
|
||||
if (expressionType == null || context.expectedType == null || context.expectedType == NO_EXPECTED_TYPE ||
|
||||
@@ -808,7 +785,7 @@ public class JetTypeInferrer {
|
||||
if (!semanticServices.getTypeChecker().isSubtypeOf(enrichedType, context.expectedType)) {
|
||||
context.trace.getErrorHandler().typeMismatch(expression, context.expectedType, expressionType);
|
||||
} else {
|
||||
context.trace.recordAutoCast(expression, enrichedType);
|
||||
context.trace.recordAutoCast(expression, context.expectedType);
|
||||
}
|
||||
return enrichedType;
|
||||
}
|
||||
@@ -885,12 +862,14 @@ public class JetTypeInferrer {
|
||||
return new TypeInferenceContext(trace, scope, services, preferBlock, newDataFlowInfo, expectedType, expectedReturnType);
|
||||
}
|
||||
|
||||
public TypeInferenceContext replaceExpectedType(@NotNull JetType newExpectedType) {
|
||||
public TypeInferenceContext replaceExpectedType(@Nullable JetType newExpectedType) {
|
||||
if (newExpectedType == null) return replaceExpectedType(NO_EXPECTED_TYPE);
|
||||
if (expectedType == newExpectedType) return this;
|
||||
return new TypeInferenceContext(trace, scope, services, preferBlock, dataFlowInfo, newExpectedType, expectedReturnType);
|
||||
}
|
||||
|
||||
public TypeInferenceContext replaceExpectedReturnType(@NotNull JetType newExpectedReturnType) {
|
||||
public TypeInferenceContext replaceExpectedReturnType(@Nullable JetType newExpectedReturnType) {
|
||||
if (newExpectedReturnType == null) return replaceExpectedReturnType(NO_EXPECTED_TYPE);
|
||||
if (expectedReturnType == newExpectedReturnType) return this;
|
||||
return new TypeInferenceContext(trace, scope, services, preferBlock, dataFlowInfo, expectedType, newExpectedReturnType);
|
||||
}
|
||||
@@ -2119,13 +2098,13 @@ public class JetTypeInferrer {
|
||||
ErrorHandlerWithRegions errorHandler = context.trace.getErrorHandler();
|
||||
errorHandler.openRegion();
|
||||
JetType selectorReturnType = getSelectorReturnType(receiverType, selectorExpression, context);
|
||||
|
||||
// if (expression instanceof JetSafeQualifiedExpression) {
|
||||
// if (!selectorReturnType.isNullable()) {
|
||||
// TypeUtils.makeNullable(selectorReturnType);
|
||||
// }
|
||||
// }
|
||||
|
||||
//TODO move further
|
||||
if (expression.getOperationSign() == JetTokens.SAFE_ACCESS) {
|
||||
if (selectorReturnType != null && !selectorReturnType.isNullable() && !JetStandardClasses.isUnit(selectorReturnType)) {
|
||||
selectorReturnType = TypeUtils.makeNullable(selectorReturnType);
|
||||
}
|
||||
}
|
||||
if (selectorReturnType != null) {
|
||||
errorHandler.closeAndCommitCurrentRegion();
|
||||
}
|
||||
@@ -2401,13 +2380,14 @@ public class JetTypeInferrer {
|
||||
}
|
||||
else if (operationType == JetTokens.ELVIS) {
|
||||
JetType leftType = getType(context.scope, left, false, context);
|
||||
JetType rightType = right == null ? null : getType(context.scope, right, false, context);
|
||||
JetType rightType = right == null ? null : getType(context.scope, right, false, contextWithExpectedType);
|
||||
if (leftType != null) {
|
||||
if (!leftType.isNullable()) {
|
||||
context.trace.getErrorHandler().genericWarning(left.getNode(), "Elvis operator (?:) is always returns the left operand of non-nullable type " + leftType);
|
||||
}
|
||||
if (rightType != null) {
|
||||
result = TypeUtils.makeNullableAsSpecified(semanticServices.getTypeChecker().commonSupertype(leftType, rightType), rightType.isNullable());
|
||||
context.services.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType);
|
||||
return TypeUtils.makeNullableAsSpecified(semanticServices.getTypeChecker().commonSupertype(leftType, rightType), rightType.isNullable());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2745,14 +2725,14 @@ public class JetTypeInferrer {
|
||||
JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) deparenthesized;
|
||||
return resolveArrayAccessToLValue(arrayAccessExpression, right, expression.getOperationReference(), context);
|
||||
}
|
||||
JetType leftType = getType(scope, left, false, context);
|
||||
JetType leftType = getType(scope, left, false, context.replaceExpectedType(NO_EXPECTED_TYPE));
|
||||
if (right != null) {
|
||||
JetType rightType = getType(scope, right, false, context);
|
||||
if (rightType != null &&
|
||||
leftType != null &&
|
||||
!semanticServices.getTypeChecker().isConvertibleTo(rightType, leftType)) {
|
||||
context.trace.getErrorHandler().typeMismatch(right, leftType, rightType);
|
||||
}
|
||||
JetType rightType = getType(scope, right, false, context.replaceExpectedType(leftType));
|
||||
// if (rightType != null &&
|
||||
// leftType != null &&
|
||||
// !semanticServices.getTypeChecker().isConvertibleTo(rightType, leftType)) {
|
||||
// context.trace.getErrorHandler().typeMismatch(right, leftType, rightType);
|
||||
// }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
namespace automatic_casts
|
||||
|
||||
fun toInt(i: Int?): Int = if (i != null) i else 0
|
||||
fun illegalWhenBody(a: Any): Int = when(a) {
|
||||
is Int => a
|
||||
is String => <error>a</error>
|
||||
}
|
||||
fun illegalWhenBlock(a: Any): Int {
|
||||
when(a) {
|
||||
is Int => return a
|
||||
is String => return <error>a</error>
|
||||
}
|
||||
}
|
||||
fun declarations(a: Any?) {
|
||||
if (a is String) {
|
||||
val p4: (Int, String) = (2, a)
|
||||
}
|
||||
if (a is String?) {
|
||||
if (a != null) {
|
||||
val s: String = a
|
||||
}
|
||||
}
|
||||
if (a != null) {
|
||||
if (a is String?) {
|
||||
val s: String = a
|
||||
}
|
||||
}
|
||||
}
|
||||
fun tuples(a: Any?) {
|
||||
if (a != null) {
|
||||
val s: (Any, String) = (a, <error>a</error>)
|
||||
}
|
||||
if (a is String) {
|
||||
val s: (Any, String) = (a, a)
|
||||
}
|
||||
fun illegalTupleReturnType(): (Any, String) = (<error>a</error>, <error>a</error>)
|
||||
if (a is String) {
|
||||
fun legalTupleReturnType(): (Any, String) = (a, a)
|
||||
}
|
||||
val illegalFunctionLiteral: Function0<Int> = <error>{ <error>a</error> }</error>
|
||||
val illegalReturnValueInFunctionLiteral: Function0<Int> = { (): Int => <error>a</error> }
|
||||
|
||||
if (a is Int) {
|
||||
val legalFunctionLiteral: Function0<Int> = { a }
|
||||
val alsoLegalFunctionLiteral: Function0<Int> = { (): Int => a }
|
||||
}
|
||||
}
|
||||
fun returnFunctionLiteralBlock(a: Any?): Function0<Int> {
|
||||
if (a is Int) return { a }
|
||||
else return { 1 }
|
||||
}
|
||||
fun returnFunctionLiteral(a: Any?): Function0<Int> =
|
||||
if (a is Int) { (): Int => a }
|
||||
else { () => 1 }
|
||||
|
||||
fun illegalTupleReturnType(a: Any): (Any, String) = (a, <error>a</error>)
|
||||
|
||||
fun declarationInsidePattern(x: (Any, Any)): String = when(x) { is (val a is String, *) => a; else => "something" }
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace qualified_expressions
|
||||
|
||||
fun test(s: String?) {
|
||||
val a: Int = <error>s?.length</error>
|
||||
val b: Int? = s?.length
|
||||
val c: Int = s?.length ?: -11
|
||||
val d: Int = s?.length ?: <error>"empty"</error>
|
||||
val e: String = <error>s?.length</error> ?: "empty"
|
||||
val f: Int = s?.length ?: b ?: 1
|
||||
val g: Int? = e? startsWith("s")?.length
|
||||
}
|
||||
@@ -152,4 +152,67 @@ fun getStringLength(obj : Any) : Char? {
|
||||
if (obj !is String)
|
||||
return null
|
||||
return <info>obj</info>.get(0) // no cast to String is needed
|
||||
}
|
||||
}
|
||||
|
||||
fun toInt(i: Int?): Int = if (i != null) <info descr="Automatically cast to Int">i</info> else 0
|
||||
fun illegalWhenBody(a: Any): Int = when(a) {
|
||||
is Int => <info descr="Automatically cast to Int">a</info>
|
||||
is String => <error>a</error>
|
||||
}
|
||||
fun illegalWhenBlock(a: Any): Int {
|
||||
when(a) {
|
||||
is Int => return <info descr="Automatically cast to Int">a</info>
|
||||
is String => return <error>a</error>
|
||||
}
|
||||
}
|
||||
fun declarations(a: Any?) {
|
||||
if (a is String) {
|
||||
val p4: (Int, String) = (2, <info descr="Automatically cast to String">a</info>)
|
||||
}
|
||||
if (a is String?) {
|
||||
if (a != null) {
|
||||
val s: String = <info descr="Automatically cast to String">a</info>
|
||||
}
|
||||
}
|
||||
if (a != null) {
|
||||
if (a is String?) {
|
||||
val s: String = <info descr="Automatically cast to String">a</info>
|
||||
}
|
||||
}
|
||||
}
|
||||
fun vars(a: Any?) {
|
||||
var b: Int = 0
|
||||
if (a is Int) {
|
||||
b = <info descr="Automatically cast to Int">a</info>
|
||||
}
|
||||
}
|
||||
fun tuples(a: Any?) {
|
||||
if (a != null) {
|
||||
val s: (Any, String) = (<info descr="Automatically cast to Any">a</info>, <error>a</error>)
|
||||
}
|
||||
if (a is String) {
|
||||
val s: (Any, String) = (<info descr="Automatically cast to Any">a</info>, <info descr="Automatically cast to String">a</info>)
|
||||
}
|
||||
fun illegalTupleReturnType(): (Any, String) = (<error>a</error>, <error>a</error>)
|
||||
if (a is String) {
|
||||
fun legalTupleReturnType(): (Any, String) = (<info descr="Automatically cast to Any">a</info>, <info descr="Automatically cast to String">a</info>)
|
||||
}
|
||||
val illegalFunctionLiteral: Function0<Int> = <error>{ <error>a</error> }</error>
|
||||
val illegalReturnValueInFunctionLiteral: Function0<Int> = { (): Int => <error>a</error> }
|
||||
|
||||
if (a is Int) {
|
||||
val legalFunctionLiteral: Function0<Int> = { <info descr="Automatically cast to Int">a</info> }
|
||||
val alsoLegalFunctionLiteral: Function0<Int> = { (): Int => <info descr="Automatically cast to Int">a</info> }
|
||||
}
|
||||
}
|
||||
fun returnFunctionLiteralBlock(a: Any?): Function0<Int> {
|
||||
if (a is Int) return { <info descr="Automatically cast to Int">a</info> }
|
||||
else return { 1 }
|
||||
}
|
||||
fun returnFunctionLiteral(a: Any?): Function0<Int> =
|
||||
if (a is Int) { (): Int => <info descr="Automatically cast to Int">a</info> }
|
||||
else { () => 1 }
|
||||
|
||||
fun illegalTupleReturnType(a: Any): (Any, String) = (a, <error>a</error>)
|
||||
|
||||
fun declarationInsidePattern(x: (Any, Any)): String = when(x) { is (val a is String, *) => <info descr="Automatically cast to String">a</info>; else => "something" }
|
||||
Reference in New Issue
Block a user