Skip to content

THRIFT-6333: Report an undefined typedef target before any generator runs - #3925

Open
slachiewicz wants to merge 1 commit into
apache:masterfrom
slachiewicz:typedef-resolve
Open

slachiewicz wants to merge 1 commit into
apache:masterfrom
slachiewicz:typedef-resolve

Conversation

@slachiewicz

Copy link
Copy Markdown
Member

t_typedef::get_type() resolved a forward typedef on first use, from inside whichever generator asked first, and on a target that was never declared it printed Type "X" not defined to stdout and called exit(1): files the earlier generators had written stayed on disk (thrift --gen go --gen java left GoUnusedProtection__.go), the message carried no file or line, and a generator that never asked did not report it at all. https://issues.apache.org/jira/browse/THRIFT-6333

get_type() now throws the message, and parse() resolves every type the program refers to — typedef chains, struct fields, constants, service signatures, through containers — before returning, so the error reaches failure() as [FAILURE:file:line] Type "X" not defined on stderr before any generator runs. generate() already catches the same exception for anything a generator asks for later. The line is the one the parser is at when the pass ends (the file's last line plus one), since the parse tree keeps no line per declaration; the file is the useful part.

Behaviour changes: none for an input the compiler accepts (over every .thrift in the repository with --gen go --gen java --gen json -r: 159 files byte-identical, 35 rejected by both with the same status). --audit now rejects an undefined type it used to ignore; test/audit/thrift_audit_test.pl still passes all 46 cases.

Tests: compiler/cpp/tests/cpp/t_cpp_parser_typedef_tests.cc checks that an undefined target throws the message and that a typedef declared after its use resolves.

  • JIRA ticket: THRIFT-6333
  • Title follows THRIFT-NNNN: pattern
  • Single commit
  • No breaking change

…runs

Client: compiler

t_typedef::get_type() resolved a forward typedef on first use, from
inside whichever generator asked first, and on a target that was never
declared it printed to stdout and exited: files the earlier generators
had written stayed on disk, the message carried no file or line, and a
generator that never asked did not report it at all.

get_type() now throws the message, and parse() resolves every type the
program refers to (typedef chains, struct fields, constants, service
signatures, through containers) before returning, so the error reaches
failure() with the file and line before any generator runs. generate()
already catches the same exception for anything a generator asks for
later. No output changes for an input the compiler accepts; the audit
mode, which never resolved typedefs, now rejects an undefined type it
used to ignore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Jens-G

Jens-G commented Sep 21, 2026

Copy link
Copy Markdown
Member

Code review

No blocking issues found. Checked for bugs and CLAUDE.md compliance.

Two suggestions, below the bar for an issue but verified:

  • The new pass in parse() does not finish when a typedef refers back to itself, and the compiler then hangs at 100% CPU without a message:

    • typedef list<L> L loops in the container branch of resolve_forward_types().
    • typedef T T, or typedef B A together with typedef A B, loops in get_true_type(), which the pass now calls on every declared typedef, even one that nothing uses.

    The base compiler exits 0 on each of these three files with --gen cpp (also java, py, html and netstd). With this PR it hangs for every generator and for --audit. When a struct field uses such a typedef, the base compiler already crashed or hung, except with --gen html.

    A fix that I tried: follow typedefs and container elements one step at a time, and keep a set of the types on the current path. Each of the three files then fails at once with Type "<name>" refers to itself, and on all 240 .thrift files in the repository, --gen cpp and --gen html give the same exit status and the same output as this PR. A set in the container branch alone is not enough, because typedef T T never leaves get_true_type().

// written a file.
resolve_forward_types(program);
} catch (string &x) {
failure(x.c_str());
}

*/
static void resolve_forward_types(t_type* type) {
type = type->get_true_type();
if (type->is_list()) {
resolve_forward_types(((t_list*)type)->get_elem_type());
} else if (type->is_set()) {
resolve_forward_types(((t_set*)type)->get_elem_type());
} else if (type->is_map()) {
resolve_forward_types(((t_map*)type)->get_key_type());
resolve_forward_types(((t_map*)type)->get_val_type());
}
}
static void resolve_forward_types(t_struct* tstruct) {
for (t_field* field : tstruct->get_members()) {
resolve_forward_types(field->get_type());
}
}
static void resolve_forward_types(t_program* program) {
for (t_typedef* td : program->get_typedefs()) {
resolve_forward_types(td);
}

const t_type* t_type::get_true_type() const {
const t_type* type = this;
while (type->is_typedef()) {
type = ((t_typedef*)type)->get_type();
}
return type;
}

  • The new rejections are right, and the description already names the --audit case. Generators that never resolved every type change the same way: thrift --gen html -r lib/go/test/NamespacedTest.thrift, where the include ThriftTest.thrift is not found (only a warning), exits 0 on base and writes 4 files; with this PR it exits 1 with Type "ThriftTest.UserId" not defined. LightRail.thrift and Streetcars.thrift in lib/rs/test_recursive/src/transit/light/ behave the same. The sweep in the description (--gen go --gen java --gen json) could not show this, because those generators already failed on base, and no build target compiles these files this way. It could go next to the --audit note.

🤖 Generated with Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants