What's wrong
No generator checks a spelled identifier against the target language's reserved words. LanguageGeneratorBase and the language generators have no reserved-word set, and names only go through NameStyles.Spell (re-casing). The AST is language-neutral, so a name that is perfectly legal in one target becomes a keyword in another once re-cased. The clearest case is the getter name made up for a computed property, PropertyDeclaration.GetterName (Coder/Ast/PropertyDeclaration.cs ~141), used from StandardLanguageGenerator.cs ~393:
Repro (reproduced with MSTest, output checked with the real compilers)
A struct Box with new PropertyDeclaration("Type", "int") and new PropertyDeclaration("Default", "int"), each with a getter body return 1. Both are ordinary C# property names.
| Target |
Generated |
Compiler |
| Rust |
pub fn type(&self) -> i32 |
rustc: expected identifier, found keyword 'type' |
| C++ |
int default() const |
g++ -std=c++20: expected unqualified-id before 'default' |
| C |
Box_type |
fine, because of the prefix |
The same applies to any other spelled identifier (parameter, local, field, function) that lands on a keyword after casing, e.g. match/impl/fn in Rust, range/func/type in Go, lambda/def in Python, delete/new in C++.
This is independent of #71: ktsu.CaseConverter would still produce type.
Suggested fix / acceptance criteria
- Give each generator a reserved-word set and a single
EscapeIdentifier hook applied wherever a name is spelled:
- Rust: raw identifiers (
r#type). Note that self/Self/crate/super can't be raw, so suffix those.
- C++/C/Go/Python/JS: append
_ (e.g. default_, type_).
- C#:
@default.
- Apply it consistently at declaration and use sites so references still resolve.
- Add compile tests with a property named
Type/Default and a parameter named after a keyword in each target.
What's wrong
No generator checks a spelled identifier against the target language's reserved words.
LanguageGeneratorBaseand the language generators have no reserved-word set, and names only go throughNameStyles.Spell(re-casing). The AST is language-neutral, so a name that is perfectly legal in one target becomes a keyword in another once re-cased. The clearest case is the getter name made up for a computed property,PropertyDeclaration.GetterName(Coder/Ast/PropertyDeclaration.cs~141), used fromStandardLanguageGenerator.cs~393:Repro (reproduced with MSTest, output checked with the real compilers)
A struct
Boxwithnew PropertyDeclaration("Type", "int")andnew PropertyDeclaration("Default", "int"), each with a getter bodyreturn 1. Both are ordinary C# property names.pub fn type(&self) -> i32expected identifier, found keyword 'type'int default() constexpected unqualified-id before 'default'Box_typeThe same applies to any other spelled identifier (parameter, local, field, function) that lands on a keyword after casing, e.g.
match/impl/fnin Rust,range/func/typein Go,lambda/defin Python,delete/newin C++.This is independent of #71:
ktsu.CaseConverterwould still producetype.Suggested fix / acceptance criteria
EscapeIdentifierhook applied wherever a name is spelled:r#type). Note thatself/Self/crate/supercan't be raw, so suffix those._(e.g.default_,type_).@default.Type/Defaultand a parameter named after a keyword in each target.