Skip to content

Platform specific docs analyzer#128411

Merged
ericstj merged 8 commits into
dotnet:mainfrom
ericstj:platform-specific-docs-analyzer
May 22, 2026
Merged

Platform specific docs analyzer#128411
ericstj merged 8 commits into
dotnet:mainfrom
ericstj:platform-specific-docs-analyzer

Conversation

@ericstj

@ericstj ericstj commented May 20, 2026

Copy link
Copy Markdown
Member

Add PlatformDocAnalyzer to enforce consistent documentation across platform-specific builds

When a library uses UseCompilerGeneratedDocXmlFile=true (the default) and targets platform-specific TFMs like net11.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 with UseCompilerGeneratedDocXmlFile=true.

Diagnostics

ID Severity Description
PLATDOC001 Warning Public type has no source file named TypeName.cs.
PLATDOC002 Warning Partial source file doesn't follow the TypeName.Something.cs naming convention.
PLATDOC003 Warning Public member in a non-primary partial file has XML doc comments that should be moved to TypeName.cs.
PLATDOC004 Warning Documentation for a public API differs from the canonical (platform-agnostic) build.

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.0 alongside net11.0-windows), the build locates the canonical TFM's compiler-generated doc XML and passes it to the analyzer as an AdditionalFile. 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-windowsnet11.0) and passes its doc XML as an AdditionalFile if available. Uses the artifact path directly rather than a ProjectReference to avoid circular dependencies with PNSE builds.
  • eng/generators.targets — Wires the analyzer into all IsSourceProject C# builds.
  • eng/analyzers/PlatformDocAnalyzer/PlatformDocAnalyzer.props — Declares CompilerVisibleProperty and CompilerVisibleItemMetadata for the analyzer to read MSBuild context.

Suppressions

Added #pragma warning disable in 5 System.Private.Xml files that use established non-platform-specific patterns (TypeNameAsync.cs for async partials, XmlResolver.FileSystemResolver.cs / ThrowingResolver.cs for nested-type-adjacent properties).

ericstj and others added 4 commits May 19, 2026 11:44
…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>
Copilot AI review requested due to automatic review settings May 20, 2026 17:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 PlatformDocAnalyzer with 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.targets and wire the analyzer into source project builds via eng/generators.targets.
  • Update documentation guidance and add suppressions in a handful of System.Private.Xml files.
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

Comment thread src/libraries/System.Private.Xml/src/System/Xml/XmlResolver.ThrowingResolver.cs Outdated
Comment thread src/libraries/System.Private.Xml/src/System/Xml/XmlResolver.FileSystemResolver.cs Outdated
Comment thread src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWriterAsync.cs Outdated
Comment thread src/libraries/System.Private.Xml/src/System/Xml/Core/XmlReaderAsync.cs Outdated
Comment thread docs/coding-guidelines/adding-api-guidelines.md
Comment thread eng/analyzers/PlatformDocAnalyzer/PlatformDocAnalyzer.cs
Comment thread eng/analyzers/PlatformDocAnalyzer/PlatformDocAnalyzer.cs
Comment thread eng/analyzers/PlatformDocAnalyzer/PlatformDocAnalyzer.cs
Comment thread eng/intellisense.targets Outdated
ericstj and others added 2 commits May 20, 2026 13:22
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>
Copilot AI review requested due to automatic review settings May 20, 2026 21:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 15/15 changed files
  • Comments generated: 5

Comment thread eng/intellisense.targets
Comment thread eng/analyzers/PlatformDocAnalyzer/PlatformDocAnalyzer.cs
Comment thread eng/analyzers/PlatformDocAnalyzer/PlatformDocAnalyzer.cs
Comment thread eng/analyzers/PlatformDocAnalyzer/PlatformDocAnalyzer.cs Outdated
Comment thread eng/analyzers/PlatformDocAnalyzer.Tests/PlatformDocAnalyzer.Tests.csproj Outdated
- 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>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-infrastructure-libraries
See info in area-owners.md if you want to be subscribed.

@ericstj ericstj requested review from jeffhandley and jkotas May 21, 2026 19:08
@ericstj ericstj changed the title [Experiment] Platform specific docs analyzer Platform specific docs analyzer May 21, 2026
@ericstj ericstj requested a review from gewarren May 21, 2026 19:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 15/15 changed files
  • Comments generated: 2

Comment thread eng/intellisense.targets
Comment thread eng/analyzers/PlatformDocAnalyzer/PlatformDocAnalyzer.cs
- 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 gewarren left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this analyzer!

@ericstj

ericstj commented May 22, 2026

Copy link
Copy Markdown
Member Author

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.

@ericstj ericstj merged commit d45cbb3 into dotnet:main May 22, 2026
167 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview6 milestone May 23, 2026
@jakobbotsch

Copy link
Copy Markdown
Member

This is breaking crossgen2.slnx and ilc.slnx. Can you take a look?
To repro just do

.\build.cmd -vs .\src\coreclr\tools\aot\crossgen2.slnx

then try to build the crossgen2 project in debug-x64 config. I get

Metadata file 'C:\dev\dotnet\runtime3\artifacts\bin\PlatformDocAnalyzer\x64\Debug\netstandard2.0\PlatformDocAnalyzer.dll' could not be found

@mrek-msft

Copy link
Copy Markdown
Member

@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.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants