Skip to content

Add a public-API break guard (Roslyn PublicApiAnalyzers) to the consumer-facing assemblies#530

Draft
ChrisonSimtian wants to merge 2 commits into
mainfrom
build/public-api-analyzer-guard
Draft

Add a public-API break guard (Roslyn PublicApiAnalyzers) to the consumer-facing assemblies#530
ChrisonSimtian wants to merge 2 commits into
mainfrom
build/public-api-analyzer-guard

Conversation

@ChrisonSimtian

Copy link
Copy Markdown
Collaborator

PR B of the #257 follow-up. A build-breaking guard on the public API surface, so a future rename/removal like the SolutionModel → Solution move (#257) fails CI instead of shipping silently. Non-breaking / additive (dev-infra only).

What it does

  • Wires Microsoft.CodeAnalysis.PublicApiAnalyzers into 11 consumer-facing assemblies via an opt-in list (_FalloutPublicApiAssemblies) in Directory.Build.props. Each records its surface in committed PublicAPI.Shipped.txt (Unshipped.txt holds future additions).
  • RS0016 (public surface not recorded) → error via .editorconfig.
  • RS0017 (a recorded entry removed/renamed) → error via WarningsAsErrors — it anchors to the API text file, where per-file editorconfig severity can't reach it.
  • Verified both directions break the build, and that the baseline round-trips to a clean build.

Deliberate scoping

  • Generated tool wrappers (*.Generated.cs) excluded. ~60k of the RS0016 hits were generated tool surface; it's regenerated from the *.json specs and already contract-guarded by the SourceGenerators Verify snapshots. Excluded via .editorconfig; RS0016 is kept out of WarningsAsErrors so a command-line /warnaserror can't outrank that per-file suppression.
  • Advisory rules off (RS0026/27/36/37) — opinionated API-design/nullability lint, pure noise on a mature surface.
  • Phased rollout. This PR covers Fallout.Common (incl. the Fallout.Solutions.SolutionAttribute that feat(persistence)!: inline vs-solutionpersistence; rename SolutionModel → Solution; namespace cleanup #257 moved) + Core, Components, ProjectModel, Solution, Build.Shared and the small Utilities.* assemblies. Mid-size Fallout.Build, Fallout.Tooling, Fallout.Utilities follow in a fast follow-up.

Baselines

~2,565 API lines across 11 assemblies (Common 2,239; Components 145; Solution 46; Net 39; Core 28; others small). Generated by recording each assembly's RS0016 signatures against empty API files.

Test plan

  • dotnet build fallout.slnx green (0 errors)
  • All 11 tracked assemblies round-trip clean at error severity
  • Rename → RS0016 errors (build fails); removal → RS0017 error (build fails)
  • Generated wrappers confirmed excluded (Common clean despite ~60k generated public symbols)

ChrisonSimtian and others added 2 commits July 21, 2026 17:20
Adds Microsoft.CodeAnalysis.PublicApiAnalyzers and an opt-in mechanism in
Directory.Build.props (_FalloutPublicApiAssemblies): each listed assembly gets the
analyzer plus its PublicAPI.Shipped/Unshipped.txt as AdditionalFiles.

Guard severities:
- RS0016 (public surface not recorded in the API files) -> error via .editorconfig,
  which also lets [*.Generated.cs] switch it off for the generated tool wrappers.
- RS0017 (a recorded entry removed/renamed) -> error via WarningsAsErrors, because it
  anchors to the PublicAPI.*.txt additional file where per-file editorconfig severity
  can't reach it. RS0016 is deliberately kept OUT of WarningsAsErrors: a command-line
  /warnaserror would outrank the per-file suppression and re-promote the whole
  generated surface.
- Advisory rules (RS0026/RS0027/RS0036/RS0037) are off.

Generated tool wrappers are excluded because that surface is regenerated from the
*.json specs and is already contract-guarded by the SourceGenerators Verify
snapshots. This is the guard that would have failed CI on the #257
SolutionModel -> Solution rename. Phased rollout: Fallout.Build, Fallout.Tooling and
Fallout.Utilities follow in a fast follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records the current shipped public API of the 11 opted-in consumer-facing
assemblies (hand-written surface only; generated tool wrappers excluded).
Empty Unshipped.txt files hold future additions until the next release rolls
them into Shipped.txt. Generated by building each assembly against empty API
files and recording the reported RS0016 signatures; all 11 round-trip to a
clean build, and both guard directions (RS0016 on add/rename, RS0017 on
remove) were verified to break the build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ChrisonSimtian ChrisonSimtian added enhancement New feature or request target/vCurrent Targets the current version labels Jul 21, 2026
@ChrisonSimtian

Copy link
Copy Markdown
Collaborator Author

@dennisdoomen thats what it would look like

@ITaluone

Copy link
Copy Markdown
Collaborator

What about using PublicApiGenerator + Verify? Like the ApprovalTests in FA?

@dennisdoomen

Copy link
Copy Markdown
Collaborator

thats what it would look like

Required more plumbing than I expected. But looks neat.

What about using PublicApiGenerator + Verify? Like the ApprovalTests in FA?

Me and @ChrisonSimtian discussed this. We want to see how the Roslyn analyzer handles this.

@ChrisonSimtian

Copy link
Copy Markdown
Collaborator Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request target/vCurrent Targets the current version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants