Conversation
…and CI Add the FirstClassErrors.Analyzers project (netstandard2.0) and its xUnit v3 test project, wired into the solution under the existing src/tests folders. Includes the diagnostic id/category catalog for the agreed 16 rules, a dependency-free in-process analyzer test harness (compiles a snippet against the running runtime + the FirstClassErrors core, runs one analyzer, returns its diagnostics), empty analyzer release-tracking files, and a GitHub Actions workflow that restores/builds/tests the analyzers. The repository has no CI; this workflow is the validation path since the development environment cannot build .NET locally. No diagnostic rules yet — those land one commit per FCExxx. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report ErrorCode.Create("") / whitespace / null literal arguments, which throw
an ArgumentException at runtime, as a build-time error. Only literal arguments
are inspected; non-literal codes are out of scope (reserved for FCE003).
Covered by four tests (empty, whitespace, valid, non-literal) exercised through
the in-process analyzer harness.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report a [DocumentedBy("X")] whose referenced method does not exist on the
containing type. The reference is resolved by name at extraction time, so a
typo is silently skipped and the error goes undocumented.
Introduces two shared helpers used here and by the next wiring rules:
KnownSymbols (resolves FirstClassErrors types by metadata name) and
SymbolFacts (attribute lookup + type-inheritance checks).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report a [DocumentedBy("X")] whose target method exists but cannot serve as a
documentation factory: it must be static, parameterless and return
ErrorDocumentation. A missing target stays FCE006's concern, so this rule is
silent when no method of that name exists.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report a type that declares [DocumentedBy] factories but is missing [ProvidesErrorsFor]. Extraction only scans types carrying [ProvidesErrorsFor], so such documentation is silently ignored. Reported once per type. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
…and CI Add the FirstClassErrors.Analyzers project (netstandard2.0) and its xUnit v3 test project, wired into the solution under the existing src/tests folders. Includes the diagnostic id/category catalog for the agreed 16 rules, a dependency-free in-process analyzer test harness (compiles a snippet against the running runtime + the FirstClassErrors core, runs one analyzer, returns its diagnostics), empty analyzer release-tracking files, and a GitHub Actions workflow that restores/builds/tests the analyzers. The repository has no CI; this workflow is the validation path since the development environment cannot build .NET locally. No diagnostic rules yet — those land one commit per FCExxx. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report ErrorCode.Create("") / whitespace / null literal arguments, which throw
an ArgumentException at runtime, as a build-time error. Only literal arguments
are inspected; non-literal codes are out of scope (reserved for FCE003).
Covered by four tests (empty, whitespace, valid, non-literal) exercised through
the in-process analyzer harness.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report a [DocumentedBy("X")] whose referenced method does not exist on the
containing type. The reference is resolved by name at extraction time, so a
typo is silently skipped and the error goes undocumented.
Introduces two shared helpers used here and by the next wiring rules:
KnownSymbols (resolves FirstClassErrors types by metadata name) and
SymbolFacts (attribute lookup + type-inheritance checks).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report a [DocumentedBy("X")] whose target method exists but cannot serve as a
documentation factory: it must be static, parameterless and return
ErrorDocumentation. A missing target stays FCE006's concern, so this rule is
silent when no method of that name exists.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report a type that declares [DocumentedBy] factories but is missing [ProvidesErrorsFor]. Extraction only scans types carrying [ProvidesErrorsFor], so such documentation is silently ignored. Reported once per type. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report the same literal error code created by more than one ErrorCode.Create("X")
in the compilation, lighting up every participating site. ErrorCode.Create
registers each code in a process-wide set and throws when a code is created
twice; this shifts the failure to build time.
Detection aggregates occurrences across the whole compilation (CompilationStart
-> operation collect -> CompilationEnd report) with ordinal comparison, matching
the runtime registry. Cross-assembly duplicates and non-literal codes remain out
of scope (the latter is FCE003); empty codes are left to FCE002.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report a call to Error.ToException() whose result is discarded as a standalone statement. ToException() only builds the exception; without a throw (or capturing the result) the error is silently lost. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report a non-private static factory in a [ProvidesErrorsFor] type that returns an Error but has no [DocumentedBy]; such an error is left out of the generated catalog. Private methods are treated as helpers and skipped to limit false positives. Adds Error to the shared KnownSymbols resolver. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report factories in the same type whose [DocumentedBy] reference the same documentation method. One documentation method describes one error, so sharing it (title, description, examples) means at least one error is mis-documented. Every sharing factory is flagged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report the terminal WithExamples() call of the documentation DSL when given no example factory. The call is mandatory (it produces ErrorDocumentation) but may be called empty, yielding documentation that shows no realistic message. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Pack FirstClassErrors.Analyzers.dll into analyzers/dotnet/cs of the core NuGet package via TargetsForTfmSpecificContentInPackage, so any project referencing the FirstClassErrors package gets the FCExxx rules automatically. The ProjectReference is ReferenceOutputAssembly=false + PrivateAssets=all: build-order only, no runtime or dependency-graph impact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
… cover it in CI Reference the analyzer from the sample project as OutputItemType=Analyzer so the FCExxx rules run at build/IDE time on real usage code, and add a CI step that builds the sample with the analyzers active. This fails on any Error-severity finding and surfaces warnings in the log, keeping the sample exemplary. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
DuplicateErrorCode is reported from a RegisterCompilationEndAction, so its descriptor must carry WellKnownDiagnosticTags.CompilationEnd. Beyond silencing RS1037, the tag tells the IDE this is a whole-compilation (cross-file) diagnostic surfaced at build / full-solution analysis rather than live per file. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report when more than one documented factory produces the same error code by referencing the same ErrorCode field. Documentation extraction groups by code and keeps a single entry, so the others collapse silently. Complements FCE001, which only sees duplicate ErrorCode.Create literals (a shared field has just one). Aggregates per code field across the compilation (operation-block collect -> compilation-end report) and carries the CompilationEnd tag. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report an example passed to WithExamples(...) that invokes no factory of the type declaring the documentation. Examples exist to expose the documented error's real messages, so each should build that error. Lambda and method-group examples are inspected; unrecognized shapes are left alone to avoid false positives. Extracts the operation-tree walk into a shared OperationFacts helper, reused by FCE011. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report ErrorCode.Create(x) where the argument is not a compile-time constant; such a code is invisible to FCE001 duplicate detection. Opt-in (disabled by default) for teams that want codes to stay literal. Also teaches the test harness to force opt-in rules on for a test run, the way an .editorconfig severity entry would. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report a literal error code that does not follow the UPPER_SNAKE_CASE convention. Convention check, opt-in (disabled by default). Empty codes stay FCE002's concern and non-literal codes FCE003's. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report a literal error code that is one of a small denylist of catch-all words (ERROR, INVALID, FAILED, ...) which carry no diagnostic value. Opt-in (disabled by default). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report WithPublicMessage(short, detailed) where both literal messages are equal. The short message is a public summary and the detailed one an optional public detail, so identical values usually signal a copy-paste. Info, enabled by default. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Report a WithTitle("...") whose literal title is one of a small denylist of
empty phrases (Error, Invalid value, Failure, ...). A good title names the
condition. Opt-in (disabled by default).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
The FCE015 edit replaced the class-closing brace without re-adding it, leaving Descriptors unclosed (CS1513). Restores the brace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
…2001) FCE003/004/005/015 ship with isEnabledByDefault: false. The analyzer release file must record their Severity as "Disabled" (the effective severity of a disabled-by-default rule), not their nominal Info; otherwise RS2001 flags a category/severity mismatch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
…nfig Raise the RS* analyzer-development rule categories to warning for the analyzer project so the command-line build (CI) flags the same issues an IDE does, instead of leaving them for a human to notice. Localization rules are left at defaults. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Raising the analyzer-development (RS*) categories surfaced nothing in the command-line build (the CI toolchain's Microsoft.CodeAnalysis.Analyzers does not carry those rules at build severity), so it did not achieve the goal of mirroring the IDE's checks — and, since editorconfig also applies in the IDE, it would only risk adding noise there. The analyzer already follows the standard RS practices (CompilationEnd tags, release tracking, concurrent execution, help links). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Add doc/analyzers/FCE001..FCE016.md (the helpLinkUri targets): each page gives the rule's category, severity, default state, a noncompliant/compliant example, details/limitations, and how to enable the opt-in rules. Add doc/analyzers/README.md as the grouped index and link it from the main README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
… link Mirror in doc/README.fr.md the analyzer section and reference link added to the English README, keeping the bilingual READMEs in step. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
Follow the repository's bilingual convention for the analyzer reference docs: rename each rule page to FCExxx.en.md and add a French FCExxx.fr.md, with an English (README.md) and French (README.fr.md) index. Point the rules' help links at the .en.md pages and fix the French README links. Keeps EN and FR docs in step. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXVAic46o24c1XAfiKEoE6
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.
Adds a set of Roslyn analyzers that catch, at build time, the mistakes the FirstClassErrors runtime and documentation pipeline would otherwise surface late or silently. They are bundled in the
FirstClassErrorsNuGet package (analyzers/dotnet/cs), so any consumer of the package gets them automatically — no extra install.Rules (16)
Error codes
Documentation wiring
Documentation content
Usage
Several rules catch failures that are otherwise silent: a mistyped
[DocumentedBy], a documented factory in a type missing[ProvidesErrorsFor], or two documented factories that collapse to one entry in the generated catalog.FCE001andFCE011are whole-compilation checks (taggedCompilationEnd).What's included
FirstClassErrors.Analyzers(netstandard2.0) +FirstClassErrors.Analyzers.UnitTests(xUnit v3 — 48 tests), wired into the solution.FirstClassErrorsNuGet package viaTargetsForTfmSpecificContentInPackage.FirstClassErrors.Usagereferences the analyzer asOutputItemType=Analyzerand builds clean (0 warnings / 0 errors)..github/workflows/analyzers.ymlruns restore + build + tests + the dogfood build.doc/analyzers/— one reference page per rule plus an index, linked from both READMEs.Notes for the reviewer
main(doc/analyzers/FCExxx.md) — they resolve once this is merged.Merge remote-tracking branchcommits from local pulls; they do not change the code. Squash and merge yields a clean single commit onmain.🤖 Generated with Claude Code