CA1416 : Valider la compatibilité de la plateforme

Propriété Value
Identificateur de la règle CA1416
Titre Valider la compatibilité de la plateforme
Catégorie Interopérabilité
Le correctif est cassant ou non cassant Sans rupture
Activé par défaut dans .NET 8 Comme avertissement

Cause

Des violations sont signalées si une API propre à la plateforme est utilisée dans le contexte d’une autre plateforme ou si la plateforme n’est pas vérifiée (API sans plateforme). Des violations sont également signalées si une API qui n’est pas prise en charge pour la plateforme cible du projet est utilisée.

Cette règle est activée par défaut uniquement pour les projets ciblant .NET 5 ou version ultérieure. Toutefois, vous pouvez l’activer pour les projets qui ciblent d’autres frameworks.

Description de la règle

.NET 5 a ajouté de nouveaux attributs, SupportedOSPlatformAttribute et UnsupportedOSPlatformAttribute, pour annoter les API propres à la plateforme. Les deux attributs peuvent être instanciés avec ou sans numéros de version dans le cadre du nom de la plateforme. Ils peuvent également être appliqués plusieurs fois avec différentes plateformes.

  • Une API non annotée est considérée comme fonctionnant sur toutes les plateformes de système d’exploitation.
  • Une API marquée avec [SupportedOSPlatform("platformName")] est considérée comme portable uniquement sur les plateformes de système d’exploitation spécifiées. Si la plateforme est un sous-ensemble d’une autre plateforme, l’attribut implique que cette plateforme est également prise en charge.
  • Une API marquée avec [UnsupportedOSPlatform("platformName")] est considérée comme non prise en charge sur les plateformes de système d’exploitation spécifiées. Si la plateforme est un sous-ensemble d’une autre plateforme, l’attribut implique que cette plateforme n’est pas prise en charge.

Vous pouvez combiner les attributs [SupportedOSPlatform] et [UnsupportedOSPlatform] sur une API unique. Dans ce cas, les règles suivantes s'appliquent :

  • Liste verte. Si la version la plus basse pour chaque plateforme de système d’exploitation est un attribut [SupportedOSPlatform], l’API est considérée comme prise en charge uniquement par les plateformes listées et non prise en charge par toutes les autres plateformes. La liste peut avoir un attribut [UnsupportedOSPlatform] avec la même plateforme, mais uniquement avec une version supérieure, ce qui indique que l’API est supprimée de cette version.
  • Liste d’exclusion. Si la version la plus basse pour chaque plateforme de système d’exploitation est un attribut [UnsupportedOSPlatform], l’API est considérée comme n’étant pas prise en charge uniquement par les plateformes listées, et prise en charge par toutes les autres plateformes. La liste peut avoir un attribut [SupportedOSPlatform] avec la même plateforme, mais uniquement avec une version supérieure, ce qui indique que l’API est prise en charge depuis cette version.
  • Liste d’incohérence. Si la version la plus basse pour certaines plateformes est [SupportedOSPlatform] mais [UnsupportedOSPlatform] pour d’autres plateformes, cette combinaison est considérée comme incohérente. Certaines annotations sur l’API sont ignorées. À l’avenir, nous pourrons introduire un analyseur qui génère un avertissement en cas d’incohérence.

Si vous accédez à une API annotée avec ces attributs à partir du contexte d’une autre plateforme, vous pouvez voir des violations CA1416.

Plateformes cibles TFM

