Skip to content

Add SupportedOSPlatformGuard and UnsupportedOSPlatformGuard Platform-Guard attributes #51541

@buyaa-n

Description

@buyaa-n

Related to #44922

Background and Motivation

The CA1416 Platform Compatibility analyzer already recognizes platform guards using the methods on OperatingSystem, such as OperatingSystem.IsWindows and OperatingSystem.IsWindowsVersionAtLeast. However, the analyzer does not recognize other guard methods like a field, property or helper methods that assert platform guards. Expanding this support involves creating new attributes that indicate that an API asserts platform checks the same way the APIs on OperatingSystem do.

Proposed API

namespace System.Runtime.Versioning
{
    // Existing base type for all platform-specific attributes.
    public abstract class OSPlatformAttribute : Attribute
    {
        private protected OSPlatformAttribute(string platformName);
        public string PlatformName { get; }
    }

+   [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Enum, AllowMultiple = true, Inherited = false)]
+   public sealed class SupportedOSPlatformGuardAttribute : OSPlatformAttribute
+   {
+       public SupportedOSPlatformGuardAttribute(string platformName) ;
+   }

+   [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Enum, AllowMultiple = true, Inherited = false)]
+   public sealed class UnsupportedOSPlatformGuardAttribute : OSPlatformAttribute
+   {
+       public UnsupportedOSPlatformGuardAttribute(string platformName) ;
+   }
}

Usage Examples

One example is in Thread, there is an internal field for IsThreadStartSupported that indicates whether or not the current platform supports calls to Thread.Start, which is not supported on browser. So in this [UnsupportedOSPlatformGuard] attributes can be used for this field. And the analyzer will recognize it as a platform guard.

#if !TARGET_BROWSER
    [UnsupportedOSPlatformGuard("browser")] // The platform guard atribute
    internal const bool IsThreadStartSupported = true;
#endif

// Usage example
protected internal override void QueueTask(Task task)
{
    if (Thread.IsThreadStartSupported) // This will be evaluated same as `!OperatingSystem.IsBrowser()`
    {
        new Thread(s_longRunningThreadWork)
        {
            IsBackground = true,
            Name = ".NET Long Running Task"
        }.UnsafeStart(task);      // Call unsupported on browser APIs safely (without the attribute it would warn here)
    }
}

Additionally, it's expected that projects will commonly create helper methods that wrap around the platform-specific API calls. The platform being guarded using these attributes can be versioned.

[SupportedOSPlatformGuard("windows10.0")]
public bool  IsWindows10OrAbove()
{ 
     return OperatingSystem.IsWindowsVersionAtLeast(10);
}

[SupportedOSPlatform("windows10.0")]
public bool  Windows10OnlyAPI() { }

void Sample ()
{
    if (IsWindows10OrAbove()) // work same as OperatingSystem.IsWindowsVersionAtLeast(10)
    {
        Windows10OnlyAPI();
    }
}

For guarding multiple platforms need to apply the attribute for each platform

class Test
{
    [SupportedOSPlatformGuard(""linux"")]
    [SupportedOSPlatformGuard(""macOS"")]
    [SupportedOSPlatformGuard(""Windows"")]
    private readonly bool _http3Enabled = OperatingSystem.IsLinux() || OperatingSystem.IsWindows() || OperatingSystem.IsMacOS();

    void M1()
    {
        if (_http3Enabled)
        {
            ApiWorkOnWindowsLinuxMac();  // Not warn
        }
        else
        {
            ApiWorkOnWindowsLinuxMac();  // Warns 
        }
    }

    [SupportedOSPlatform("windows")]
    [SupportedOSPlatform("Linux")]
    [SupportedOSPlatform("macos")]
    void ApiWorkOnWindowsLinuxMac() { }
}

cc @terrajobst @jeffhandley

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-approvedAPI was approved in API review, it can be implementedarea-Meta

    Type

    No type

    Projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions