mirror of
https://github.com/irmen/prog8.git
synced 2024-09-07 03:54:27 +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,9 +428,16 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt)) {
|
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt)) {
|
||||||
if(targetDt.isIterable)
|
if(targetDt.isIterable)
|
||||||
errors.err("cannot assign value to string or array", assignment.value.position)
|
errors.err("cannot assign value to string or array", assignment.value.position)
|
||||||
else if(!(valueDt istype DataType.STR && targetDt istype DataType.UWORD))
|
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)
|
errors.err("type of value $valueDt doesn't match target $targetDt", assignment.value.position)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(assignment.value is TypecastExpression) {
|
if(assignment.value is TypecastExpression) {
|
||||||
if(assignment.isAugmentable && targetDt istype DataType.FLOAT)
|
if(assignment.isAugmentable && targetDt istype DataType.FLOAT)
|
||||||
|
@ -3,6 +3,7 @@ package prog8tests
|
|||||||
import io.kotest.assertions.withClue
|
import io.kotest.assertions.withClue
|
||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
|
import io.kotest.matchers.string.shouldContain
|
||||||
import io.kotest.matchers.types.instanceOf
|
import io.kotest.matchers.types.instanceOf
|
||||||
import io.kotest.matchers.types.shouldBeSameInstanceAs
|
import io.kotest.matchers.types.shouldBeSameInstanceAs
|
||||||
import prog8.ast.GlobalNamespace
|
import prog8.ast.GlobalNamespace
|
||||||
@ -10,13 +11,15 @@ import prog8.ast.base.ParentSentinel
|
|||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
|
import prog8tests.helpers.assertFailure
|
||||||
import prog8tests.helpers.assertSuccess
|
import prog8tests.helpers.assertSuccess
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
|
|
||||||
|
|
||||||
class TestScoping: FunSpec({
|
class TestScoping: FunSpec({
|
||||||
|
|
||||||
test("testModulesParentIsGlobalNamespace") {
|
test("modules parent is global namespace") {
|
||||||
val src = """
|
val src = """
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
@ -31,7 +34,7 @@ class TestScoping: FunSpec({
|
|||||||
module.parent.parent shouldBe instanceOf<ParentSentinel>()
|
module.parent.parent shouldBe instanceOf<ParentSentinel>()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("testAnonScopeVarsMovedIntoSubroutineScope") {
|
test("anon scope vars moved into subroutine scope") {
|
||||||
val src = """
|
val src = """
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
@ -69,7 +72,7 @@ class TestScoping: FunSpec({
|
|||||||
repeatbody.statements[1] shouldBe instanceOf<PostIncrDecr>()
|
repeatbody.statements[1] shouldBe instanceOf<PostIncrDecr>()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("testLabelsWithAnonScopes") {
|
test("labels with anon scopes") {
|
||||||
val src = """
|
val src = """
|
||||||
main {
|
main {
|
||||||
sub start() {
|
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"
|
(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*,
|
// note: the *parser* accepts this as it is valid *syntax*,
|
||||||
// however, it's not (yet) valid for the compiler
|
// however, it's not (yet) valid for the compiler
|
||||||
val text = """
|
val text = """
|
||||||
@ -187,7 +187,7 @@ class TestSubroutines: FunSpec({
|
|||||||
func.statements.isEmpty() shouldBe true
|
func.statements.isEmpty() shouldBe true
|
||||||
}
|
}
|
||||||
|
|
||||||
test("testUwordParameterAndNormalVarIndexedAsArrayWorkAsDirectMemoryRead") {
|
test("uword param and normal varindexed as array work as DirectMemoryRead") {
|
||||||
val text="""
|
val text="""
|
||||||
main {
|
main {
|
||||||
sub thing(uword rr) {
|
sub thing(uword rr) {
|
||||||
@ -232,7 +232,7 @@ class TestSubroutines: FunSpec({
|
|||||||
(valueZZexpr.right as NumericLiteralValue).number.toInt() shouldBe 3
|
(valueZZexpr.right as NumericLiteralValue).number.toInt() shouldBe 3
|
||||||
}
|
}
|
||||||
|
|
||||||
test("testUwordParameterAndNormalVarIndexedAsArrayWorkAsMemoryWrite") {
|
test("uword param and normal varindexed as array work as MemoryWrite") {
|
||||||
val text="""
|
val text="""
|
||||||
main {
|
main {
|
||||||
sub thing(uword rr) {
|
sub thing(uword rr) {
|
||||||
|
Loading…
Reference in New Issue
Block a user