Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Platform compatibility analyzer #3917

Merged
merged 56 commits into from
Aug 26, 2020

Conversation

buyaa-n
Copy link
Contributor

@buyaa-n buyaa-n commented Jul 28, 2020

Customer Impact

Customers could get a warning if they referenced platform-specific APIs

Testing

Unit tests, manual tests, and dogfooding within the dotnet/runtime repo on local builds.

Risk

Medium

Description:

We have added new attributes to annotate APIs being platform-specific; SupportedOSPlatform and UnsupportedOSPlatform.
Based on them we want a Roslyn analyzer that informs developers when they use platform-specific APIs from call sites where the API might not be available.

The semantics of these attributes are updated to become as follows:

  • An API that doesn't have any of these attributes is considered supported by all platforms.
  • If either [SupportedOSPlatform or [UnsupportedOSPlatform] attributes are present, we group all attributes by OS platform identifier:
    • Allow list. If the lowest version for each OS platform is a [SupportedOSPlatform] attribute, the API is considered to only be supported by the listed platforms and unsupported by all other platforms.
    • Deny list. If the lowest version for each OS platform is a [UnsupportedOSPlatform] attribute, then the API is considered to only be unsupported by the listed platforms and supported by all other platforms.
    • Inconsistent list. If for some platforms the lowest version attribute is [SupportedOSPlatform] while for others it is [UnsupportedOSPlatform]
  • Both attributes can be instantiated without version numbers. This means the version number is assumed to be 0.0. This simplifies guard clauses, see examples below for more details.

Sample 1: Deny list.

The API is unsupported only on Windows, then added support from version 10.0.1903, then removed from version 10.0.2004:

[UnsupportedOSPlatform("windows")]
[SupportedOSPlatform("windows10.0.1903")]
[UnsupportedOSPlatform("windows10.0.2004")]
public void DoesNotWorkOnWindows();

Sample 2: Allow list:

Api only allowed on ios12.0 - 14.0 and on ipados from version 13.0

[SupportedOSPlatform("ios12.0")]
[UnsupportedOSPlatform("ios14.0")]
[SupportedOSPlatform("ipados13.0")]
public void OnlyWorksOniOSAndIpadOs();

Related design doc https://github.com/dotnet/designs/blob/master/accepted/2020/platform-checks/platform-checks.md,
https://github.com/dotnet/designs/blob/master/accepted/2020/platform-exclusion/platform-exclusion.md

Runtime issue: dotnet/runtime#37359

Finding Diagnostics

The analyzer could start analyzing diagnostics for any method | event | property | enum invocation, check if they have annotated with any OSPlatformAttribute.

  • if an assembly or a class has marked with any OSPlatformAttribute that attribute applies to all members defined within the assembly/class. So a member will have a combination of its immediate attributes and derived attributes from containing type or assembly.

In general, the analyzer can produce the following diagnostics:

  1. If the member has an SupportedOSPlatform which haven't suppressed => could warn with '{API}' is supported on {OS} {OS version} and later.* (Fixer could suggest for adding SupportedOSPlatform or platform guard OperatingSystem.IsOSPlatformVersionAtLeast(…))
  2. If the member has a UnsupportedOSPlatform which haven't supppressed => could warn with '{API}' is unsupported on {OS} {OS version} and later.

Automatic suppression via Platform context or platform guard methods

Diagnostics can be suppressed by call site annotated with the same attribute with the correct version or a call to guard methods:

  1. Call-site application of OSPlatformAttribute to the containing member, type, module, or assembly related to the current diagnostic.
  2. If the call-site is guarded by a platform check (OperatingSystem.IsOSPlatformVersionAtLeast() related to the current diagnostic.
  • if (1) call-site's containing method, type, module, or assembly's OSPlatformAttribute type and osPlatform string matches check the versions to determine if context suppresses the diagnostic.

  • If (1) not suppressing the diagnostic, determine if the call-site is guarded by a platform check using flow analysis.

Sample: diagnostic suppressed by (2) a call to OperatingSystem.IsOSPlatformVersionAtLeast(). This should work for simple cases where the call is contained inside an if check but also when the check causes control flow to never reach the call:

public void Case1()
{
    if (OperatingSystem.IsOSPlatformVersionAtLeast("iOS", 13))
        AppleApi();
}

public void Case2()
{
    if (!OperatingSystem.IsOSPlatformVersionAtLeast("iOS", 13))
        return;

    AppleApi();
}

Diagnostic properties

  • Category: Interoperability
  • Severity: Warning
  • isEnabledByDefault: true
  • Id: CA1416
  • Title: Validate platform compatibility
  • Description: Using platform-dependent API on a component makes the code no longer work across all platforms.

@buyaa-n buyaa-n requested a review from a team as a code owner July 28, 2020 17:17
@buyaa-n buyaa-n added this to the .NET 5 milestone Jul 28, 2020
@mavasani
Copy link
Contributor

@buyaa-n I think your PR needs to target release/rc-1 branch, master is targeting .NET5 RC2. Last set of merges indicate you have pulled in some changes in your branch that are only in master branch. You may have to revert that merge commit and retarget your PR to the correct branch.

@jeffhandley
Copy link
Member

Good catch, @mavasani!

@buyaa-n buyaa-n force-pushed the platform_spec_analyzer branch from 1d4733c to d5e1e74 Compare August 23, 2020 04:55
@buyaa-n buyaa-n changed the base branch from master to release/dotnet5-rc1 August 23, 2020 04:56
@jeffhandley
Copy link
Member

Please note that we need to address the build warnings that this analyzer will introduce in the dotnet/runtime repo before we can merge this in to the SDK. That effort is underway here: dotnet/runtime#41209

Copy link
Contributor

@mavasani mavasani left a comment

Choose a reason for hiding this comment

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

Overall looks great to me! I haven't reviewed the tests thoroughly, but the given the amount of dogfooding that was done for this analyzer, along with follow-up bug fixes with regression tests, I have no concerns in that space.

Awesome work @buyaa-n!

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.

10 participants