diff --git a/CHANGELOG.md b/CHANGELOG.md index efb802825..eaf53e5e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `foreach` loops now properly handle `as coins` map value serialization type: PR [#1186](https://github.com/tact-lang/tact/pull/1186) - The typechecker now rejects integer map key types with variable width (`coins`, `varint16`, `varint32`, `varuint16`, `varuint32`): PR [#1276](https://github.com/tact-lang/tact/pull/1276) - Code generation for `self` argument in optional struct methods: PR [#1284](https://github.com/tact-lang/tact/pull/1284) +- 'The "remainder" field can only be the last field:' inspection now shows location: PR [#1300](https://github.com/tact-lang/tact/pull/1300) ### Docs diff --git a/src/types/__snapshots__/resolveDescriptors.spec.ts.snap b/src/types/__snapshots__/resolveDescriptors.spec.ts.snap index adcfecf6e..c8091958f 100644 --- a/src/types/__snapshots__/resolveDescriptors.spec.ts.snap +++ b/src/types/__snapshots__/resolveDescriptors.spec.ts.snap @@ -338,6 +338,16 @@ Line 8, col 1: " `; +exports[`resolveDescriptors should fail descriptors for message-decl-remainder-in-the-middle 1`] = ` +":8:5: The "remainder" field can only be the last field of the message +Line 8, col 5: + 7 | a: Int; +> 8 | s: Cell as remaining; + ^~~~~~~~~~~~~~~~~~~~ + 9 | b: Int; +" +`; + exports[`resolveDescriptors should fail descriptors for message-negative-opcode-1 1`] = ` ":1:9: Opcode of message "Foo" is negative ('-1') which is not allowed Line 1, col 9: @@ -590,7 +600,15 @@ Line 4, col 8: " `; -exports[`resolveDescriptors should fail descriptors for struct-decl-remainder-in-the-middle 1`] = `"The "remainder" field can only be the last field of the struct"`; +exports[`resolveDescriptors should fail descriptors for struct-decl-remainder-in-the-middle 1`] = ` +":8:5: The "remainder" field can only be the last field of the struct +Line 8, col 5: + 7 | a: Int; +> 8 | s: Cell as remaining; + ^~~~~~~~~~~~~~~~~~~~ + 9 | b: Int; +" +`; exports[`resolveDescriptors should fail descriptors for struct-decl-self-reference 1`] = ` ":4:8: Self-referencing types are not supported: type "A" refers to itself in its definition @@ -11937,6 +11955,333 @@ exports[`resolveDescriptors should resolve descriptors for map-value-as-varuint exports[`resolveDescriptors should resolve descriptors for map-value-as-varuint 2`] = `[]`; +exports[`resolveDescriptors should resolve descriptors for message-decl-remainder 1`] = ` +[ + { + "ast": { + "attributes": [], + "declarations": [], + "id": 2, + "kind": "trait", + "loc": trait BaseTrait { }, + "name": { + "id": 1, + "kind": "id", + "loc": BaseTrait, + "text": "BaseTrait", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "BaseTrait", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 1020, + }, + { + "ast": { + "id": 4, + "kind": "primitive_type_decl", + "loc": primitive Int;, + "name": { + "id": 3, + "kind": "id", + "loc": Int, + "text": "Int", + }, + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "primitive_type_decl", + "name": "Int", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 38154, + }, + { + "ast": { + "id": 6, + "kind": "primitive_type_decl", + "loc": primitive Cell;, + "name": { + "id": 5, + "kind": "id", + "loc": Cell, + "text": "Cell", + }, + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "primitive_type_decl", + "name": "Cell", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 26294, + }, + { + "ast": { + "fields": [ + { + "as": null, + "id": 10, + "initializer": null, + "kind": "field_decl", + "loc": a: Int, + "name": { + "id": 8, + "kind": "id", + "loc": a, + "text": "a", + }, + "type": { + "id": 9, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + { + "as": null, + "id": 13, + "initializer": null, + "kind": "field_decl", + "loc": b: Int, + "name": { + "id": 11, + "kind": "id", + "loc": b, + "text": "b", + }, + "type": { + "id": 12, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + { + "as": { + "id": 16, + "kind": "id", + "loc": remaining, + "text": "remaining", + }, + "id": 17, + "initializer": null, + "kind": "field_decl", + "loc": s: Cell as remaining, + "name": { + "id": 14, + "kind": "id", + "loc": s, + "text": "s", + }, + "type": { + "id": 15, + "kind": "type_id", + "loc": Cell, + "text": "Cell", + }, + }, + ], + "id": 18, + "kind": "message_decl", + "loc": message Test { + a: Int; + b: Int; + s: Cell as remaining; +}, + "name": { + "id": 7, + "kind": "id", + "loc": Test, + "text": "Test", + }, + "opcode": null, + }, + "constants": [], + "dependsOn": [], + "fields": [ + { + "abi": { + "name": "a", + "type": { + "format": 257, + "kind": "simple", + "optional": false, + "type": "int", + }, + }, + "as": null, + "ast": { + "as": null, + "id": 10, + "initializer": null, + "kind": "field_decl", + "loc": a: Int, + "name": { + "id": 8, + "kind": "id", + "loc": a, + "text": "a", + }, + "type": { + "id": 9, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + "default": undefined, + "index": 0, + "loc": a: Int, + "name": "a", + "type": { + "kind": "ref", + "name": "Int", + "optional": false, + }, + }, + { + "abi": { + "name": "b", + "type": { + "format": 257, + "kind": "simple", + "optional": false, + "type": "int", + }, + }, + "as": null, + "ast": { + "as": null, + "id": 13, + "initializer": null, + "kind": "field_decl", + "loc": b: Int, + "name": { + "id": 11, + "kind": "id", + "loc": b, + "text": "b", + }, + "type": { + "id": 12, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + "default": undefined, + "index": 1, + "loc": b: Int, + "name": "b", + "type": { + "kind": "ref", + "name": "Int", + "optional": false, + }, + }, + { + "abi": { + "name": "s", + "type": { + "format": "remainder", + "kind": "simple", + "optional": false, + "type": "cell", + }, + }, + "as": "remaining", + "ast": { + "as": { + "id": 16, + "kind": "id", + "loc": remaining, + "text": "remaining", + }, + "id": 17, + "initializer": null, + "kind": "field_decl", + "loc": s: Cell as remaining, + "name": { + "id": 14, + "kind": "id", + "loc": s, + "text": "s", + }, + "type": { + "id": 15, + "kind": "type_id", + "loc": Cell, + "text": "Cell", + }, + }, + "default": undefined, + "index": 2, + "loc": s: Cell as remaining, + "name": "s", + "type": { + "kind": "ref", + "name": "Cell", + "optional": false, + }, + }, + ], + "functions": Map {}, + "header": { + "base": 10, + "id": 0, + "kind": "number", + "loc": , + "value": 3344478825n, + }, + "init": null, + "interfaces": [], + "kind": "struct", + "name": "Test", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": "Test{a:int257,b:int257,s:remainder}", + "tlb": "test#c758b269 a:int257 b:int257 s:remainder = Test", + "traits": [], + "uid": 44104, + }, +] +`; + +exports[`resolveDescriptors should resolve descriptors for message-decl-remainder 2`] = `[]`; + exports[`resolveDescriptors should resolve descriptors for message-opcode-expr 1`] = ` [ { diff --git a/src/types/resolveSignatures.ts b/src/types/resolveSignatures.ts index b5f5ec72f..11167f14e 100644 --- a/src/types/resolveSignatures.ts +++ b/src/types/resolveSignatures.ts @@ -187,8 +187,11 @@ export function resolveSignatures(ctx: CompilerContext, Ast: FactoryAst) { // Check for no "remainder" in the middle of the struct for (const field of t.fields.slice(0, -1)) { if (field.as === "remaining") { + const kind = + t.ast.kind === "message_decl" ? "message" : "struct"; throwCompilationError( - `The "remainder" field can only be the last field of the struct`, + `The "remainder" field can only be the last field of the ${kind}`, + field.loc, ); } } diff --git a/src/types/test-failed/message-decl-remainder-in-the-middle.tact b/src/types/test-failed/message-decl-remainder-in-the-middle.tact new file mode 100644 index 000000000..459a31a05 --- /dev/null +++ b/src/types/test-failed/message-decl-remainder-in-the-middle.tact @@ -0,0 +1,10 @@ +trait BaseTrait { } + +primitive Int; +primitive Cell; + +message Test { + a: Int; + s: Cell as remaining; + b: Int; +} diff --git a/src/types/test/message-decl-remainder.tact b/src/types/test/message-decl-remainder.tact new file mode 100644 index 000000000..105f013a0 --- /dev/null +++ b/src/types/test/message-decl-remainder.tact @@ -0,0 +1,10 @@ +trait BaseTrait { } + +primitive Int; +primitive Cell; + +message Test { + a: Int; + b: Int; + s: Cell as remaining; +}