Completes the nullable migration for ICSharpCode.Decompiler/CSharp/Syntax: every
hand-written file now carries #nullable enable. The pattern-matching API
(INode/Pattern/PatternExtensions DoMatch/Match/IsMatch) takes a nullable 'other',
since matching legitimately compares a pattern against a missing child. Members
that genuinely return or accept null are annotated to match (AttributeSection's
target token, IAnnotatable.Annotation<T>, Statement/Expression.ReplaceWith,
SyntaxExtensions.GetNextStatement), and a latent null dereference in
Backreference.DoMatch is fixed.
Assisted-by: Claude:claude-opus-4-8:Claude Code
A blank line as the first line inside a node class body (right after the opening brace) or the
last line before the closing brace, left over from the migration. Cosmetic only -- deletions of blank
lines at class boundaries; method bodies are untouched.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Nodes use nullable reference types now, so the generated null-object
node and its hasNullNode / NeedsNullNode / NullNodeBaseCtorParamCount
plumbing are dead. Drop them and reword the affected comments.
The [Slot] and [NameSlot] string argument only feeds SlotKind derivation, which
takes the last dotted segment and strips a trailing "Role", so the Roles.
qualifier and the Role suffix were dead decoration. Rewrite each to the bare
SlotKind it already produces, so the attribute reads as exactly the kind.
Collapse the runs of consecutive blank lines that removing the hand-written
constructors, fields and properties left between the remaining members.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Each concrete node's constructors are now emitted by the source generator
from its members in source order: single and collection [Slot] children,
the [NameSlot] string, and settable enum scalars (Operator, FieldDirection,
...). It emits the empty ctor (for object initializers), a required-prefix
ctor, one ending at each collection, and the full ctor, with later ctors
forwarding to shorter ones via this(...) and a params[] overload alongside
each IEnumerable<T> one. The hand-written ctors the generator now produces
are removed; scalar/location/Identifier convenience overloads that it cannot
express are kept (e.g. AssignmentExpression(left, right), SimpleType(Identifier),
the string+TextLocation overloads).
Because ctor parameters follow source order, BinaryOperatorExpression and
AssignmentExpression declare Operator between Left and Right so the generated
ctor is the expected (left, op, right). Pure-scalar nodes whose state is not
in slots (e.g. PrimitiveExpression's literal value) are left untouched.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Slot identity already lived in node.Slot/CSharpSlotInfo/SlotKind after
the storage flip, so the parallel Role/Role<T> child model was redundant.
The Role-keyed mutation/query API is reexpressed over SlotKind, and the
role-index packing on AstNode flags is gone.
Because a node's Slot is now derived from its index in its parent rather
than stored on the child, the located-AST reattach in
InsertMissingTokensDecorator must capture the child's slot kind before
Remove() detaches it.
Assisted-by: Claude:claude-opus-4-8:Claude Code
With every optional slot nullable, the null-object pattern is dead. Generated
non-nullable getters return the backing field directly, which surfaced a last
tier of slots the decompiler legitimately leaves empty (omitted range operands,
an implicitly-typed array creation, unnamed parameters, an unbound generic
argument, and others) and flips them to nullable too. The machinery is then
removed entirely: the per-node null classes, the .Null statics and
VisitNullNode, AstNode.IsNull, the role null object, and Identifier.Null.
AcceptVisitor becomes unconditionally generated, and consumers move from
.IsNull to is null and from unconditional visits to ?.AcceptVisitor.
Assisted-by: Claude:claude-opus-4-8:Claude Code
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
Generate DoMatch across the remaining expression, statement, type-member,
type-reference and general-scope nodes, including the inherited
EntityDeclaration name/return-type/attribute match, and stop generating it for
abstract base nodes. Matching every structural member fixes real
under-matching bugs (PointerReferenceExpression ignored its Target;
ExtensionDeclaration matched anything) while computed or derived members are
excluded from matching. PrimitiveExpression and PreProcessorDirective stay
hand-written by design. Pretty output is byte-identical.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The grammar-production doc comments were transcribed in ANTLR style and
often copied the C# spec verbatim, including sub-rules the AST does not
model. Rewrite them as W3C EBNF (::=, ?, *, +), unroll sub-productions
that are not themselves AST nodes (e.g. anonymous_function_modifier ->
'async'), and shape each production to the node's actual members. Render
top-level alternations of node productions as multi-line <code> blocks;
keep operator and keyword token lists inline.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Each concrete syntax node now carries, in an XML-doc <remarks> block, the
matching production from the C# language specification grammar (ECMA/Microsoft,
ANTLR notation), quoted verbatim. Aggregate nodes (e.g. BinaryOperatorExpression,
ComposedType, TypeDeclaration) list every production they span; lexical/trivia
nodes (Comment, the preprocessor directives, Identifier) cite the lexical rule.
Nodes with no spec production -- ErrorExpression, UndocumentedExpression,
InvocationAstType, TypeReferenceExpression, NamedExpression, DocumentationReference,
and the C# 14 ExtensionDeclaration -- carry a hand-written EBNF plus a note
explaining why no official production exists.
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
Each child of a C# AST node is declared as a [Slot] partial property, and the
source generator emits the accessor bodies and an ordered slot schema
(SlotCount/GetSlotRole/IsCollectionSlot) from them. Generating the schema
keeps slot order from being mis-stated by hand and lets a DEBUG invariant
check declared slot order against document order on every decompile. The node
hierarchy is converted family by family; the EntityDeclaration leaves flatten
their inherited Attributes/ReturnType/NameToken into each leaf's ordered slot
set. Storage stays the NRefactory linked list at this stage, so only the
declaration model changes and output is unchanged.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The generator emits the IAstVisitor interface, the AcceptVisitor overloads,
and the null-node and pattern-placeholder nodes from [DecompilerAstNode]
declarations, so drop the hand-written equivalents across the C# AST: per-node
AcceptVisitor/DoMatch, the #region Null / #region PatternPlaceholder blocks,
IAstVisitor.cs, and now-dead usings. Also adds AccessorKind and moves
IdentifierExpressionBackreference into the PatternMatching folder.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Introduce a Roslyn source generator that emits the visitor boilerplate for
the C# AST from [DecompilerAstNode]-tagged node declarations: the
IAstVisitor interface, the AcceptVisitor overloads, the pattern-placeholder
nodes, and the initial DoMatch support. AccessorKind lets an accessor's
keyword be chosen independently of its role, an early step toward shedding
the NRefactory role model.