Skip to content

Report a property whose type is a contract the document cannot declare - #2530

Merged
woksin merged 2 commits into
mainfrom
fix/screenplay-report-undeclarable-contracts
Aug 12, 2026
Merged

Report a property whose type is a contract the document cannot declare#2530
woksin merged 2 commits into
mainfrom
fix/screenplay-report-undeclarable-contracts

Conversation

@woksin

@woksin woksin commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Fixed

woksin and others added 2 commits August 12, 2026 10:57
A type reference is written as a single identifier, and what is reported is
a type the document then refers to without ever declaring it. That was
recognised only where the name itself lost something - a constructed generic
or a type parameter - so an interface or an abstract class, whose name
survives intact, matched neither branch and was emitted with no diagnostic
at all.

The declaration is what cannot be written for those: a type says what a value
holds, and a contract leaves that to whatever implements it. Only a record is
declared, so the property names something the document never introduces, which
is the same dangling reference and is now said the same way. An abstract record
is excluded - it is declared like any other record.

What the document contains is unchanged; this only stops the silence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Covers an interface and an abstract class being reported, and an abstract
record not being - the line between a type no declaration is written for and
a type something can merely derive from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@woksin woksin added the patch label Aug 12, 2026
@woksin

woksin commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Reviewer context. This is part 1 only of #2527 — what an unnameable property should emit is a Screenplay language question and is deliberately untouched.

The change. ReportWhatTheNameLoses recognised a type only when the name lost something: TypeArguments.Length > 0, or TypeKind.TypeParameter. An interface or an abstract class loses nothing in the writing — ICaptureSource is written as ICaptureSource — so it matched neither branch and was emitted with no diagnostic. What it cannot have is a declaration: a type says what a value holds, and a contract leaves that to whatever implements it. Both roads end at the same place, a name the document never introduces, which is what SP0030 is for.

The boundary, and why there is a spec for it. Being abstract is not on its own the trigger. CarriedTypes.IsRecord admits abstract records, so an abstract record is registered as a shape and is declared — reporting it would claim a dangling reference that actually resolves. The condition excludes records for exactly that reason, and a_property_carrying_an_abstract_record pins it.

Verified against Studio. Generated from Source/Core/Core.csproj of a git archive origin/main export (2026-08-12), through Screenplay.EndToEnd:

before after
SP0030 7 11
every other code (SP0007, SP0011, SP0012, SP0013, SP0015, SP0016, SP0019, SP0020, SP0025, SP0028, SP0031, SP0032, SP0036, SP0038, SP0039, SP0040, SP0041, SP0043) unchanged unchanged
Unknown type document warnings 27 27

The four new ones are IRule, UIElement, ICaptureSource and ICaptureOutput — precisely the 10 previously silent references (UIElement ×5, ICaptureSource ×2, ICaptureOutput ×2, IRule ×1). SP0030 counts distinct types rather than references, which is why 10 references become 4 diagnostics on top of the existing 7.

The 27 document warnings do not move, and that is the point. The two generated documents are byte-identical — diffed, not assumed. Part 1 changes what is reported and nothing about what is written, so the invalid document stays exactly as invalid until part 2 is decided.

Why patch rather than minor. No public API is added — the diagnostic code already existed and only its XML remarks changed; generated output is byte-identical; one existing code now fires on more of the cases it was defined for. The argument for minor is that a consumer treating Screenplay warnings as errors sees new warnings, but each names a defect that was already in their document and already in the compiler's output as an Unknown type warning. Happy to flip the label if you read new-warning surface as minor.

Verification. Screenplay builds clean Debug and Release with zero warnings; Screenplay.Specs 1,251 passing, 0 failing (1,240 before, 11 new). The three new specs were run against the unfixed source: the six facts asserting a report all fail, and the abstract-record guard passes both ways — as it should, since it asserts nothing changed for it.

@woksin
woksin merged commit 506ee40 into main Aug 12, 2026
54 of 55 checks passed
@woksin
woksin deleted the fix/screenplay-report-undeclarable-contracts branch August 12, 2026 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant