Skip to content

Fix platform guard analysis with call-site support - #55540

Open
rolfbjarne wants to merge 4 commits into
mainfrom
roslyn-analyzers-issue-7665
Open

Fix platform guard analysis with call-site support#55540
rolfbjarne wants to merge 4 commits into
mainfrom
roslyn-analyzers-issue-7665

Conversation

@rolfbjarne

@rolfbjarne rolfbjarne commented Jul 30, 2026

Copy link
Copy Markdown
Member

Summary

CA1416 (Platform compatibility) reported false positives when a custom [SupportedOSPlatformGuard] (or [UnsupportedOSPlatformGuard]) declared multiple platform attributes, and the call site's own platform support already satisfied one or more of those guards.

Repro

The simplest reproduction (roslyn-analyzers#7665):

[assembly: SupportedOSPlatform("macos12.0")]
[assembly: SupportedOSPlatform("tvos12.2")]

partial class TestType
{
    void DoSomething()
    {
        if (IsAtLeastXcode11)
        {
            Console.WriteLine(PerformanceRating); // ❌ spurious CA1416
        }
    }

    [SupportedOSPlatform("macos11.0")]
    [SupportedOSPlatform("tvos13.0")]
    public ulong? PerformanceRating { get; private set; }

    [SupportedOSPlatformGuard("macos11.0")]
    [SupportedOSPlatformGuard("tvos13.0")]
    internal static bool IsAtLeastXcode11 => true;
}

Inside the if, PerformanceRating is fully guarded, yet the analyzer warned. Tellingly, removing either the macOS or the tvOS attributes (from all three sites) made the warning disappear — the bug only surfaced with mixed-platform guards.

Root cause

When processing a guard, the analyzer treated all of its platform attributes as a single unit. If the call site's existing support already covered one platform of a multi-platform guard, the analyzer mishandled the remaining platforms instead of evaluating each one on its own.

Fix

In PlatformCompatibilityAnalyzer.IsKnownValueGuarded, handle each platform of a mixed guard individually:

  • Supported guards already covered by the call site: when a supported guard is suppressed because the call site's support already covers it, preserve the guarded flow (RemoveOtherSupportsOnDifferentPlatforms) instead of re-applying it as a call-site attribute. Gated by the new IsPlatformSupportSuppressedByCallsite helper.
  • Negated guards that can't be reached: exclude a platform from a negated guard branch when that branch is unreachable at the call site's minimum version, while still analyzing platforms whose pre-version branch is reachable. Gated by the new IsPlatformExcludedByCallsite helper.

Tests

Added regression coverage:

All 602 PlatformCompatibilityAnalyzer guard and obsoletion tests pass.

Fixes dotnet/roslyn-analyzers#7665

Preserve the semantics of supported guards already covered by call-site attributes, and recognize when negated guards make a branch unreachable on every platform supported by the call site. Add regressions for roslyn-analyzers issue 7665 and guarded obsoleted APIs.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 6987672a-aa50-405c-a61a-3656b3e567ca
Exclude individual platforms when a negated guard cannot be reached at the call site's minimum version, while continuing to analyze platforms where the pre-version branch is reachable. Cover the mixed four-platform pattern used by macios.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: 6987672a-aa50-405c-a61a-3656b3e567ca
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
2 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@rolfbjarne
rolfbjarne marked this pull request as ready for review August 3, 2026 11:58
@rolfbjarne
rolfbjarne requested a review from a team as a code owner August 3, 2026 11:58
Copilot AI review requested due to automatic review settings August 3, 2026 11:58
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
2 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

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 updates PlatformCompatibilityAnalyzer guard-flow analysis to better handle custom [SupportedOSPlatformGuard] / [UnsupportedOSPlatformGuard] scenarios with multiple platform attributes, especially when the call site’s own platform annotations already satisfy some of the guard conditions, and adds regression tests for those cases.

Changes:

  • Refines IsKnownValueGuarded to evaluate mixed-platform guard attributes independently and to account for call-site platform support when suppressing or excluding guard platforms.
  • Adds new helper predicates (IsPlatformSupportSuppressedByCallsite, IsPlatformExcludedByCallsite) to control when guarded flow should be preserved vs. re-applied as call-site attributes.
  • Adds regression tests covering multi-platform custom guards and negated-guard scenarios with call-site suppression.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs Adjusts guard analysis logic and introduces helpers for call-site suppression/exclusion decisions.
src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs Adds regression test for multi-platform custom guards suppressed by call-site support.
src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.ObsoletedOSPlatformTests.cs Adds regression tests for negated custom guards suppressed (fully/partially) by call-site minimum versions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: dd56dce9-69ee-4cd6-9778-1bcee82f66bd

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs:695

  • IsPlatformSupportSuppressedByCallsite can throw a NullReferenceException because callsiteVersion.SupportedFirst is nullable, but the code calls callsiteVersion.SupportedFirst.IsGreaterThanOrEqualTo(...) without checking for null.
            static bool IsPlatformSupportSuppressedByCallsite(
                PlatformMethodValue value,
                SmallDictionary<string, Versions> attributes,
                SmallDictionary<string, Versions> originalAttributes,
                SmallDictionary<string, Versions>? callsiteAttributes)
                => !attributes.ContainsKey(value.PlatformName) &&
                    originalAttributes.TryGetValue(value.PlatformName, out Versions? originalVersion) &&
                    originalVersion.SupportedFirst != null &&
                    callsiteAttributes != null &&
                    callsiteAttributes.TryGetValue(value.PlatformName, out Versions? callsiteVersion) &&
                    callsiteVersion.SupportedFirst.IsGreaterThanOrEqualTo(originalVersion.SupportedFirst);

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: dd56dce9-69ee-4cd6-9778-1bcee82f66bd

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs:681

  • IsPlatformExcludedByCallsite treats any call site with a non-null SupportedFirst as having a minimum supported version, but that isn’t true for deny-list call sites (e.g. [UnsupportedOSPlatform("macos12.0")] + [SupportedOSPlatform("macos15.0")]), which are also supported on earlier versions (<12). In those cases the negated guard branch can still be reachable on older OS versions, so excluding the platform here can incorrectly skip analysis and lead to false negatives.
            // A call site without a minimum supported version for the platform cannot prove the branch unreachable,
            // so 'SupportedFirst' being null must not exclude the platform.
            static bool IsPlatformExcludedByCallsite(
                PlatformMethodValue value,
                SmallDictionary<string, Versions>? callsiteAttributes)
                => callsiteAttributes != null &&
                    callsiteAttributes.TryGetValue(value.PlatformName, out Versions? attributes) &&
                    attributes.SupportedFirst != null &&
                    attributes.SupportedFirst.IsGreaterThanOrEqualTo(value.Version);

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CA1416 with multiple guard attributes

2 participants