ReduceNesting walks an else-if chain to its innermost if and asks
ShouldReduceNesting whether to extract the else block, which ExtractElseBlock
does by casting the block to Block. A chain with no trailing else reaches this
with a bare Nop, yet the heuristic still approved it (its stats count a Nop as
one statement), so the cast threw InvalidCastException. Take a Block in
ShouldReduceNesting and skip the reduction at the call site when the else is
absent.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The C# debug-steps view highlights and centers the exact AST node a
transform changed; the ILAst view already had the step tree and
replay-at-step but produced no highlight. Bring it to parity.
IL rendering has no token-writer seam like the C# output visitor, so
per-instruction text spans are recorded by bracketing
ILInstruction.WriteTo via a new INodeTrackingOutput. The dominant
inst.ReplaceWith(newInst) transform pattern detaches the instruction
passed to Step, so ILTransformContext gains EndStep to record the
produced instruction; Stepper additionally records the position's
ancestor chain as fallback candidates before the step-limit throw, so
the "show state before" view -- which halts at the selected step --
still resolves to a surviving ancestor (ultimately the ILFunction).
The highlight-range resolver is shared with the C# language.
Assisted-by: Claude:claude-opus-4-8:Claude Code
ILFunctions can appear as statements when the source code has a lambda expression which is immediately discarded.
```
_ = (Action)delegate { ... };
```
This allows reverting the changes to HighLevelLoopTransform+ReduceNestingTransform from the previous commit, which fixes a bug in loop detection (the previous commit did not handle loops where the loop BlockContainer didn't have a Block as parent).