Repository navigation
THRIFT-6333: fix(compiler): Resolve every typedef before any generator runs - #3925
slachiewicz wants to merge 1 commit into
Conversation
Code reviewNo blocking issues found. Checked for bugs and CLAUDE.md compliance. Two suggestions, below the bar for an issue but verified:
thrift/compiler/cpp/src/thrift/main.cc Lines 1050 to 1054 in b5a7d62 thrift/compiler/cpp/src/thrift/main.cc Lines 937 to 959 in b5a7d62 thrift/compiler/cpp/src/thrift/parse/parse.cc Lines 29 to 35 in b5a7d62
🤖 Generated with Claude Code |
b5a7d62 to
35eee1f
Compare
|
Thanks. Both are in |
Code reviewRe-reviewed at Both points from the last round are addressed. With compilers built from this head and from master, One suggestion, below the bar for an issue but verified:
thrift/compiler/cpp/src/thrift/parse/parse.cc Lines 43 to 49 in 35eee1f thrift/compiler/cpp/tests/cpp/t_cpp_parser_typedef_tests.cc Lines 81 to 89 in 35eee1f 🤖 Generated with Claude Code |
…r runs Client: compiler t_typedef::get_type() resolved a forward typedef on first use, from inside whichever generator asked first. An undeclared target printed to stdout and exited, leaving files the earlier generators had written; a typedef that reached itself sent get_true_type() round forever. get_type() now throws the message, and parse() calls the new t_program::resolve_types(), which walks every type the program refers to one typedef or container element at a time and reports an undeclared target or a typedef that refers to itself through failure(), before any generator runs. Do not replace the walk with get_true_type(): that loop has no cycle check. A struct is not followed, so a recursive struct still resolves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
35eee1f to
0cbb256
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
XSD attribute types bypass preflight resolution, and the generator-ordering regression lacks integration coverage.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
Pre-validates typedef resolution before code generation to prevent partial output and cyclic-resolution hangs.
Changes:
- Adds whole-program type resolution.
- Replaces process exit with throwable errors.
- Adds typedef-resolution tests.
| File | Description |
|---|---|
compiler/cpp/tests/cpp/t_cpp_parser_typedef_tests.cc |
Tests valid, undefined, cyclic, and recursive typedefs. |
compiler/cpp/src/thrift/parse/t_typedef.cc |
Throws on undefined typedef targets. |
compiler/cpp/src/thrift/parse/t_program.h |
Declares program-wide resolution. |
compiler/cpp/src/thrift/parse/parse.cc |
Traverses referenced types and detects cycles. |
compiler/cpp/src/thrift/main.cc |
Runs resolution before generators. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| for (const t_field* field : tstruct->get_members()) { | ||
| resolve_type(field->get_type(), path, done); |
| // first use. Resolve every type the program refers to here, so that a | ||
| // target that is never declared, or a typedef that refers to itself, is | ||
| // reported before any generator has written a file. | ||
| program->resolve_types(); |


The compiler resolved a forward typedef the first time a generator asked for it. An undeclared target printed
Type "X" not definedto stdout and exited, leaving whatever the earlier generators had already written (thrift --gen go --gen javaleftGoUnusedProtection__.go). A generator that never asked reported nothing. A typedef that reached itself (typedef T T,typedef list<L> L, ortypedef B Awithtypedef A B) looped forever in whichever generator touched it. https://issues.apache.org/jira/browse/THRIFT-6333parse()now callst_program::resolve_types(), which walks every type the program refers to (typedefs, constants, struct fields, service signatures, through containers) one step at a time. Before any generator runs, it reports[FAILURE:file:line] Type "X" not definedorType "X" refers to itselfon stderr. The line is where the parser stands at the end of the file, because the parse tree keeps no line per declaration.t_typedef::get_type()throws instead of exiting, andgenerate()already catches that exception. Structs are not followed, so a recursive struct still compiles.Behaviour changes, all on inputs that are already broken:
--auditrejects an undefined type it used to ignore;test/audit/thrift_audit_test.plpasses.thrift --gen html -ronlib/go/test/NamespacedTest.thrift, and onLightRail.thriftandStreetcars.thriftinlib/rs/test_recursive/src/transit/light/, whose includes are not found (only a warning), exited 0 and now exit 1 withType "…" not defined.Verified: every
.thriftin the repository with--gen cpp -rand--gen html -r, master against this branch → 480 of 484 runs give the same exit status and output. The three html runs above now fail, and--gen cpponNamespacedTest.thriftexits 1 as before but writes no files (master left 6).Tests:
compiler/cpp/tests/cpp/t_cpp_parser_typedef_tests.cccovers an undeclared target, a typedef declared after its use, the three self-referencing forms and a struct that contains itself through a typedef.t_cpp_parser_typedef_tests.cc); codespell reports nothing on the changed lines.Co-Authored-By:trailer. No new dependency or third-party code.