The C# syntax tree's name accessors (ParameterDeclaration.Name, GotoStatement.Label,
catch variable, query continuations, ...) are convenience strings over a backing Identifier
token. Names that may be absent are now typed string? and read as null when absent, instead of
a non-null empty-string sentinel; required names stay non-null string. The backing token slot's
nullability follows, and an empty or null assignment clears the token (empty == absent).
Because the property type now carries optionality, the separate [NameSlot] attribute and its
nullOnEmpty flag are redundant: a [Slot] on a string property is a name (child slots are
AstNode-typed, so the type disambiguates), and the generator infers optionality from the
declared nullable annotation -- which a string? declaration already requires #nullable for. The
repeated empty-to-null setter body becomes Identifier.CreateIfNotEmpty. To make the annotation
readable, #nullable enable is turned on across the syntax node files (the directive the inferred
optionality depends on), with the attendant local nullability fixups. Consumers that feed a name
into a non-null slot assert it where the variable structurally has a name.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Turn on #nullable enable across the AST consumer layer: the output visitor, the
IL-to-C# builders (statement, call and expression builders, CSharpDecompiler,
TypeSystemAstBuilder), the translation-result wrappers, the sequence-point and
required-namespace collectors, and the annotation helpers. Optional inputs,
fields and returns are typed nullable, detector out-parameters use
[NotNullWhen(true)], and structurally-guaranteed dereferences use the
null-forgiving operator. A few public parameters that already tolerate null are
widened to match their downstream callers. The annotations emit no IL, so the
Pretty suite stays byte-identical.
Assisted-by: Claude:claude-opus-4-8:Claude Code
TokenRole was a printer-side descriptor whose only jobs were holding a token's
text and giving the writers an identity to single out specific tokens. The text
becomes plain const strings on the nodes, and the few identity checks are
reexpressed as node-stack context: interpolation braces are recognized by an
Interpolation on the writer's stack, record class versus struct coloring keys
off TypeDeclaration.ClassType, and the accessor/this/base/override cases fall
out of the surrounding node. WriteKeyword/WriteToken drop the descriptor
parameter. The constants are named for what the token is: a keyword, a symbol
token, or a modifier.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The token writers and the UI syntax highlighter only need a token's identity (to
single out structural braces, the constructor this/base keyword, the override
modifier, and to colour keywords) -- not the AST child-role machinery. Make
TokenRole a standalone printer-side descriptor instead of a Role, turn the
modifier marker into a TokenRole, and change the WriteKeyword/WriteToken
signatures from Role to TokenRole across the writer hierarchy and the highlighter.
This lets the child Role hierarchy be removed without disturbing token output.
The dead OptionalComma/OptionalSemicolon bodies (no comma/semicolon/whitespace
children exist since the token drop) become no-ops, and the few sites that handed
the writer a child role for a keyword now pass none. Output is unchanged across
the decompiler suite, and the UI builds.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Comments and preprocessor directives were positional children interleaved
into the child list, and punctuation, keywords and operators were token-node
children. Add a leading/trailing trivia side-channel for comments and
directives, emit it from the output visitor, and re-home every comment
receiver onto it (including inside-block comments as comment-only empty
statements and undecodable attribute arguments as an ErrorExpression). With
locations and sequence points no longer sourced from token nodes, stop
reconstructing them on the locations path and delete CSharpTokenNode,
CSharpModifierToken and InsertSpecialsDecorator. The AST no longer carries
token children or positional comments; output is byte-identical.
Assisted-by: Claude:claude-opus-4-8:Claude Code