Skip to content

feat(justdummies): add JD011-JD013 for recipes reaching value positions - #370

Merged
Reefact merged 1 commit into
mainfrom
claude/justdummies-analyzers-analysis-z9q1xd
Jul 29, 2026
Merged

feat(justdummies): add JD011-JD013 for recipes reaching value positions#370
Reefact merged 1 commit into
mainfrom
claude/justdummies-analyzers-analysis-z9q1xd

Conversation

@Reefact

@Reefact Reefact commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Third wave of the analyzer study, completing the JustDummies.Usage category: the three remaining ways a generator stands in for the value it would draw. JD011 ships opt-in, and that is the measurement ADR-0059's follow-up demanded, not a preference — see below.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring
  • Analyzer / diagnostic change
  • Tests
  • Documentation
  • Build / CI / tooling

Changes

  • JD011 GeneratorWhereValueExpected (Warning, opt-in) — the residue ADR-0020 could not close. Generators are reference types, so an object, dynamic or params object[] position takes one with no conversion; there was nothing to remove there. Assert.NotNull(Any.String()) is green for ever, a theory row built as object[] feeds the recipe to the code under test, and gen.Equals(value) is false for every run and every seed.
  • JD012 GeneratorPooledAsValue (Warning) — Any.OneOf(Any.Int32(), Any.Int32()) infers the builder as the pool's element type, so drawing yields a recipe. A trap rather than an obvious slip because the surface is inconsistent: pooled generators of different types fail inference and the compiler stops you, while two of the same type bind cleanly.
  • JD013 HeldCollectionPassedToOneOf (Warning) — a held collection binds T to the collection itself, making a pool of one whose every draw returns the same list. ADR-0032 recorded this as an accepted risk "mitigated by ElementOf being the documented path"; this turns that mitigation into a check.
  • The analyzer test harness gains the opt-in mechanism FirstClassErrors.Analyzers.UnitTests already had, so a disabled-by-default rule can be forced on for its own tests exactly as an .editorconfig would.
  • 20 new analyzer tests (86 total), 6 documentation pages (EN + FR), both index tables, release-tracking rows.

Why JD011 ships opt-in

ADR-0059's follow-up said: "Decide the remaining object-position rule on dogfooding evidence gathered from this repository's own suites, not before." So I measured before choosing.

Pass Hits Verdict
First 6 all false positives
After the ReferenceEquals exclusion 2 both false positives

The four that disappeared were ReferenceEquals(original, narrowed) in the interval property tests — that is how the suite proves a constraint returned a new recipe rather than mutating the receiver, and Generate() there would destroy the property under test. My Equals branch already excluded generator-vs-generator; the conversion branch had to draw the same line, and now does.

The two that remain are in SizeGuardConventionTests.cs, which collects generators into a List<object> on purpose to enumerate every generator shape. That is the same shape as the object[] theory row the rule exists to catch, so it cannot be narrowed away.

Net: no true positive, two false positives. That is weak evidence for enabling it everywhere and good evidence that it belongs in a consumer's suite, where object-typed assertion helpers are common and reflection over generator instances is not. Hence isEnabledByDefault: false, with the enabling snippet in the help page. Flip it if a consumer suite ever provides better evidence — a unit test pins the current choice so the flip is deliberate.

JD012 and JD013 produced zero hits and ship enabled.

Dogfooding

Run over JustDummies.UnitTests, JustDummies.PropertyTests, JustDummies.Xunit.UnitTests, FirstClassErrors.Testing.UnitTests, FirstClassErrors.UnitTests and FirstClassErrors.RequestBinder.UnitTests. A planted positive control confirmed JD012 and JD013 fire at the expected locations and that JD011 stays silent by default; the control was then removed.

Testing

  • dotnet build FirstClassErrors.sln — 0 warnings, 0 errors
  • dotnet test FirstClassErrors.sln — 2 003 tests, 0 failures across all 13 test projects
  • Analyzer tests pass (FirstClassErrors.Analyzers.UnitTests, 132) — the rules added here live in JustDummies.Analyzers.UnitTests (86)

Mutation testing was not run locally.

Documentation

  • Public API / error documentation updated — no public API change
  • README / doc/ updated — JD011JD013 rule pages and the rule index, with JD011 marked opt-in and carrying its enabling snippet
  • French translation updated — JD011.fr.mdJD013.fr.md and the French entries of README.fr.md
  • No documentation change required

Architecture decisions

  • No architectural decision in this pull request
  • New decision recorded — ADR drafted as Proposed: ADR-____
  • Supersedes an existing ADR — successor proposed, status not flipped: ADR-____
  • ⚠️ Conflicts with an existing ADR — flagged for the maintainer: ADR-____

No new decision: this executes ADR-0059's follow-up action rather than deciding anything fresh. If you consider that follow-up now discharged, its wording is the place to say so — but that is an edit to a Proposed ADR and therefore yours, not mine.

Related issues

None. Third wave of the analyzer-opportunity study; #368 and #369 shipped the first two. Fifteen catalogued rules remain, all in the constraint-decidability family, which needs the shared fluent-chain and constant-folding helpers first.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GKqyhG9Y4AfdxEYekP2Evq


Generated by Claude Code

Completes the JustDummies.Usage category with the three remaining ways a
generator stands in for the value it would draw.

JD011 covers the residue ADR-0020 could not close. Generators are
reference types, so an object, dynamic or params object[] position takes
one with no conversion — there was nothing to remove there. The recipe
then survives as an opaque object: Assert.NotNull(Any.String()) is green
for ever, a theory row built as object[] feeds the recipe to the code
under test, and gen.Equals(value) is false for every run and seed.

JD012 and JD013 are the two Any.OneOf traps. Pooling generators infers
the builder type as the element type, so drawing yields a recipe; and a
held collection binds T to the collection itself, making a pool of one
whose every draw returns the same list. ADR-0032 recorded the second as
an accepted risk "mitigated by ElementOf being the documented path" —
this turns that mitigation into a check.

JD011 ships opt-in (isEnabledByDefault: false), which is a measurement
and not a preference. ADR-0059's follow-up required its default to be
decided on dogfooding evidence; across this repository's suites it found
no true positive and two false ones, both in a convention test that
collects generators into a List<object> on purpose. That shape cannot be
told apart from the theory-row mistake the rule targets, so it cannot be
narrowed away — and the rule earns its keep in a consumer suite instead,
where object-typed assertion helpers are common and reflection over
generators is not.

Two exclusions came out of that same pass and are covered by tests citing
the real sites: a comparison between two generators is never reported,
because ReferenceEquals(original, narrowed) is how the property suite
proves a constraint returned a new recipe, and Generate() there would
destroy the property under test.

The test harness gains the opt-in mechanism FirstClassErrors' harness
already had, so a disabled-by-default rule can be forced on for its own
tests exactly as an .editorconfig would.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GKqyhG9Y4AfdxEYekP2Evq
@Reefact
Reefact merged commit c7a291e into main Jul 29, 2026
28 checks passed
@Reefact
Reefact deleted the claude/justdummies-analyzers-analysis-z9q1xd branch July 29, 2026 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants