Platform specific docs analyzer#128411
Conversation
…onventions Introduces a Roslyn analyzer (PLATDOC001-003) that enforces documentation placement conventions for platform-specific libraries with UseCompilerGeneratedDocXmlFile=true: - PLATDOC001: Public types must have a source file named TypeName.cs - PLATDOC002: Partial source files must follow TypeName.Something.cs convention - PLATDOC003: Public members in non-primary partial files must not have XML documentation comments (docs should be in TypeName.cs) The analyzer only activates when the TargetFramework has a platform suffix (e.g. -windows, -linux) and UseCompilerGeneratedDocXmlFile is true. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Only check types declared across multiple files (partial types split into separate files). Single-file types don't have doc placement issues. - Exclude nested type declarations from PLATDOC003 since they define their own documentation scope. - Suppress PLATDOC002 on System.Private.Xml Async partial files that use the established TypeNameAsync.cs naming convention. - Suppress PLATDOC003 on XmlResolver.FileSystemResolver.cs and XmlResolver.ThrowingResolver.cs which contain non-platform-specific public properties organized by convention. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a section to adding-api-guidelines.md explaining why documentation must be placed on the primary source file (TypeName.cs) for platform- specific libraries, and how the PlatformDocAnalyzer enforces this. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ilds When a library targets both a platform-agnostic TFM (e.g. net11.0) and platform-specific TFMs (e.g. net11.0-windows), the build now passes the canonical TFM's compiler-generated doc XML to the PlatformDocAnalyzer. The analyzer compares each public API's documentation against the canonical and reports PLATDOC004 when they differ. This directly validates the goal (consistent docs across platforms) rather than relying solely on file-naming heuristics. The canonical doc XML is located at its expected artifact path rather than via ProjectReference, avoiding circular dependencies with PNSE builds and the overhead of GetTargetFrameworks calls. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Roslyn analyzer (eng/analyzers/PlatformDocAnalyzer) and MSBuild wiring intended to prevent XML doc comments from drifting or going missing across platform-specific TFMs when compiler-generated doc XML is used.
Changes:
- Add
PlatformDocAnalyzerwith diagnostics PLATDOC001–PLATDOC004 to enforce partial-file doc placement conventions and (optionally) compare docs against a canonical doc XML. - Integrate canonical doc XML discovery for platform-specific TFMs via
eng/intellisense.targetsand wire the analyzer into source project builds viaeng/generators.targets. - Update documentation guidance and add suppressions in a handful of
System.Private.Xmlfiles.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/System.Private.Xml/src/System/Xml/XmlResolver.ThrowingResolver.cs | Adds PLATDOC003 suppression. |
| src/libraries/System.Private.Xml/src/System/Xml/XmlResolver.FileSystemResolver.cs | Adds PLATDOC003 suppression. |
| src/libraries/System.Private.Xml/src/System/Xml/Resolvers/XmlPreloadedResolverAsync.cs | Adds PLATDOC002 suppression. |
| src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs | Adds PLATDOC002 suppression. |
| src/libraries/System.Private.Xml/src/System/Xml/Core/XmlReaderAsync.cs | Adds PLATDOC002 suppression. |
| eng/intellisense.targets | Adds target to pass canonical doc XML as an AdditionalFiles input when available. |
| eng/generators.targets | Adds analyzer project reference and imports analyzer props for source projects. |
| eng/analyzers/PlatformDocAnalyzer/PlatformDocAnalyzer.props | Declares compiler-visible MSBuild properties/metadata for analyzer context. |
| eng/analyzers/PlatformDocAnalyzer/PlatformDocAnalyzer.csproj | Defines the analyzer project and Roslyn dependency. |
| eng/analyzers/PlatformDocAnalyzer/PlatformDocAnalyzer.cs | Implements diagnostics and canonical doc comparison logic. |
| eng/analyzers/PlatformDocAnalyzer.Tests/PlatformDocAnalyzerTests.cs | Adds analyzer unit tests covering PLATDOC001–004 behavior. |
| eng/analyzers/PlatformDocAnalyzer.Tests/PlatformDocAnalyzer.Tests.csproj | Defines the analyzer test project. |
| eng/analyzers/Directory.Build.props | Marks eng/analyzers/* projects as generator/analyzer-style projects. |
| docs/coding-guidelines/adding-api-guidelines.md | Documents the new conventions and diagnostics for platform-specific docs. |
Copilot's findings
- Files reviewed: 14/14 changed files
- Comments generated: 10
Replace the separate GetPNSEDocTargetFramework, AddProjectReferenceTo- PNSEDocSource, and ResolveCanonicalDocSource targets with a single unified flow: GetCanonicalDocSourceTargetFramework -> AddCanonicalDoc- SourceReference -> ConsumeCanonicalDocSource. Both PNSE and platform-specific builds now resolve the same canonical doc source via ProjectReference, guaranteeing it builds first. The canonical doc source has IsCandidateCompilerGeneratedDocFile=true, meaning it never adds a doc-source reference of its own -- so no cycles. For PNSE builds the canonical doc is used as DocFileOverride; for platform-specific builds it is passed to PlatformDocAnalyzer as an AdditionalFile for PLATDOC004 comparison. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Narrow #pragma suppressions to minimal scope with restore in all 5 System.Private.Xml files. - Fix net11.0-linux TFM examples to use valid platform TFMs (windows, ios) in docs and code comments. - Remove unreachable TypeDeclarationSyntax and DelegateDeclarationSyntax switch arms from GetMemberName/GetMemberIdentifierLocation since CheckMembersForDocs skips nested type declarations. - Add eng/analyzers/README.md noting test run instructions and that analyzer tests follow the existing convention of running locally. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Move PNSE Error check into GetCanonicalDocSourceTargetFramework where CanonicalDocSourceTargetFramework can actually be empty, making it reachable. - Skip nested INamedTypeSymbol members in AnalyzeDocConsistency to avoid duplicate PLATDOC004 reports (nested types get their own callback). - Cache s_memberRegex and s_whitespaceRegex as static compiled Regex instances to avoid repeated allocation on the hot path. - Remove unused Microsoft.CodeAnalysis.CSharp.CodeFix.Testing package. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/area-infrastructure-libraries |
- Canonical TFM selection now first tries the exact TFM with platform suffix stripped (e.g. net11.0-windows -> net11.0) before falling back to any non-platform candidate. This ensures the canonical matches the same version when a project targets multiple platform-agnostic TFMs. - Fix IsEffectivelyPublic for C# 8+ interfaces: check for explicit private/protected/internal modifiers instead of treating all interface members as implicitly public. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
gewarren
left a comment
There was a problem hiding this comment.
Thanks for adding this analyzer!
|
Perf testing this results in pretty low overhead: Across all 31 compilations in the System.Private.Xml build (including its dependencies), the analyzer consistently reports <0.001s with <1 callback invocation overhead. For context, the largest total analyzer time was 19.7s (dominated by other analyzers like ILLink). PlatformDocAnalyzer is negligible. |
|
This is breaking crossgen2.slnx and ilc.slnx. Can you take a look? then try to build the crossgen2 project in debug-x64 config. I get |
|
@jakobbotsch It breaks more sln(x)s. I hit it in System.Net today. Dll is not present in x64\Debug folder, but it is present in just Debug folder. As a workaround I create x64 manually and copy pasted Debug there and then I was able to build from VS. |
Add PlatformDocAnalyzer to enforce consistent documentation across platform-specific builds
When a library uses
UseCompilerGeneratedDocXmlFile=true(the default) and targets platform-specific TFMs likenet11.0-windows, only one platform's compiler-generated doc XML is shipped to customers in the IntelliSense package. If XML doc comments are placed on platform-specific partial source files, they may be missing or inconsistent on other platforms.This PR adds a Roslyn analyzer (
eng/analyzers/PlatformDocAnalyzer) that enforces documentation placement conventions and validates doc consistency. It only activates for platform-specific TFMs withUseCompilerGeneratedDocXmlFile=true.Diagnostics
TypeName.cs.TypeName.Something.csnaming convention.TypeName.cs.PLATDOC001–003 are heuristic rules that guide source organization for partial types split across multiple files. PLATDOC004 is the authoritative check: when a project also targets a platform-agnostic TFM (e.g.
net11.0alongsidenet11.0-windows), the build locates the canonical TFM's compiler-generated doc XML and passes it to the analyzer as anAdditionalFile. The analyzer then compares each public API's documentation against the canonical version and reports mismatches.Build integration
eng/intellisense.targets— For platform-specific TFMs, derives the canonical TFM by stripping the platform suffix (e.g.net11.0-windows→net11.0) and passes its doc XML as anAdditionalFileif available. Uses the artifact path directly rather than aProjectReferenceto avoid circular dependencies with PNSE builds.eng/generators.targets— Wires the analyzer into allIsSourceProjectC# builds.eng/analyzers/PlatformDocAnalyzer/PlatformDocAnalyzer.props— DeclaresCompilerVisiblePropertyandCompilerVisibleItemMetadatafor the analyzer to read MSBuild context.Suppressions
Added
#pragma warning disablein 5 System.Private.Xml files that use established non-platform-specific patterns (TypeNameAsync.csfor async partials,XmlResolver.FileSystemResolver.cs/ThrowingResolver.csfor nested-type-adjacent properties).