Implement nullable shorthand for params

This commit is contained in:
sergeych committed 2026-02-05 19:14:03 +03:00
1 parent 7e538ed8aa
commit 9db5b12c31
3 files changed
+205 -5

No files matched your search

@@ -2641,8 +2641,31 @@ class Compiler(
access
}
// Nullable shorthand: "x?" means Object? (or makes explicit type nullable)
var nullableHint = false
val nullableHintPos = cc.savePos()
cc.skipWsTokens()
if (cc.peekNextNonWhitespace().type == Token.Type.QUESTION) {
cc.nextNonWhitespace()
nullableHint = true
} else {
cc.restorePos(nullableHintPos)
}
// type information (semantic + mini syntax)
val (typeInfo, miniType) = parseTypeDeclarationWithMini()
if (nullableHint) {
val afterHint = cc.savePos()
cc.skipWsTokens()
if (cc.peekNextNonWhitespace().type == Token.Type.COLON) {
throw ScriptError(t.pos, "nullable shorthand '?' cannot be combined with explicit type")
}
cc.restorePos(afterHint)
}
var (typeInfo, miniType) = parseTypeDeclarationWithMini()
if (nullableHint) {
typeInfo = makeTypeDeclNullable(typeInfo)
miniType = miniType?.let { makeMiniTypeNullable(it) }
}
var defaultValue: Statement? = null
cc.ifNextIs(Token.Type.ASSIGN) {
@@ -2713,6 +2736,31 @@ class Compiler(
return parseTypeUnionWithMini()
}
private fun makeTypeDeclNullable(type: TypeDecl): TypeDecl {
if (type.isNullable) return type
return when (type) {
TypeDecl.TypeAny -> TypeDecl.TypeNullableAny
TypeDecl.TypeNullableAny -> type
is TypeDecl.Function -> type.copy(nullable = true)
is TypeDecl.TypeVar -> type.copy(nullable = true)
is TypeDecl.Union -> type.copy(nullable = true)
is TypeDecl.Intersection -> type.copy(nullable = true)
is TypeDecl.Simple -> TypeDecl.Simple(type.name, true)
is TypeDecl.Generic -> TypeDecl.Generic(type.name, type.args, true)
}
}
private fun makeMiniTypeNullable(type: MiniTypeRef): MiniTypeRef {
return when (type) {
is MiniTypeName -> type.copy(nullable = true)
is MiniGenericType -> type.copy(nullable = true)
is MiniFunctionType -> type.copy(nullable = true)
is MiniTypeVar -> type.copy(nullable = true)
is MiniTypeUnion -> type.copy(nullable = true)
is MiniTypeIntersection -> type.copy(nullable = true)
}
}
private fun parseTypeUnionWithMini(): Pair<TypeDecl, MiniTypeRef> {
var left = parseTypeIntersectionWithMini()
val options = mutableListOf(left)
@@ -146,4 +146,24 @@ class TypesTest {
println("Result: "+limit)
""".trimIndent())
}
@Test
fun testNullableHints() = runTest {
eval("""
// nullable, without type os Object?
class N(x=null)
assertEquals(null, N().x)
assertEquals("foo", N("foo").x)
// nullable shortcut (x?)is same as (var x: Object?)
class A(x?)
assertEquals(null, A(null).x)
assertEquals("ok", A("ok").x)
// same in function: x? is a shortcut for (x: Object?)
fun f(x?) = x?.let { x + "!" }
assertEquals(null, f(null))
assertEquals("ok!", f("ok"))
""".trimIndent()
)
}
}