Skip to content
Purview Dev logo

SDK-Shipped Analyzers

Stable Build SDK Reviewed 2026-10-28 purview-dev/build-sdk Star on GitHub 1 central-package-management code-style csharp developer-experience developer-tooling devex dotnet msbuild msbuild-sdk nuget project-templates roslyn roslyn-analyzers roslyn-generators tunit xunit

The package ships Purview.BuildSdk.Analyzers.dll (plus a separate code-fix assembly for the IDE) and adds the analyzer to every C# project as an <Analyzer> item, so the rules surface in both command-line builds and Visual Studio.

RuleCategorySeverityDescription
PDS0001(suppressor)—Suppresses CS1591 for EditorBrowsable(Never) members
PDS0002NamingWarningFiles under a project-root Extensions/ folder reset their namespace
PDS0003StyleWarningPrefer an explicit type with target-typed new() over var
PDS0004NamingWarningUse correct acronym capitalization (Api → API)
PDS0005(suppressor)—Suppresses IDE0130 for files rooted under Extensions/
PDS0006(suppressor)—Suppresses CA1815 on a union declaration
PDS0007(suppressor)—Suppresses CA1034 on an extension block
PDS0008SecurityWarningA BFF’s authentication cookie must not be set HttpOnly = false
PDS0009SecurityWarningA BFF’s authentication cookie must not be set SameSite = None
Section titled “PDS0008 / PDS0009 — BFF authentication cookie”

Active only in a project the SDK classifies as a BFF (build_property.IsBffProject=true). A BFF keeps the tokens on the server and hands the browser a session cookie; these rules report the two settings that undo that:

  • PDS0008 — HttpOnly = false: script running in the page, injected script included, can read the session and replay it from anywhere.
  • PDS0009 — SameSite = None: the browser attaches the session to cross-site requests, so every BFF endpoint becomes a cross-site request forgery target.

Only a constant assigned to the cookie of CookieAuthenticationOptions is reported (options.Cookie.HttpOnly = false, or Cookie = { SameSite = SameSiteMode.None }). The antiforgery cookie, which an SPA legitimately reads, and the OpenID Connect correlation and nonce cookies, which legitimately need SameSite=None, are never reported. A cookie configured in another assembly, or through a local variable holding the builder, is missed rather than guessed at.

A persisted Data Protection key ring is deliberately not checked: the PersistKeysTo* call usually lives in a shared ServiceDefaults project the BFF’s compilation cannot see, so a rule would report correctly configured BFFs. Anti-forgery on forwarded routes stays a review obligation.

The SDK ships the modifier/visibility and field-naming policy in .editorconfig and enforces it with ValidatePurviewStylePolicy (PRSGD0006-PRSGD0009), so a repository cannot neuter it:

  • dotnet_style_require_accessibility_modifiers = omit_if_default:warning: a declared modifier that matches the language default (private inside a type, internal at namespace scope, public inside an interface) is reported by IDE0040 and must be removed.
  • Private instance fields must be _camelCase (_name, never name): the _ prefix keeps field access unambiguous, so the noisy this. qualifier is never needed. Private constants and private static readonly fields are type-level state and stay PascalCase; non-private constants and static readonly fields follow the same rule, and local constants stay camelCase.
  • CA1515 (public type in an application/test assembly) ships at warning; CA1852 (seal internal types) is not suppressed for types exposed through InternalsVisibleTo; CA1034 is not disabled for Extensions/ files; the API-surface rules CA1062/CA1707 are not pre-suppressed for repositories. Where the public surface is framework-mandated the SDK exempts itself: Aspire hosts and CLI apps keep their nested public options types (Spectre.Console settings, [ZodSchema] resource-kit options), and test projects use the test-context rule set below. Both exemptions are recorded so PRSGD0008 accepts them while still rejecting repository-authored silencing.
  • Test and shared-testing projects are context aware: they enforce the same strict style contract as the rest of the repository (IDE0040, field naming, formatting, the IDE1006 naming rules) but not the production API-surface rules. PurviewTestContextNoWarn exempts CA1002, CA1012, CA1034, CA1047, CA1050, CA1051, CA1062, CA1064, CA1515 and CA1707, because test classes and fixtures are legitimately public, test names use Method_Scenario_Expectation, helpers expose fields and take fixture parameters without null guards, and abstract test bases have public constructors. DisablePurviewTestContextRuleSet=true opts a repository into the production rules for tests too.
  • Shared testing projects are helper libraries (their test packages request the test-host shape, and the SDK reasserts OutputType=Library after package props). Their fixtures stay public API, which is also why CA1515 does not apply to them; PurviewSharedTestingOutputType=Exe opts back in.