L’analyseur ne vérifie pas les plateformes cibles de moniker de framework cible (TFM) à partir des propriétés MSBuild, telles que <TargetFramework> ou <TargetFrameworks>. Si le TFM a une plateforme cible, le Kit de développement logiciel (SDK) .NET injecte un attribut SupportedOSPlatform avec le nom de la plateforme ciblée dans le fichier AssemblyInfo.cs, qui est consommé par l’analyseur. Par exemple, si le TFM est net5.0-windows10.0.19041, le SDK injecte l’attribut [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.19041")] dans le fichier AssemblyInfo.cs, et l’assembly entier est considéré comme étant « Windows uniquement ». Par conséquent, l’appel d’API « Windows uniquement » avec une version 7.0 ou antérieure ne provoquerait pas d’avertissements dans le projet.

Notes

Si la génération de fichier AssemblyInfo.cs est désactivée pour le projet (autrement dit, la propriété <GenerateAssemblyInfo> a la valeur false), l’attribut SupportedOSPlatform de niveau d’assembly requis ne peut pas être ajouté par le SDK. Dans ce cas, vous pouvez voir des avertissements pour une utilisation d’API propres à la plateforme, même si vous ciblez cette plateforme. Pour résoudre les avertissements, activez la génération de fichier AssemblyInfo.cs ou ajoutez l’attribut manuellement dans votre projet.

Violations

  • Si vous accédez à une API prise en charge uniquement sur une plateforme spécifiée ([SupportedOSPlatform("platformName")]) à partir de code accessible sur d’autres plateformes, vous verrez la violation suivante : « API » est pris en charge sur « nom_plateforme ».

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    // API is supported on Windows, iOS from version 14.0, and MacCatalyst from version 14.0.
    [SupportedOSPlatform("windows")]
    [SupportedOSPlatform("ios14.0")] // MacCatalyst is a superset of iOS, therefore it's also supported.
    public void SupportedOnWindowsIos14AndMacCatalyst14() { }
    
    public void Caller()
    {
        LinuxOnlyApi(); // This call site is reachable on all platforms. 'LinuxOnlyApi()' is only supported on: 'linux'
    
        SupportedOnWindowsIos14AndMacCatalyst14(); // This call site is reachable on all platforms. 'SupportedOnWindowsIos14AndMacCatalyst14()'
                                                   // is only supported on: 'windows', 'ios' 14.0 and later, 'MacCatalyst' 14.0 and later.
    }
    

    Notes

    Une violation se produit uniquement si le projet ne cible pas la plateforme prise en charge (net5.0-differentPlatform). Cela s’applique également aux projets multi-ciblés. Aucune violation ne se produit si le projet cible la plateforme spécifiée (net5.0-platformName) et que la génération de fichier AssemblyInfo.cs est activée pour le projet.

  • L’accès à une API attribuée avec [UnsupportedOSPlatform("platformName")] à partir d’un contexte qui cible la plateforme non prise en charge peut produire une violation : « API » n’est pas pris en charge sur « nom_plateforme ».

    // An API not supported on Android but supported on all other platforms.
    [UnsupportedOSPlatform("android")]
    public void DoesNotWorkOnAndroid() { }
    
    // An API was unsupported on Windows until version 10.0.18362.
    // The API is considered supported everywhere else without constraints.
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows10.0.18362")]
    public void StartedWindowsSupportFromCertainVersion() { }
    
    public void Caller()
    {
        DoesNotWorkOnAndroid(); // This call site is reachable on all platforms.'DoesNotWorkOnAndroid()' is unsupported on: 'android'
    
        StartedWindowsSupportFromCertainVersion(); // This call site is reachable on all platforms. 'StartedWindowsSupportFromCertainVersion()' is unsupported on: 'windows' 10.0.18362 and before
    }
    

Notes

Si vous créez une application qui ne cible pas la plateforme non prise en charge, aucune violation ne se produira. Une violation se produit uniquement dans les cas suivants :

  • Le projet cible la plateforme qui est attribuée comme non prise en charge.

  • platformName est inclus dans le groupe d’éléments MSBuild <SupportedPlatform> par défaut.

  • platformName est inclus manuellement dans le groupe d’éléments MSBuild <SupportedPlatform>.

    <ItemGroup>
        <SupportedPlatform Include="platformName" />
    </ItemGroup>
    

Comment corriger les violations

La méthode recommandée pour traiter les violations consiste à vous assurer que vous appelez uniquement des API propres à la plateforme lors de l’exécution sur une plateforme appropriée. Pour ce faire, vous pouvez exclure le code au moment de la génération à l’aide de #if et du multi-ciblage, ou en appelant de manière conditionnelle le code au moment de l’exécution. L’analyseur reconnaît les protections de plateforme dans la classe OperatingSystem et System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform.

  • Supprimez les violations en entourant le site d’appel avec les méthodes de protection de plateforme standard ou les API de protection personnalisée annotées avec SupportedOSPlatformGuardAttribute ou UnsupportedOSPlatformGuardAttribute.

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    // API is supported on Windows, iOS from version 14.0, and MacCatalyst from version 14.0.
    [SupportedOSPlatform("windows")]
    [SupportedOSPlatform("ios14.0")] // MacCatalyst is a superset of iOS, therefore it's also supported.
    public void SupportedOnWindowsIos14AndMacCatalyst14() { }
    
    public void Caller()
    {
        LinuxOnlyApi(); // This call site is reachable on all platforms. 'LinuxOnlyApi()' is only supported on: 'linux'.
    
        SupportedOnWindowsIos14AndMacCatalyst14(); // This call site is reachable on all platforms. 'SupportedOnWindowsIos14AndMacCatalyst14()'
                                                   // is only supported on: 'windows', 'ios' 14.0 and later, 'MacCatalyst' 14.0 and later.
    }
    
    [SupportedOSPlatformGuard("windows")]  // The platform guard attributes used
    [SupportedOSPlatformGuard("ios14.0")]
    private readonly bool _isWindowOrIOS14 = OperatingSystem.IsWindows() || OperatingSystem.IsIOSVersionAtLeast(14);
    
    // The warnings are avoided using platform guard methods.
    public void Caller()
    {
        if (OperatingSystem.IsLinux()) // standard guard examples
        {
            LinuxOnlyApi(); // no diagnostic
        }
    
        if (OperatingSystem.IsIOSVersionAtLeast(14))
        {
            SupportedOnWindowsAndIos14(); // no diagnostic
        }
    
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            SupportedOnWindowsAndIos14(); // no diagnostic
        }
    
        if (_isWindowOrMacOS14) // custom guard example
        {
            SupportedOnWindowsAndIos14(); // no diagnostic
        }
    }
    
    // An API not supported on Android but supported on all other platforms.
    [UnsupportedOSPlatform("android")]
    public void DoesNotWorkOnAndroid() { }
    
    // An API was unsupported on Windows until version 10.0.18362.
    // The API is considered supported everywhere else without constraints.
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows10.0.18362")]
    public void StartedWindowsSupportFromCertainVersion();
    
    public void Caller()
    {
        DoesNotWorkOnAndroid(); // This call site is reachable on all platforms.'DoesNotWorkOnAndroid()' is unsupported on: 'android'
    
        StartedWindowsSupportFromCertainVersion(); // This call site is reachable on all platforms. 'StartedWindowsSupportFromCertainVersion()' is unsupported on: 'windows' 10.0.18362 and before.
    }
    
    [UnsupportedOSPlatformGuard("android")] // The platform guard attribute
    bool IsNotAndroid => !OperatingSystem.IsAndroid();
    
    public void Caller()
    {
        if (!OperatingSystem.IsAndroid()) // using standard guard methods
        {
            DoesNotWorkOnAndroid(); // no diagnostic
        }
    
        // Use the && and || logical operators to guard combined attributes.
        if (!OperatingSystem.IsWindows() || OperatingSystem.IsWindowsVersionAtLeast(10, 0, 18362))
        {
            StartedWindowsSupportFromCertainVersion(); // no diagnostic
        }
    
        if (IsNotAndroid) // custom guard example
        {
            DoesNotWorkOnAndroid(); // no diagnostic
        }
    }
    
  • L’analyseur respecte également System.Diagnostics.Debug.Assert comme moyen d’empêcher le code d’être atteint sur des plateformes non prises en charge. L’utilisation de Debug.Assert permet de supprimer la vérification des mises en production, si vous le souhaitez.

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    public void Caller()
    {
        Debug.Assert(OperatingSystem.IsLinux());
    
        LinuxOnlyApi(); // No diagnostic
    }
    
  • Vous pouvez choisir de marquer vos propres API comme étant propres à la plateforme, ce qui a pour conséquence de transférer les exigences à vos appelants. Vous pouvez appliquer des attributs de plateforme aux API suivantes :

    • Types
    • Membres (méthodes, champs, propriétés et événements)
    • Assemblys
    [SupportedOSPlatform("windows")]
    [SupportedOSPlatform("ios14.0")]
    public void SupportedOnWindowsAndIos14() { }
    
    [SupportedOSPlatform("ios15.0")] // call site version should be equal to or higher than the API version
    public void Caller()
    {
        SupportedOnWindowsAndIos14(); // No diagnostics
    }
    
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows10.0.18362")]
    public void StartedWindowsSupportFromCertainVersion();
    
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows10.0.18362")]
    public void Caller()
    {
        StartedWindowsSupportFromCertainVersion(); // No diagnostics
    }
    
  • Lorsqu’un attribut au niveau de l’assembly ou au niveau du type est appliqué, tous les membres de l’assembly ou du type sont considérés comme propres à la plateforme.

    [assembly:SupportedOSPlatform("windows")]
    public namespace ns
    {
        public class Sample
        {
            public void SupportedOnWindows() { }
    
            public void Caller()
            {
                SupportedOnWindows(); // No diagnostic as call site and calling method both windows only
            }
        }
    }
    

Quand supprimer les avertissements

Il n’est pas recommandé de référencer des API propres à la plateforme sans un contexte de plateforme ou une protection approprié(e). Toutefois, vous pouvez supprimer ces diagnostics à l’aide de #pragma ou de l’indicateur du compilateur NoWarn, ou en définissant la gravité de la règle sur none dans un fichier .editorconfig.

[SupportedOSPlatform("linux")]
public void LinuxOnlyApi() { }

public void Caller()
{
#pragma warning disable CA1416
    LinuxOnlyApi();
#pragma warning restore CA1416
}

Configurer le code à analyser

L’analyseur est activé par défaut uniquement pour les projets qui ciblent .NET 5 ou version ultérieure et dont la valeur AnalysisLevel est 5 ou plus. Vous pouvez l’activer pour les frameworks cibles inférieurs à net5.0 en ajoutant la paire clé-valeur suivante à un fichier .editorconfig dans votre projet :

dotnet_code_quality.enable_platform_analyzer_on_pre_net5_target = true

Voir aussi