NodeType was NRefactory's coarse node category, but only three reads remained
here: two checks now expressed as "is not Trivia" and one debug assert for the
pattern category. Remove the enum, the abstract property, every per-node
override, and the generator's emission, preserving the pattern-placeholder case
through an IPatternPlaceholder marker interface the output-visitor assert
checks. Also remove the unused PrimitiveExpression.AdvanceLocation helper.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Delete source-fidelity state the decompiler never reads, since it generates
the AST and never parses: SyntaxTree.FileName/ConditionalSymbols/TopExpression,
Comment.StartsLine/IsDocumentation, the preprocessor line/file fields, and a
computed ComposedType flag. Hoist the duplicated start/end location storage
into the shared Trivia base, and drop the redundant per-instance Location field
on single-token leaf nodes in favor of the base's print-time location.
Generate DoMatch for the last nodes (Comment, SyntaxTree, the using and
namespace declarations, Identifier).
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
Introduce the successor to node.Role for child-slot identity: the generator
emits a CSharpSlotInfo per [Slot], exposed as node.Slot, plus a shared SlotKind
enum for the polymorphic "is this node in an embedded-statement / condition /
base-type slot?" comparisons a per-node identity cannot express. Migrate the
printer and transform position checks from node.Role to node.Slot and
node.Slot.Kind, and read identifier children and role-keyed writes through the
typed properties. Role is still present and is removed later; output is
unchanged.
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
Source locations were virtual, computed by recursing to the first and last
child, whose leftmost and rightmost leaves are token nodes; sequence-point
coordinates likewise came from reconstructed token nodes. Store locations as
fields assigned while printing, and derive sequence-point coordinates from the
surrounding real nodes plus the decompiler's fixed formatting, so neither
depends on token children. The using/foreach await modifier becomes a plain
bool field. Characterization gates lock the emitted locations and PDB
coordinates, which are unchanged.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Member and local modifiers were stored as modifier token children, and a
ComposedType's ref/readonly/nullable/pointer specifiers and an array rank as
token and comma children. The output visitor already derived all of these
from scalar accessors, so move them to plain enum/bool/int fields. This
removes another dependency on token children ahead of deleting the token
nodes; the emitted keyword and specifier sequences are unchanged.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Various improvements regarding primary constructor decompilation, including:
- introduce `HasPrimaryConstructor` property in the AST, as there is a difference between no primary constructor and a parameterless primary constructor
- improved support for inherited records and forwarded ctor calls
- exclude non-public fields and properties in IsPrintedMember
- introduce an option to always make the decompiler emit primary constructors, when possible
* Parenthesize interpolations containing global::
* Improvements:
* Cleaner output
* More unit testing
* More efficient tree search
* Implement revisions
* Update Lambda1 to be invariant
* Visit descendents before deciding whether or not to parenthesize an interpolation expression
* Rename local function
* Remove branch for conditional expressions
* Handle Lambda expressions without a block body
* Check for parenthesized expressions
* `NET60` instead of `!NET40`