diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index d8a116a..a5baf62 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -7762,7 +7762,11 @@ class Compiler( nameStartPos = t.pos } if (parentClassCtx?.isExtern == true && !isExtern) { - throw ScriptError(nameStartPos, "members of extern classes/objects must be marked extern") + val owner = parentClassCtx.name + throw ScriptError( + nameStartPos, + "member '$name' in extern class/object '$owner' must be declared extern (use `extern fun $name(...)`)" + ) } val extensionWrapperName = extTypeName?.let { extensionCallableName(it, name) } val classCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody @@ -8788,7 +8792,12 @@ class Compiler( } val parentClassCtx = codeContexts.lastOrNull() as? CodeContext.ClassBody if (parentClassCtx?.isExtern == true && !isExtern) { - throw ScriptError(nameStartPos, "members of extern classes/objects must be marked extern") + val owner = parentClassCtx.name + val kind = if (isMutable) "var" else "val" + throw ScriptError( + nameStartPos, + "member '$name' in extern class/object '$owner' must be declared extern (use `extern $kind $name: ...`)" + ) } val receiverNormalization = normalizeReceiverTypeDecl(receiverTypeDecl, emptySet()) diff --git a/lynglib/src/commonTest/kotlin/TypesTest.kt b/lynglib/src/commonTest/kotlin/TypesTest.kt index a9e944b..36f7878 100644 --- a/lynglib/src/commonTest/kotlin/TypesTest.kt +++ b/lynglib/src/commonTest/kotlin/TypesTest.kt @@ -615,6 +615,32 @@ class TypesTest { """.trimIndent()) } + + @Test + fun testExternGenerics() = runTest { + eval(""" + extern fun f(x: T): T + extern class Cell { + extern var value: T + } + """) + } + + @Test + fun testExternClassMemberMustBeExternMessage() = runTest { + val e = assertFailsWith { + eval( + """ + extern class Cell { + var value: T + } + """.trimIndent() + ) + } + assertTrue(e.message?.contains("must be declared extern") == true) + assertTrue(e.message?.contains("extern var value") == true) + } + // @Test fun nonTrivialOperatorsTest() = runTest { // val s = Script.newScope() // s.eval("""