Skip to content

Recognize IsApplePlatform as a four-platform OS guard - #56576

Draft
makazeu wants to merge 2 commits into
dotnet:mainfrom
makazeu:u/makazeu/isappleplatform-analyzer-support
Draft

makazeu wants to merge 2 commits into
dotnet:mainfrom
makazeu:u/makazeu/isappleplatform-analyzer-support

Conversation

@makazeu

@makazeu makazeu commented Oct 10, 2026 •

Copy link
Copy Markdown

Summary

Teach the platform compatibility analyzer (CA1416) to recognize OperatingSystem.IsApplePlatform() as a guard for the union of these four platforms:

  • macOS
  • MacCatalyst
  • iOS
  • tvOS

This supports the public API introduced by dotnet/runtime#135475. The current runtime implementation does not include watchOS, and the analyzer follows that definition.

This change is entirely within the analyzer and its tests. It does not require changes to the runtime implementation, reference API, or platform guard attributes.

Problem

The analyzer already discovers OperatingSystem.IsApplePlatform() through its existing convention for static, parameterless Boolean methods named Is* on System.OperatingSystem.

However, the existing decoder assumes that each such method represents one platform. It removes the Is prefix and therefore interprets this method as a check for a fictitious platform named ApplePlatform, rather than a check for four actual platforms.

This can produce both false positives and false negatives:

  • False positive: an API supported on all four Apple platforms can still receive CA1416 inside an IsApplePlatform() guard because ApplePlatform does not match its supported platform annotations.
  • False negative: an API annotated with [UnsupportedOSPlatform("ios")] can incorrectly have its warning suppressed because the fictitious ApplePlatform is treated as different from ios, even though the guard can be true on iOS.

Calls to APIs annotated only with [UnsupportedOSPlatform("windows")] can already appear to work with the old decoder. That result is coincidental: it does not demonstrate that the analyzer understands the Apple platform set.

Implementation

Add an IsApplePlatform method-name constant and special-case this method in the parameterless branch of PlatformMethodValue.TryDecode, before the existing single-platform name extraction.

The decoder emits four unversioned, non-negated PlatformMethodValue instances:

macos
maccatalyst
ios
tvos

The existing operation visitor merges these values with OR semantics. Existing control-flow analysis also handles negation, so neither the operation visitor nor the guard discovery logic needs to change.

The resulting behavior is:

  • The true branch is reachable on any of the four Apple platforms.
  • A guarded API must be safe on every reachable Apple platform, not merely one of them.
  • The false branch, including an explicit !IsApplePlatform() condition, excludes all four Apple platforms.
  • Windows and watchOS are not included in the true branch and are not excluded by the false branch.

The production changes are limited to:

  • PlatformCompatibilityAnalyzer.cs: add the method-name constant.
  • PlatformCompatibilityAnalyzer.Value.cs: decode the aggregate guard into four platform values.

FilterPlatformCheckMethods and PlatformCompatibilityAnalyzer.OperationVisitor.cs remain unchanged. No Unix platform-family mapping or OS-version behavior is introduced.

Regression test setup

The existing test helpers use .NET 6 reference assemblies, which do not expose OperatingSystem.IsApplePlatform(). The new tests therefore provide a source stub with the exact System.OperatingSystem type name and a static, parameterless Boolean IsApplePlatform method.

The stub intentionally has no platform guard attributes. This ensures that the tests exercise the built-in guard discovery and decoding path changed by this PR, rather than the separate custom guard attribute path.

The method body throws NotImplementedException. Test source is compiled and analyzed, not executed; no runtime platform detection is involved.

The tests also explicitly configure:

build_property._SupportedPlatformList = macos,maccatalyst,ios,tvos,watchos,windows
build_property.TargetFramework = net5.0
build_property.TargetFrameworkIdentifier = .NETCoreApp
build_property.TargetFrameworkVersion = v5.0

The target framework metadata enables platform analysis under the existing analyzer rules. _SupportedPlatformList ensures that the unsupported-platform annotations used by the tests are retained for analysis instead of being filtered out beforehand.

Including watchOS in this test configuration is intentional: it is a negative control that verifies watchOS is not part of the Apple guard. It does not add watchOS support to the runtime or the decoder.

Each [|...|] marker identifies a call that must produce CA1416. Calls without markers must remain free of unexpected diagnostics. The tests therefore check both required warnings and required suppression.

Test coverage

