mirror of
https://github.com/irmen/prog8.git
synced 2024-11-18 19:12:44 +00:00
no longer report unknown type errors as well for unknown symbols,
added a bunch more unit tests for symbol scoping rules
This commit is contained in:
parent
0c2f30fd45
commit
b00db4f8a2
@ -428,8 +428,15 @@ internal class AstChecker(private val program: Program,
|
||||
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt)) {
|
||||
if(targetDt.isIterable)
|
||||
errors.err("cannot assign value to string or array", assignment.value.position)
|
||||
else if(!(valueDt istype DataType.STR && targetDt istype DataType.UWORD))
|
||||
errors.err("type of value $valueDt doesn't match target $targetDt", assignment.value.position)
|
||||
else if(!(valueDt istype DataType.STR && targetDt istype DataType.UWORD)) {
|
||||
if(targetDt.isUnknown) {
|
||||
if(assignment.target.identifier?.targetStatement(program)!=null)
|
||||
errors.err("target datatype is unknown", assignment.target.position)
|
||||
// otherwise, another error about missing symbol is already reported.
|
||||
} else {
|
||||
errors.err("type of value $valueDt doesn't match target $targetDt", assignment.value.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(assignment.value is TypecastExpression) {
|
||||
|
@ -3,6 +3,7 @@ package prog8tests
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import io.kotest.matchers.types.shouldBeSameInstanceAs
|
||||
import prog8.ast.GlobalNamespace
|
||||
@ -10,13 +11,15 @@ import prog8.ast.base.ParentSentinel
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.assertFailure
|
||||
import prog8tests.helpers.assertSuccess
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
|
||||
class TestScoping: FunSpec({
|
||||
|
||||
test("testModulesParentIsGlobalNamespace") {
|
||||
test("modules parent is global namespace") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
@ -31,7 +34,7 @@ class TestScoping: FunSpec({
|
||||
module.parent.parent shouldBe instanceOf<ParentSentinel>()
|
||||
}
|
||||
|
||||
test("testAnonScopeVarsMovedIntoSubroutineScope") {
|
||||
test("anon scope vars moved into subroutine scope") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
@ -69,7 +72,7 @@ class TestScoping: FunSpec({
|
||||
repeatbody.statements[1] shouldBe instanceOf<PostIncrDecr>()
|
||||
}
|
||||
|
||||
test("testLabelsWithAnonScopes") {
|
||||
test("labels with anon scopes") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
@ -133,4 +136,214 @@ class TestScoping: FunSpec({
|
||||
}
|
||||
}
|
||||
|
||||
test("good subroutine call without qualified names") {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
routine()
|
||||
routine2()
|
||||
|
||||
sub routine2() {
|
||||
}
|
||||
}
|
||||
sub routine() {
|
||||
start()
|
||||
}
|
||||
}
|
||||
"""
|
||||
compileText(C64Target, false, text, writeAssembly = false).assertSuccess()
|
||||
}
|
||||
|
||||
test("wrong subroutine call without qualified names") {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
sub routine2() {
|
||||
}
|
||||
}
|
||||
sub routine() {
|
||||
routine2()
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors= ErrorReporterForTests()
|
||||
compileText(C64Target, false, text, writeAssembly = false, errors = errors).assertFailure()
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "undefined symbol: routine2"
|
||||
}
|
||||
|
||||
test("good subroutine calls with qualified names (from root)") {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
main.routine()
|
||||
main.start.routine2()
|
||||
|
||||
sub routine2() {
|
||||
}
|
||||
}
|
||||
sub routine() {
|
||||
main.start.routine2()
|
||||
}
|
||||
}
|
||||
"""
|
||||
compileText(C64Target, false, text, writeAssembly = false).assertSuccess()
|
||||
}
|
||||
|
||||
test("wrong subroutine calls with qualified names (not from root)") {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
start.routine2()
|
||||
wrong.start.routine2()
|
||||
sub routine2() {
|
||||
}
|
||||
}
|
||||
sub routine() {
|
||||
start.routine2()
|
||||
wrong.start.routine2()
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors= ErrorReporterForTests()
|
||||
compileText(C64Target, false, text, writeAssembly = false, errors=errors).assertFailure()
|
||||
errors.errors.size shouldBe 4
|
||||
errors.errors[0] shouldContain "undefined symbol: start.routine2"
|
||||
errors.errors[1] shouldContain "undefined symbol: wrong.start.routine2"
|
||||
errors.errors[2] shouldContain "undefined symbol: start.routine2"
|
||||
errors.errors[3] shouldContain "undefined symbol: wrong.start.routine2"
|
||||
}
|
||||
|
||||
test("good variables without qualified names") {
|
||||
val text="""
|
||||
main {
|
||||
ubyte v1
|
||||
|
||||
sub start() {
|
||||
ubyte v2
|
||||
v1=1
|
||||
v2=2
|
||||
|
||||
sub routine2() {
|
||||
ubyte v3
|
||||
v1=1
|
||||
v2=2
|
||||
v3=3
|
||||
}
|
||||
}
|
||||
sub routine() {
|
||||
ubyte v4
|
||||
v1=1
|
||||
v4=4
|
||||
}
|
||||
}
|
||||
"""
|
||||
compileText(C64Target, false, text, writeAssembly = false).assertSuccess()
|
||||
}
|
||||
|
||||
test("wrong variables without qualified names") {
|
||||
val text="""
|
||||
main {
|
||||
ubyte v1
|
||||
|
||||
sub start() {
|
||||
ubyte v2
|
||||
v1=1
|
||||
v2=2
|
||||
v3=3 ; can't access
|
||||
v4=4 ; can't access
|
||||
sub routine2() {
|
||||
ubyte v3
|
||||
v1=1
|
||||
v2=2
|
||||
v3=3
|
||||
v4=3 ;can't access
|
||||
}
|
||||
}
|
||||
sub routine() {
|
||||
ubyte v4
|
||||
v1=1
|
||||
v2=2 ; can't access
|
||||
v3=3 ; can't access
|
||||
v4=4
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors= ErrorReporterForTests()
|
||||
compileText(C64Target, false, text, writeAssembly = false, errors=errors).assertFailure()
|
||||
errors.errors.size shouldBe 5
|
||||
errors.errors[0] shouldContain "undefined symbol: v3"
|
||||
errors.errors[1] shouldContain "undefined symbol: v4"
|
||||
errors.errors[2] shouldContain "undefined symbol: v4"
|
||||
errors.errors[3] shouldContain "undefined symbol: v2"
|
||||
errors.errors[4] shouldContain "undefined symbol: v3"
|
||||
}
|
||||
|
||||
test("good variable refs with qualified names (from root)") {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
uword xx
|
||||
xx = &main.routine
|
||||
main.routine(5)
|
||||
main.routine.value = 5
|
||||
main.routine.arg = 5
|
||||
xx = &main.routine.nested
|
||||
main.routine.nested(5)
|
||||
main.routine.nested.nestedvalue = 5
|
||||
main.routine.nested.arg2 = 5
|
||||
}
|
||||
|
||||
sub routine(ubyte arg) {
|
||||
ubyte value
|
||||
|
||||
sub nested(ubyte arg2) {
|
||||
ubyte nestedvalue
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
compileText(C64Target, false, text, writeAssembly = false).assertSuccess()
|
||||
}
|
||||
|
||||
test("wrong variable refs with qualified names (not from root)") {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
uword xx
|
||||
xx = &routine
|
||||
routine(5)
|
||||
routine.value = 5
|
||||
routine.arg = 5
|
||||
xx = &routine.nested
|
||||
routine.nested(5)
|
||||
routine.nested.nestedvalue = 5
|
||||
routine.nested.arg2 = 5
|
||||
xx = &wrong.routine
|
||||
wrong.routine.value = 5
|
||||
}
|
||||
|
||||
sub routine(ubyte arg) {
|
||||
ubyte value
|
||||
|
||||
sub nested(ubyte arg2) {
|
||||
ubyte nestedvalue
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors= ErrorReporterForTests()
|
||||
compileText(C64Target, false, text, writeAssembly = false, errors=errors).assertFailure()
|
||||
errors.errors.size shouldBe 10
|
||||
errors.errors[0] shouldContain "undefined symbol: routine"
|
||||
errors.errors[1] shouldContain "undefined symbol: routine"
|
||||
errors.errors[2] shouldContain "undefined symbol: routine.value"
|
||||
errors.errors[3] shouldContain "undefined symbol: routine.arg"
|
||||
errors.errors[4] shouldContain "undefined symbol: routine.nested"
|
||||
errors.errors[5] shouldContain "undefined symbol: routine.nested"
|
||||
errors.errors[6] shouldContain "undefined symbol: routine.nested.nestedvalue"
|
||||
errors.errors[7] shouldContain "undefined symbol: routine.nested.arg2"
|
||||
errors.errors[8] shouldContain "undefined symbol: wrong.routine"
|
||||
errors.errors[9] shouldContain "undefined symbol: wrong.routine.value"
|
||||
}
|
||||
})
|
||||
|
@ -127,7 +127,7 @@ class TestSubroutines: FunSpec({
|
||||
(call.args.single() as IdentifierReference).nameInSource.single() shouldBe "thing"
|
||||
}
|
||||
|
||||
test("arrayParameterNotYetAllowed_ButShouldPerhapsBe") {
|
||||
test("array param not yet allowd (but should perhaps be?)") {
|
||||
// note: the *parser* accepts this as it is valid *syntax*,
|
||||
// however, it's not (yet) valid for the compiler
|
||||
val text = """
|
||||
@ -187,7 +187,7 @@ class TestSubroutines: FunSpec({
|
||||
func.statements.isEmpty() shouldBe true
|
||||
}
|
||||
|
||||
test("testUwordParameterAndNormalVarIndexedAsArrayWorkAsDirectMemoryRead") {
|
||||
test("uword param and normal varindexed as array work as DirectMemoryRead") {
|
||||
val text="""
|
||||
main {
|
||||
sub thing(uword rr) {
|
||||
@ -232,7 +232,7 @@ class TestSubroutines: FunSpec({
|
||||
(valueZZexpr.right as NumericLiteralValue).number.toInt() shouldBe 3
|
||||
}
|
||||
|
||||
test("testUwordParameterAndNormalVarIndexedAsArrayWorkAsMemoryWrite") {
|
||||
test("uword param and normal varindexed as array work as MemoryWrite") {
|
||||
val text="""
|
||||
main {
|
||||
sub thing(uword rr) {
|
||||
|
Loading…
Reference in New Issue
Block a user