Skip to content

Commit 7f0e7b5

Browse files
authored
fix: better error message when two require() calls produce the same error code (#3286)
1 parent 18ffa53 commit 7f0e7b5

File tree

3 files changed

+51
-9
lines changed

3 files changed

+51
-9
lines changed

dev-docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- [fix] Compiler now doesn't require explicit re-declaration for abstract methods and fields in traits: PR [#3272](https://github.com/tact-lang/tact/pull/3272)
1313
- Compiler now allows `toCell()` and `toSlice()` methods on contract types: PR [#3274](https://github.com/tact-lang/tact/pull/3274)
1414
- Compiler now generates message loading in place for better performance: PR [#2993](https://github.com/tact-lang/tact/pull/2993)
15+
- Compiler now shows a more informative error message if two `require()` calls have the same generated error code: PR [#3286](https://github.com/tact-lang/tact/pull/3286)
1516

1617
### Docs
1718

src/storage/resolveErrors.spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,45 @@ contract Test {
4949
ctx = resolveStatements(ctx);
5050
expect(() => resolveErrors(ctx, ast)).toThrow(expectedErrors);
5151
});
52+
53+
it("should throw on duplicate error code", () => {
54+
const src = `
55+
trait BaseTrait {}
56+
57+
contract Test {
58+
get fun foo(): Int {
59+
require(false, "113");
60+
return 0;
61+
}
62+
63+
get fun bar(): Int {
64+
require(false, "241");
65+
return 0;
66+
}
67+
}
68+
`;
69+
70+
const expectedErrors = `<unknown>:11:24: Error message "241" maps to error code 38048, which is already assigned to "113", change the message to resolve this conflict
71+
10 | get fun bar(): Int {
72+
> 11 | require(false, "241");
73+
^~~~~
74+
12 | return 0;
75+
`;
76+
77+
const ast = getAstFactory();
78+
const sources: Source[] = [
79+
{ code: stdlib, path: primitivesPath, origin: "stdlib" },
80+
{ code: src, path: "<unknown>", origin: "user" },
81+
];
82+
let ctx = openContext(
83+
new CompilerContext(),
84+
sources,
85+
[],
86+
parseModules(sources, getParser(ast)),
87+
);
88+
ctx = resolveDescriptors(ctx, ast);
89+
ctx = resolveSignatures(ctx, ast);
90+
ctx = resolveStatements(ctx);
91+
expect(() => resolveErrors(ctx, ast)).toThrow(expectedErrors);
92+
});
5293
});

src/types/resolveErrors.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,21 +40,21 @@ function resolveStringsInAST(
4040
) {
4141
traverse(ast, (node) => {
4242
if (node.kind === "static_call" && isRequire(node.function)) {
43-
if (node.args.length !== 2) {
43+
const [, messageArg] = node.args;
44+
if (typeof messageArg === "undefined") {
4445
return;
4546
}
4647
const resolved = ensureString(
47-
evaluateRequireErrorString(node.args[1]!, ctx, util),
48+
evaluateRequireErrorString(messageArg, ctx, util),
4849
).value;
4950
if (!exceptions.get(ctx, resolved)) {
5051
const id = exceptionId(resolved);
51-
if (
52-
Array.from(exceptions.all(ctx).values()).find(
53-
(v) => v.id === id,
54-
)
55-
) {
56-
throwInternalCompilerError(
57-
`Duplicate error id: "${resolved}"`,
52+
const exceptionsArray = [...exceptions.all(ctx).values()];
53+
const prev = exceptionsArray.find((v) => v.id === id);
54+
if (typeof prev !== "undefined") {
55+
throwCompilationError(
56+
`Error message "${resolved}" maps to error code ${id}, which is already assigned to "${prev.value}", change the message to resolve this conflict`,
57+
messageArg.loc,
5858
);
5959
}
6060
ctx = exceptions.set(ctx, resolved, { value: resolved, id });

0 commit comments

Comments
 (0)