Skip to content

Commit

Permalink
fix incorrect nullable resolution of extension properties
Browse files Browse the repository at this point in the history
  • Loading branch information
sunny-chung committed Apr 16, 2024
1 parent 0092dbf commit 860361f
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -1733,6 +1733,7 @@ open class SemanticAnalyzer(val rootNode: ASTNode, val executionEnvironment: Exe

// find member
val subjectType = subject.type().unboxClassTypeAsCompanion().toDataType()
log.v { "NavigationNode pos=${position} member=${member.name} subjectType = ${subjectType.descriptiveName} ${subjectType.nameWithNullable} ${subjectType.isNullable}" }
val subjectTypesToSearch = if (operator == "?." && subjectType.isNullable) {
listOf(subjectType.copyOf(isNullable = false), subjectType)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ open class SymbolTable(
return extensionProperties.asSequence()
.filter {
it.value.receiverType!!.name == resolvedReceiver.name &&
(resolvedReceiver.isNullable || !it.value.receiverType!!.isNullable) &&
(!resolvedReceiver.isNullable || it.value.receiverType!!.isNullable) &&
it.value.declaredName == declaredName
}
// Here assumes at most 3 same-name extension properties declared: exact type or Any? or Any. exact type one has higher precedence
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.sunnychung.lib.multiplatform.kotlite.test.interpreter
import com.sunnychung.lib.multiplatform.kotlite.model.ExecutionEnvironment
import com.sunnychung.lib.multiplatform.kotlite.model.ExtensionProperty
import com.sunnychung.lib.multiplatform.kotlite.model.IntValue
import com.sunnychung.lib.multiplatform.kotlite.model.NullValue
import com.sunnychung.lib.multiplatform.kotlite.model.StringValue
import com.sunnychung.lib.multiplatform.kotlite.model.TypeParameter
import com.sunnychung.lib.multiplatform.kotlite.test.semanticanalysis.assertSemanticFail
Expand Down Expand Up @@ -260,4 +261,19 @@ class CustomBuiltinExtensionPropertyTest {
assertEquals(10, (symbolTable.findPropertyByDeclaredName("a") as IntValue).value)
assertEquals(-5, (symbolTable.findPropertyByDeclaredName("b") as IntValue).value)
}

@Test
fun accessNullableExtensionPropertyMemberWithDot() {
val env = ExecutionEnvironment().apply {
registerExtensionProperty(ExtensionProperty(
declaredName = "prop",
receiver = "Int",
type = "Pair<Int, Double>?",
getter = { interpreter, subject, typeArgs -> NullValue }
))
}
assertSemanticFail("""
val a = 1.prop.second
""".trimIndent(), environment = env)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import com.sunnychung.lib.multiplatform.kotlite.model.CustomFunctionDefinition
import com.sunnychung.lib.multiplatform.kotlite.model.CustomFunctionParameter
import com.sunnychung.lib.multiplatform.kotlite.model.ExecutionEnvironment
import com.sunnychung.lib.multiplatform.kotlite.model.IntValue
import com.sunnychung.lib.multiplatform.kotlite.model.NullValue
import com.sunnychung.lib.multiplatform.kotlite.model.SourcePosition
import com.sunnychung.lib.multiplatform.kotlite.model.StringValue
import com.sunnychung.lib.multiplatform.kotlite.test.semanticanalysis.assertSemanticFail
import kotlin.test.Test
import kotlin.test.assertEquals

Expand Down Expand Up @@ -226,4 +228,46 @@ class CustomBuiltinFunctionTest {
assertEquals(22, (symbolTable.findPropertyByDeclaredName("d") as IntValue).value)
assertEquals(12, (symbolTable.findPropertyByDeclaredName("e") as IntValue).value)
}
@Test
fun accessMemberOfNullableValueReturnedByGlobalFunctionWithDot() {
val env = ExecutionEnvironment().apply {
registerFunction(CustomFunctionDefinition(
receiverType = null,
functionName = "myGlobalFunc",
returnType = "Pair<String, Int>?",
parameterTypes = listOf(
CustomFunctionParameter("a", "String"),
CustomFunctionParameter("b", "Int"),
),
executable = { interpreter, _, args, typeArgs ->
NullValue
},
position = SourcePosition("<Test>", 1, 1),
))
}
assertSemanticFail("""
val b = myGlobalFunc("aaaaa", 123).second
""".trimIndent(), environment = env)
}
@Test
fun accessMemberOfNullableValueReturnedByExtensionFunctionWithDot() {
val env = ExecutionEnvironment().apply {
registerFunction(CustomFunctionDefinition(
receiverType = "String",
functionName = "myExtFunc",
returnType = "Pair<String, Int>?",
parameterTypes = listOf(
CustomFunctionParameter("a", "String"),
CustomFunctionParameter("b", "Int"),
),
executable = { interpreter, _, args, typeArgs ->
NullValue
},
position = SourcePosition("<Test>", 1, 1),
))
}
assertSemanticFail("""
val b = "abc".myExtFunc("aaaaa", 123).second
""".trimIndent(), environment = env)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ class CustomBuiltinPropertyTest {
""".trimIndent(), environment = env)
}

@Test
fun accessNullablePropertyMemberWithDot() {
val env = ExecutionEnvironment().apply {
registerGlobalProperty(
GlobalProperty(
position = SourcePosition("<Test>", 1, 1),
declaredName = "x",
type = "Pair<Int, Int>?",
isMutable = false,
getter = { interpreter -> DoubleValue(3.14, interpreter.symbolTable()) },
)
)
}
assertSemanticFail("""
val y = x.first
""".trimIndent(), environment = env)
}

@Test
fun incorrectType() {
val env = ExecutionEnvironment().apply {
Expand Down

0 comments on commit 860361f

Please sign in to comment.