CodeReported when
PRSGD0006dotnet_style_require_accessibility_modifiers is set to anything other than omit_if_default.
PRSGD0007A policy rule is downgraded below warning (IDE0040, CA1515, CA1852, CA1034), any policy rule is set to none/silent, or the Style category is disabled in bulk without an explicit IDE0040 severity.
PRSGD0008One of the accessibility rules (IDE0040, CA1515, CA1852, CA1034, CA1012, CA1047, CA1050, CA1051, CA1062, CA1064, CA1707) is added to NoWarn by the repository. Entries the SDK injects itself (the test-context rule set, CA1515 for Aspire hosts and CLI apps) are ignored.
PRSGD0009IDE1006 is hidden, the private instance field naming rule is downgraded below warning, or the _ prefix is removed from the field naming style.

Opt out with <DisablePurviewStylePolicyValidation>true</DisablePurviewStylePolicyValidation>. A stale bootstrapped .editorconfig is reported rather than obeyed — refresh it with PurviewRepoBootstrapMode=Always (or delete it).

When a file is placed under a project-root Extensions/ folder, the analyzer intentionally treats that folder as a namespace reset point.

  • Scope: only files where the first project-relative segment is exactly Extensions
  • Expected namespace: derived from subfolders under Extensions/ (file name is ignored)
  • RootNamespace is deliberately ignored for these files

Examples:

Project-relative file pathExpected namespace
Extensions/System/StringExtensions.csSystem
Extensions/Microsoft/Extensions/Configuration/ConfigurationExtensions.csMicrosoft.Extensions.Configuration
Extensions/TopLevel.cs(global namespace)

To avoid conflicting guidance, IDE0130 is suppressed (PDS0005) for files in this root Extensions/ scope. The shipped .editorconfig also suppresses the namespace-conflict diagnostics this convention can trigger (CA1724, CS0436, CS1591, IDE0005), so extension files never need #pragma suppressions. Outside this scope, normal IDE0130 behaviour remains unchanged.

PDS0003 — Prefer explicit type with target-typed new()

Section titled “PDS0003 — Prefer explicit type with target-typed new()”

Flags var declarations that use a target-typed object creation initializer, preferring:

// ❌ flagged
var service = new Service();
// ✅ preferred
Service service = new();

PDS0004 follows .NET naming guidance for well-known framework spellings (Sql, Guid, Uuid, Url, Dns, Http, Xml, DbContext, …) while still enforcing uppercase for acronyms such as Api → API, Ai → AI, Cpu → CPU, Gpu → GPU, Cli → CLI, Gui → GUI, Ram → RAM, and Ssh → SSH. By default the acronym-like segments Http, Xml, Json, Id, Sdk, Sql, Uuid, Url, Dns, Tcp, Udp, Csv, Pdf, Html, Css, Ftp, Smtp, Imap, and Db are exempt — Db is deliberately exempt so the prevalent EF Core/ADO.NET spellings (DbContext, DbConnection, DbSet, CreateDbContext) are never flagged; a repo that prefers DB can re-enable it via acronym_map = Db:DB. All options are customisable per repo, project, or folder via .editorconfig and merge with the shipped defaults — config entries override them (so you can opt into Sql → SQL or opt out of Cli → CLI without re-declaring every default):

  • dotnet_analyzer_configuration.pds0004.allowed_words — semicolon-separated segments that are never flagged.
  • dotnet_analyzer_configuration.pds0004.acronym_map — semicolon-separated Key:Value corrections.
  • dotnet_analyzer_configuration.pds0004.allowed_identifiers — semicolon-separated whole identifiers that are never flagged. Matched in the order listed (first match wins) by exact name or word-boundary prefix, so a brand name like CosmosDb also covers CosmosDbServer/CosmosDbContext.

Members whose names are mandated by a contract — interface implementations (implicit or explicit) and base-class overrides — are never renamed, since doing so would break the contract.

[*.cs]
# Optional overrides; everything not mentioned keeps its shipped default.
dotnet_analyzer_configuration.pds0004.allowed_words = Cli
dotnet_analyzer_configuration.pds0004.acronym_map = Db:DB
dotnet_analyzer_configuration.pds0004.allowed_identifiers = ICosmosDBService;CosmosDb;GitHub;YouTube

The code fix for PDS0004 renames the identifier and all of its references across the solution; see Code Fixes.