Debugger: project hoisted async/generator variables as source locals
- Dominant language
- C#
- Stars
- 154
- Forks
- 4
- Avg merge
- 2h 46m
- Merged PRs (30d)
- 189
Description
Deferred debugger polish from #1306. This issue is independent of the standalone-editor epic.
## Goal
Make user-declared variables hoisted into async/generator/async-generator state-machine fields appear with useful source names and lexical lifetimes in managed debugger Locals/Watch experiences, without exposing compiler spills or changing runtime semantics.
## Current state
SharpTS emits stable state-machine/display-class names, standard state-machine attributes, `StateMachineMethod` mappings, async suspension/resume records, sequence points, and source-named fields. Hoisted variables can be inspected as fields on the generated frame, but full projection into ordinary locals is not guaranteed and is documented as accepted v1 behavior.
## Investigation gate
Before changing metadata or generated names:
- [ ] Run the #1306 manual debugger checklist and record how current hoisted parameters/locals/captures appear in current VS Code C# debugger, Visual Studio, and Rider where available.
- [ ] Produce equivalent small Roslyn C# async, iterator, and async-iterator control programs and compare PE/PDB metadata plus debugger presentation.
- [ ] Determine which portable-PDB custom information and generated-field conventions each debugger actually consumes (for example state-machine hoisted-local scopes), rather than assuming Roslyn metadata is universally interpreted.
- [ ] Record the accepted cross-debugger target and any unavoidable debugger-specific differences.
## Work
- [ ] Track source binding identity, source name, lexical scope, generated state-machine field, and suspension lifetime through async/generator lowering.
- [ ] Emit standards-compatible hoisted-local scope/custom debug information required by the accepted debugger target.
- [ ] If debugger expression evaluators require generated field-name conventions, adopt a stable convention while preserving runtime/reflection correctness and readable fallback presentation.
- [ ] Represent parameters, locals crossing suspension, nested-block/shadowed locals, loop bindings, catch bindings, captures shared with display classes, and async-generator state consistently.
- [ ] Keep spills, awaiters, state/current fields, destructuring temporaries, and other scaffolding hidden/non-user.
- [ ] Preserve kickoff/MoveNext mappings, async stepping records, Just My Code attributes, source sequence points, and final-PE/PDB identity.
- [ ] Emit no additional debug metadata or runtime cost for non-debug builds.
- [ ] Update debugger documentation with the verified presentation and remaining debugger limitations.
## Verification
- [ ] Metadata tests decode and assert every added custom-debug-information blob, ownership handle, local-scope range, and field association; do not merely check that a GUID exists.
- [ ] Tests cover async functions, generators, async generators, multiple awaits/yields, nested blocks, shadowed names, loop/catch bindings, parameters, captures/display classes, destructuring, imports, and transformed `var` declarations.
- [ ] Negative tests prove compiler-generated fields/temporaries are not projected as user locals.
- [ ] Deterministic builds produce stable generated names and byte-equivalent semantic metadata where expected.
- [ ] Final rewritten PE and PDB identities/method row mappings remain valid; debug and non-debug programs both run.
- [ ] Manual debugger matrix confirms, at minimum in the accepted primary debugger, that each source variable appears under its source name only while in scope before and after suspension, can be evaluated, and shadowed variables resolve to the correct value.
- [ ] At least one second managed debugger is checked or its incompatibility is documented with a minimized control comparison.
- [ ] Existing `DebugSymbolsTests`, full unit tests, IL verification, Test262, and TypeScript conformance validation are green.
## Success criteria
The accepted debugger(s) present supported hoisted variables like source locals across suspension points with correct names, values, and lexical visibility; generated scaffolding remains hidden; portable-PDB metadata is structurally decoded by automated tests; and runtime/non-debug output is unchanged.
## Non-goals
- Interpreter debugging or a SharpTS DAP adapter.
- Fabricating locals that have no stable storage/lifetime.
- Making every debugger display identical when its portable-PDB support differs.
Contributor guide
Research direction
Start with the #1306 manual debugger checklist, then compare small Roslyn C# async, iterator, and async-iterator programs across the named debuggers. Use the existing DebugSymbolsTests and the listed full validation suites to define metadata and runtime checks; done means source-named hoisted variables have correct lexical visibility while compiler scaffolding remains hidden and non-debug output is unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, typescript
- Domain
- compilers, devtools, testing-qa
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100