Add test coverage for untested corners of implemented language features#3859
Merged
Conversation
The Documentation namespace (XmlDocumentationProvider, IdStringProvider, XmlDocLoader) had no test coverage at all, even though XmlDoc is listed as an implemented C# 1 feature on the wiki Language-Feature-Roadmap. These tests pin ID-string generation and FindEntity round-trips for the intricate encodings (nested generic types, `0/``0 type-parameter references, ref/out @ suffixes, pointer and jagged/multi-dimensional array forms incl. [0:,0:], conversion operators with the ~ReturnType suffix, indexers, #ctor) plus XmlDocumentationProvider lookup by ID string and by entity against a generated doc file. All green. Assisted-by: Claude:claude-fable-5:Claude Code
The UnsafeCode fixture had no double-indirection coverage at all: no pointer-to-pointer loads, stores, or indexing, and no fixed statement over an element of a pointer array. Assisted-by: Claude:claude-fable-5:Claude Code
Pins the reconstruction of a continue statement in a catch block inside a foreach loop, which used to decompile to a goto targeting a label at the end of the loop body. #2829 reported the goto form and can be closed. Assisted-by: Claude:claude-fable-5:Claude Code
All existing filter tests only read state; a filter that mutates a ref local observed by the handler pins the ordering between the filter expression and the handler body. Assisted-by: Claude:claude-fable-5:Claude Code
Generic co-/contravariance had no test coverage at all: no out/in variance modifier on any interface or delegate declaration and no variant reference conversion appeared anywhere in the test suite. The new fixture pins declarations (including a constrained covariant interface and an explicit implementation of a variant interface) and conversion shapes that must not produce explicit casts. Assisted-by: Claude:claude-fable-5:Claude Code
All iterator producers in the fixture used concrete element types; a generic method pins the reconstruction of the generic state machine type. Assisted-by: Claude:claude-fable-5:Claude Code
Overrides whose signatures use a substituted type parameter from a generic base class were not covered. Assisted-by: Claude:claude-fable-5:Claude Code
The lifted-operator matrix only used pure expressions; compound assignment on nullable locals and boxing/unboxing conversions of Nullable<T> were not covered. Assisted-by: Claude:claude-fable-5:Claude Code
Anonymous types nested as members of other anonymous types (and read back through the projection), ToString on an anonymous instance, and explicit member projections were not covered. Assisted-by: Claude:claude-fable-5:Claude Code
Virtual/override/sealed-override auto-properties, implicit and explicit interface implementations, asymmetric accessor accessibility, and auto-properties in structs were not covered. Assisted-by: Claude:claude-fable-5:Claude Code
Only group-by continuations were covered; a continuation introduced by select-into exercises a different transparent-identifier reset. Assisted-by: Claude:claude-fable-5:Claude Code
AddChecked/MultiplyChecked/ConvertChecked nodes were not covered; they are pretty-printed as a checked block around the tree-building calls. Assisted-by: Claude:claude-fable-5:Claude Code
dynamic used as a while-loop condition and the null-conditional invocation operator on a dynamic receiver were not covered. Assisted-by: Claude:claude-fable-5:Claude Code
427f5d2 to
3ca22f6
Compare
await inside a null-coalescing expression, as a do-while condition, on a ValueTask, and inside an interpolated-string hole (the DefaultInterpolatedStringHandler lowering) were not covered. Assisted-by: Claude:claude-fable-5:Claude Code
The fixture had an iterator local function but no async local function; the async state-machine-in-local-function shape, static and capturing, was not covered. Assisted-by: Claude:claude-fable-5:Claude Code
protected and protected internal default interface members, static properties and field-like events in interfaces, and a generic interface with a constrained generic default method were not covered. Assisted-by: Claude:claude-fable-5:Claude Code
MaybeNullWhen, NotNull, and return-targeted MaybeNull had no coverage anywhere in the suite. Assisted-by: Claude:claude-fable-5:Claude Code
Only interface-based disposal was covered; a using declaration over a ref struct with a pattern-based Dispose exercises the recognition heuristic without IDisposable. Assisted-by: Claude:claude-fable-5:Claude Code
ConfigureAwait(false) inside an async iterator and await foreach over ConfiguredCancelableAsyncEnumerable were not covered. Assisted-by: Claude:claude-fable-5:Claude Code
The C# 6 index-initializer syntax was only covered through custom indexers; a real Dictionary<,> initialized with [key] = value pins the choice between the index form and the Add form. Assisted-by: Claude:claude-fable-5:Claude Code
Only two plain ASCII u8 literals were covered; the empty literal and a literal made of escape sequences pin the recovery heuristic boundaries. Assisted-by: Claude:claude-fable-5:Claude Code
readonly record struct had no coverage, and no record overrode a synthesized member; user-declared ToString and PrintMembers pin the synthesized-vs-user member detection. Assisted-by: Claude:claude-fable-5:Claude Code
SetsRequiredMembers had no coverage anywhere; required members across a base/derived pair and in a generic abstract host were also untested. Assisted-by: Claude:claude-fable-5:Claude Code
Generic attributes were only closed over simple types; constructed generic, array, and enum type arguments exercise separate paths in the custom-attribute blob decoder. Assisted-by: Claude:claude-fable-5:Claude Code
Compound assignments selecting between op_Addition and op_CheckedAddition across a checked block boundary were not covered. Assisted-by: Claude:claude-fable-5:Claude Code
Static abstract interface members had no checked operator or unsigned-right-shift coverage, and static abstract property getters were never read as rvalues through the constraint. Assisted-by: Claude:claude-fable-5:Claude Code
UnscopedRef only appeared on a params parameter anywhere in the suite; a ref-returning method and property on a ref struct pin the attribute round-trip. Assisted-by: Claude:claude-fable-5:Claude Code
3ca22f6 to
84977e0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was written by an AI agent (Claude Code), driven and reviewed by @siegfriedpammer.
An audit of the language features marked as implemented in #829 and on the wiki Language-Feature-Roadmap, compared against the existing Pretty fixtures, found a number of constructs that decompile correctly today but had no test pinning them. This PR adds green tests only, one commit per feature (27 commits, 26 files, +791 lines, no production code changes).
Highlights:
out T/in Tdeclaration or variance conversion anywhere in the suite. NewVariancefixture + runner method.ICSharpCode.Decompiler/Documentation/) had zero tests: new unit tests pinIdStringProviderround-trips (nested generics,`0/0`` references,@/pointer/array forms incl. `[0:,0:]`, conversion-operator `~ReturnType` suffixes, indexers, `#ctor`) and `XmlDocumentationProvider` lookup.[SetsRequiredMembers]and required-member inheritance,[UnscopedRef]members, static abstractchecked/>>>operators, generic attributes with constructed/array/enum type arguments, nullable postcondition attributes (MaybeNullWhenetc.),ConfigureAwait(false)in async iterators,awaitin an interpolated-string hole, pattern-basedDisposeof a ref struct, protected/static/generic default interface members, readonly record structs and records with user-declaredToString/PrintMembers, async local functions, checked compound assignment, lifted compound assignment andNullable<T>boxing, pointer-to-pointer shapes, dictionary index initializers, UTF-8 literal edge cases, select-into continuations, nested anonymous-type members, generic iterators, exception filters with side effects, andcontinueinside a catch within a loop (pins the fixed behavior from Continue statement unrecognized in try-catch #2829, which can be closed).All added tests pass on the Linux configuration subset (Roslyn 3.11 / 4.14 / latest, plus/minus Optimize and net40 where the fixture's group includes it): 303/303 including the pre-existing cases of the touched fixtures. Legacy csc/mcs configurations run on Windows CI only.
🤖 Generated with Claude Code