Three test methods produce six executed test cases: one comprehensive C# case, four parameterized C# cases, and one Visual Basic case.

1. C# positive guard and implicit negation

IsApplePlatform_GuardsOnlyFourApplePlatformsAsync exercises both branches of:

if (OperatingSystem.IsApplePlatform())
{
    // Reachable on macOS, MacCatalyst, iOS, or tvOS.
}
else
{
    // All four Apple platforms are excluded.
}
API annotations True branch Else branch
Supported on all four Apple platforms No warning CA1416
Supported on iOS and macOS, but not all four Apple platforms CA1416 Not exercised
Supported only on Windows CA1416 Not exercised
Supported only on watchOS CA1416 Not exercised
Unsupported on Windows No warning CA1416
Unsupported on watchOS No warning CA1416
Unsupported on iOS CA1416 No warning
Unsupported on all four Apple platforms Not exercised No warning

These assertions cover:

  • Correct suppression for APIs supported throughout the Apple platform set.
  • Preservation of warnings when an API supports only part of that set.
  • Preservation of an iOS deny-list warning inside the positive guard, preventing the original false negative.
  • Exclusion of Windows and watchOS from the positive guard.
  • Correct propagation of the inverse condition into else.
  • Suppression of Apple deny-list warnings when all Apple platforms have been excluded.

2. C# explicit negation, independently for every Apple platform

IsApplePlatform_NegatedGuardExcludesOnlyApplePlatformsAsync runs with four data rows:

macos
maccatalyst
ios
tvos

For each platform, it defines an API unsupported on that platform and checks:

Condition API unsupported on the selected Apple platform API unsupported on Windows
!OperatingSystem.IsApplePlatform() No warning CA1416
Else branch of that condition CA1416 No warning

Testing each Apple platform independently verifies that all four values participate in decoding and negation. It also detects omissions from the platform set: a missing platform would no longer be correctly excluded by the negated guard.

Windows is an independent control that verifies the negative guard does not incorrectly exclude non-Apple platforms.

3. Visual Basic positive and negative guards

IsApplePlatform_GuardsApplePlatforms_VisualBasicAsync verifies the same built-in decoding path through Visual Basic operations and control flow.

Condition Scenario Expected result
If OperatingSystem.IsApplePlatform() Then API supported on all four Apple platforms No warning
Positive guard API supported only on iOS CA1416
Positive guard API unsupported on Windows No warning
Positive guard API unsupported on iOS CA1416
If Not OperatingSystem.IsApplePlatform() Then API unsupported on iOS No warning
Negative guard API unsupported on Windows CA1416
Negative guard API supported only on the four Apple platforms CA1416

This covers both If and Not in VB and verifies that the fix is not limited to C# syntax.

Validation

The new regressions were exercised against the original decoder and reproduced incorrect analysis involving the fictitious ApplePlatform. All six new cases pass with the fix.

The analyzer solution builds successfully with 0 warnings and 0 errors:

$env:DOTNET_ADD_GLOBAL_TOOLS_TO_PATH = '0'
.\build.cmd -projects src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.slnx -c Debug

The platform compatibility analyzer test group was run using the repository test driver:

.\.dotnet\dotnet.exe scripts\RunTests.cs -- `
    --project src\Microsoft.CodeAnalysis.NetAnalyzers\tests\Microsoft.CodeAnalysis.NetAnalyzers.UnitTests\Microsoft.CodeAnalysis.NetAnalyzers.UnitTests.csproj `
    --filter "FullyQualifiedName~PlatformCompatabilityAnalyzerTests" `
    --skip-redist-check

Result: 344 total, 342 passed, 2 existing skipped tests, 0 failed.

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

Decode OperatingSystem.IsApplePlatform as macOS, MacCatalyst, iOS, or tvOS instead of a fictitious ApplePlatform OS. Preserve existing builtin guard registration and flow-analysis negation.

Add C# and VB regressions for allow lists, deny lists, each Apple platform, Windows, watchOS, and negation. Document builtin guard test setup.

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

Copilot-Session: ee6e48ad-6898-45d7-98ed-9850afb3bd30
@azure-pipelines

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

Restore AGENTS.md to the original baseline as requested, preserving IsApplePlatform analyzer changes and regression tests.

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

Copilot-Session: ee6e48ad-6898-45d7-98ed-9850afb3bd30

This branch has not been deployed

No deployments
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.

1 participant