The slot kind is now the canonical typed CSharpSlotInfo<T> in Slots, matched by
object identity (the IL SlotInfo model), instead of a parallel SlotKind enum.
CSharpSlotInfo.Kind points at the canonical shared slot; a Slots constant is its
own kind (null Kind, never read -- only per-node slots are asked for their kind),
so there is no self-reference. Child access (GetChild/GetChildren/SetChild,
GetCollectionByKind, AddChild/InsertChild) and the polymorphic
node.Slot.Kind == Slots.X comparisons key on the canonical reference; the
generated SlotKind enum is removed. No behavior change.
Assisted-by: Claude:claude-opus-4-8:Claude Code
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
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
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
Optional single-child slots return T? with a real null instead of a role
null-object, taking the C# grammar as the oracle for which slots are optional.
The generator emits the property type as T? and matches it with MatchOptional,
and consumers move from .IsNull to is null / ?.. This covers the optional
statement, member, try-catch, creation-initializer and pattern slots and the
optional NameSlot tokens. A few slots the grammar marks required but the
decompiler legitimately leaves empty (the implicit-element-access target, an
implicitly-typed lambda parameter's type) are flipped to nullable as well.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Turn on #nullable enable across the AST transform pipeline, ahead of
annotating the slot properties themselves. TransformContext now exposes the
nullable CurrentMember/CurrentTypeDefinition/CurrentModule contract already
declared by ITypeResolveContext, and the generated pattern-to-node conversion
returns a non-null node so patterns can be used in collection initializers
without warnings. No IL changes.
Assisted-by: Claude:claude-opus-4-8:Claude Code