Skip to content

Escape keyword-named identifiers in the Rust and C++ generators - #330

Open
matt-edmondson wants to merge 3 commits into
mainfrom
claude/project-thread-kddtpg-106
Open

matt-edmondson wants to merge 3 commits into
mainfrom
claude/project-thread-kddtpg-106

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Requested by Matt · project thread

Before: the Rust and C++ generators wrote every name as given. A property called Type came out as pub fn type(&self) in Rust, and one called Default as int default() const in C++, both of which fail to parse. The same happened to fields, parameters, locals, designated initialisers and member initialisers named where, match, loop, virtual, delete, new and the rest.

After: each generator escapes a name that is a keyword of its target, where the name is declared and wherever it is referred to, so the two spellings agree.

  • Rust writes a raw identifier (r#type, r#match). self, Self, crate and super cannot be raw identifiers, so they get an underscore instead (super_).
  • C++ appends an underscore (default_, virtual_). C++ has no escape for a keyword, so this is a rename, and a caller of the generated header uses the renamed name.

How: LanguageGeneratorBase gains a ReservedWords set, an EscapeIdentifier hook (default: append _ to a reserved word), and EscapeReference, which escapes each dotted segment of a VariableReference while leaving a leading receiver (self, this, …) as written, so self.type becomes self.r#type. The set is empty by default, so the other five generators are unchanged. Rust and C++ supply their keyword lists and route their declaration sites (fields, constants, functions, parameters, locals, member and designated initialisers, field-init shorthand) through the hook.

ReservedWordIdentifierTests adds four tests: two compile and run a struct with keyword-named fields, getters, parameters and locals through rustc --edition 2021 and g++ -std=c++20 -Wall -Wextra -pedantic, and two pin the Rust receiver and path-keyword rules. All four fail without the fix.

Left for follow-ups, so this is part of the issue rather than all of it:

  • The C, Go, Python, JavaScript and C# generators have no reserved-word sets yet; the hook is in place for them.
  • Enum member names and type parameter names are not escaped yet in any target.
  • CallExpression.Callee is verbatim text by design, so a call to a keyword-named getter has to be written already escaped by whoever builds the AST.

Part of #106


Generated by Claude Code

A property called Type or Default, or a field, parameter or local named
after a keyword of the target, came out verbatim and failed to parse.
Rust now writes a raw identifier (r#type), falling back to an underscore
for self, Self, crate and super; C++ appends an underscore. Declarations
and references go through the same hook, so they agree, and a leading
receiver in a reference (self.x, this->x) is left as written.

The base generator's reserved set is empty, so the other targets are
unchanged. Part of #106.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tZnrna9oooMZHDaLyXumk
@sonarqubecloud

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants