diff --git a/spec/lang/declaration/local_type_spec.lua b/spec/lang/declaration/local_type_spec.lua index f65c17cd..3f048d1e 100644 --- a/spec/lang/declaration/local_type_spec.lua +++ b/spec/lang/declaration/local_type_spec.lua @@ -231,4 +231,16 @@ describe("local type", function() ]], { { y = 14, msg = 'in record field: Data: string "invalid" is not a member of MyEnum' } })) + + it("does not accept a built-in simple type followed by a dotted name (regression test for #1136)", util.check_type_error([[ + local a: number.x.y.z = 1 + local b: string.a.b = "x" + local c: integer.foo = 1 + local d: boolean.foo = true + ]], { + { y = 1, msg = "unknown type number.x.y.z" }, + { y = 2, msg = "unknown type string.a.b" }, + { y = 3, msg = "unknown type integer.foo" }, + { y = 4, msg = "unknown type boolean.foo" }, + })) end) diff --git a/teal.lua b/teal.lua index 3e56bbc4..b97d34bd 100644 --- a/teal.lua +++ b/teal.lua @@ -1971,7 +1971,12 @@ end parse_simple_type_or_nominal = function(state, block) local tk = block.tk local st = simple_types[tk] - if st then + + + + + local rest = block[reader.BLOCK_INDEXES.NOMINAL_TYPE.NAME + 1] + if st and not (rest and rest.kind == "identifier") then return new_type(state, block, tk) elseif tk == "table" and block.kind ~= "nominal_type" then local typ = new_type(state, block, "map") diff --git a/teal/ast.lua b/teal/ast.lua index ffef498b..ed3105f0 100644 --- a/teal/ast.lua +++ b/teal/ast.lua @@ -1969,7 +1969,12 @@ end parse_simple_type_or_nominal = function(state, block) local tk = block.tk local st = simple_types[tk] - if st then + + + + + local rest = block[reader.BLOCK_INDEXES.NOMINAL_TYPE.NAME + 1] + if st and not (rest and rest.kind == "identifier") then return new_type(state, block, tk) elseif tk == "table" and block.kind ~= "nominal_type" then local typ = new_type(state, block, "map") diff --git a/teal/ast.tl b/teal/ast.tl index 65306fb6..6bb0e65a 100644 --- a/teal/ast.tl +++ b/teal/ast.tl @@ -1969,7 +1969,12 @@ end parse_simple_type_or_nominal = function(state: ParseState, block: Block): FirstOrderType local tk = block.tk local st = simple_types[tk as TypeName] - if st then + -- Only short-circuit to the built-in simple type when there is no trailing + -- dotted name part. Otherwise fall through to the nominal path below so that + -- an invalid annotation like `number.x.y.z` is reported as an unknown type + -- instead of being silently accepted as `number` (issue #1136). + local rest = block[reader.BLOCK_INDEXES.NOMINAL_TYPE.NAME + 1] + if st and not (rest and rest.kind == "identifier") then return new_type(state, block, tk as TypeName) as StructuralType elseif tk == "table" and block.kind ~= "nominal_type" then -- reader already turns plain `table` into map_type local typ = new_type(state, block, "map") as MapType diff --git a/tl.lua b/tl.lua index caf916c2..b90777b6 100644 --- a/tl.lua +++ b/tl.lua @@ -2225,7 +2225,12 @@ end parse_simple_type_or_nominal = function(state, block) local tk = block.tk local st = simple_types[tk] - if st then + + + + + local rest = block[reader.BLOCK_INDEXES.NOMINAL_TYPE.NAME + 1] + if st and not (rest and rest.kind == "identifier") then return new_type(state, block, tk) elseif tk == "table" and block.kind ~= "nominal_type" then local typ = new_type(state, block, "map")