Skip to content

Add an analyzer for [Obsolete] on authored APIs without [Deprecated] - #2506

Merged
Sergio0694 merged 3 commits into
user/sergiopedri/project-experimental-attributefrom
user/sergiopedri/obsolete-on-authored-types
Aug 1, 2026
Merged

Add an analyzer for [Obsolete] on authored APIs without [Deprecated]#2506
Sergio0694 merged 3 commits into
user/sergiopedri/project-experimental-attributefrom
user/sergiopedri/obsolete-on-authored-types

Conversation

@Sergio0694

@Sergio0694 Sergio0694 commented Aug 1, 2026

Copy link
Copy Markdown
Member

Summary

Add CSWINRT2021, an analyzer that reports publicly exposed APIs of an authored Windows Runtime component carrying [Obsolete] without [Windows.Foundation.Metadata.Deprecated].

Motivation

The WinMD generator does not translate [Obsolete] into [Deprecated]: it copies it verbatim, so the .winmd ends up with a System.ObsoleteAttribute reference that no other language projection (C++/WinRT, windows-rs, ...) understands. [Obsolete] on its own therefore deprecates an API for nobody but the C# code inside the component itself.

That is not obvious. [Obsolete] is the way to deprecate an API in C#, it compiles without complaint, and the component builds and works — the deprecation just silently never reaches any consumer. #2503 documented the behavior, but a doc only helps the developer who already suspects there is something to look up.

Changes

  • src/Authoring/WinRT.SourceGenerator2/Diagnostics/DiagnosticDescriptors.cs, AnalyzerReleases.Shipped.md: the CSWINRT2021 descriptor (Warning), registered for release tracking.

  • src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/ObsoleteWithoutDeprecatedAnalyzer.cs: the analyzer. Gated on CsWinRTComponent, and silent when both attributes are applied, which is the supported way to deprecate an API for .NET and Windows Runtime consumers alike.

  • docs/attribute-projections.md: links the diagnostic from the [Obsolete] note added in Project the Windows Runtime [Experimental] attribute as the .NET one #2503.

Scope

The check covers types and their members, not just types: the WinMD generator supports [Deprecated] on methods, properties and events too, so [Obsolete] on a member is exactly as silently ineffective as it is on a type.

Only what actually reaches the .winmd is reported, so that every report has an action the developer can take:

  • Types are only reported when public and top level, as Windows Runtime has no nested types.

  • Members are only reported when public and declared by such a type, and only for classes and interfaces: a Windows Runtime struct is a plain field aggregate, so the generator drops every member of one other than its public instance fields.

  • Accessors are skipped, as they are only exported as part of their property or event (which is reported instead). The generator moves a [Deprecated] from the property or event down onto the accessor row, and never reads one written on a C# accessor.

  • Constructors are skipped, because [Deprecated] does not include AttributeTargets.Constructor in its usage and so cannot be applied to one at all. Reporting them would produce a warning whose only resolutions are suppressing it or dropping the [Obsolete].

Testing

src/Tests/SourceGenerator2Test/Test_ObsoleteWithoutDeprecatedAnalyzer.cs covers the cases the analyzer must stay quiet for (no attributes, only [Deprecated], both attributes, non-component projects, non-public types and members, nested types, struct members, constructors, accessors) and the ones it must report (every public type kind, and public methods, properties and events of both classes and interfaces).

The accessor test deliberately puts [Obsolete] on the accessor rather than on the property: an attribute written on a property is never surfaced on its accessor symbols, so a test relying on that would pass with the guard removed. Verified that this one does fail without it.

Full suite: 148/148 passing.

Sergio0694 and others added 3 commits August 1, 2026 13:44
…ted]

'[Obsolete]' is a .NET concept with no Windows Runtime counterpart, so the WinMD generator
copies it verbatim into the '.winmd' rather than translating it, where no other language
projection can see it. Deprecating an API of an authored component requires
'[Windows.Foundation.Metadata.Deprecated]', which is the only deprecation Windows Runtime
metadata can carry.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ed5549ac-fb33-4d08-a8f3-d6cf3b8bc8e1
Reports publicly exposed APIs of a Windows Runtime component that carry '[Obsolete]' but no
'[Windows.Foundation.Metadata.Deprecated]'. Having both applied is the supported way to deprecate
an API for .NET and Windows Runtime consumers alike, so that combination is not reported.

The check covers types and their members, not just types: the WinMD generator supports
'[Deprecated]' on methods, properties and events too, so '[Obsolete]' on a member is exactly as
silently ineffective as it is on a type.

Only what actually reaches the '.winmd' is considered, so that every report has an action the
developer can take:

  - Types are only reported when public and top level, as Windows Runtime has no nested types.
  - Members are only reported when public and declared by such a type. They are also restricted to
    classes and interfaces: a Windows Runtime struct is a plain field aggregate, so the generator
    drops every member of one other than its public instance fields.
  - Accessors are skipped, as they are only exported as part of their property or event (which is
    reported instead). The generator moves a '[Deprecated]' from the property or event down onto
    the accessor row, and never reads one written on a C# accessor.
  - Constructors are skipped, because '[Deprecated]' does not include 'AttributeTargets.Constructor'
    in its usage and so cannot be applied to one at all. Reporting them would produce a warning
    whose only resolutions are suppressing it or dropping the '[Obsolete]'.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ed5549ac-fb33-4d08-a8f3-d6cf3b8bc8e1
Covers the cases the analyzer must stay quiet for (no attributes, only '[Deprecated]', both
attributes, non component projects, non public types and members, nested types, struct members,
constructors and accessors) and the ones it must report (every public type kind, and public
methods, properties and events of both classes and interfaces).

The accessor case is the one most likely to regress into a vacuous test, so it puts '[Obsolete]'
on the accessor itself rather than on the property: an attribute written on a property is never
surfaced on its accessor symbols, so a test that relies on that would pass with the guard removed.
Verified that this one does fail without it.

Also links the diagnostic from the '[Obsolete]' note in the attribute projections doc, which is
where the behavior it guards is documented.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ed5549ac-fb33-4d08-a8f3-d6cf3b8bc8e1
@Sergio0694 Sergio0694 added authoring Related to authoring feature work tooling CsWinRT 3.0 labels Aug 1, 2026
@Sergio0694
Sergio0694 requested a review from manodasanW August 1, 2026 21:18
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@Sergio0694
Sergio0694 merged commit fb36062 into staging/3.0 Aug 1, 2026
7 of 12 checks passed
@Sergio0694
Sergio0694 deleted the user/sergiopedri/obsolete-on-authored-types branch August 1, 2026 22:04
@Sergio0694
Sergio0694 restored the user/sergiopedri/obsolete-on-authored-types branch August 3, 2026 02:21
@Sergio0694

Copy link
Copy Markdown
Member Author

Superseded by #2508. This PR was never actually merged: while #2503 was being fixed, its branch (the base of this PR) briefly contained this PR's commits, and GitHub marks a PR as merged as soon as its head commits become reachable from its base. Those commits were removed from #2503 again two minutes later, so nothing from here landed in \staging/3.0, #2503 or #2457. A merged PR cannot be reopened, so the work continues in #2508 with byte-identical content.

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

Labels

authoring Related to authoring feature work CsWinRT 3.0 tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant