diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Shipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Shipped.md
index e46ea924ed..257f87974e 100644
--- a/src/NetAnalyzers/Core/AnalyzerReleases.Shipped.md
+++ b/src/NetAnalyzers/Core/AnalyzerReleases.Shipped.md
@@ -63,7 +63,7 @@ CA1308 | Globalization | Disabled | NormalizeStringsToUppercaseAnalyzer, [Docume
CA1309 | Globalization | Hidden | UseOrdinalStringComparisonAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1309)
CA1401 | Interoperability | Info | PInvokeDiagnosticAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1401)
CA1414 | Interoperability | Disabled | MarkBooleanPInvokeArgumentsWithMarshalAsAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1414)
-CA1416 | Interoperability | Info | RuntimePlatformCheckAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1416)
+CA1416 | Interoperability | Warning | PlatformCompatabilityAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1416)
CA1417 | Interoperability | Warning | DoNotUseOutAttributeStringPInvokeParametersAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1417)
CA1500 | Maintainability | Disabled | VariableNamesShouldNotMatchFieldNamesAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1500)
CA1501 | Maintainability | Disabled | CodeMetricsAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1501)
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs
new file mode 100644
index 0000000000..b7015d064d
--- /dev/null
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.NetCore.Analyzers.InteropServices
+{
+ public sealed partial class PlatformCompatabilityAnalyzer
+ {
+ ///
+ /// Class used for keeping platform information of an API, all properties are optional.
+ ///
+ /// We need to keep only 2 values for [SupportedOSPlatform] attribute, first one will be the lowest version found, mostly for assembly level
+ /// attribute which denotes when the API first introduced, second one would keep new APIs added later and requries higher platform version
+ /// (if there is multiple version found in the API parents chain we will keep only highest version)
+ ///
+ /// Same for [UnsupportedOSPlatform] attribute, an API could be unsupported at first and then start supported from some version then eventually removed.
+ /// So we only keep at most 2 versions of [UnsupportedOSPlatform] first one will be the lowest version found, second one will be second lowest if there is any
+ ///
+ /// Properties:
+ /// - SupportedFirst - keeps lowest version of [SupportedOSPlatform] attribute found
+ /// - SupportedSecond - keeps the highest version of [SupportedOSPlatform] attribute if there is any
+ /// - UnsupportedFirst - keeps the lowest version of [UnsupportedOSPlatform] attribute found
+ /// - UnsupportedSecond - keeps the second lowest version of [UnsupportedOSPlatform] attribute found
+ ///
+ private class PlatformAttributes
+ {
+ public Version? SupportedFirst { get; set; }
+ public Version? SupportedSecond { get; set; }
+ public Version? UnsupportedFirst { get; set; }
+ public Version? UnsupportedSecond { get; set; }
+ public bool HasAttribute() => SupportedFirst != null || UnsupportedFirst != null ||
+ SupportedSecond != null || UnsupportedSecond != null;
+ }
+ }
+}
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.OperationVisitor.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs
similarity index 56%
rename from src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.OperationVisitor.cs
rename to src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs
index 08b1b0b6dd..f757236e0c 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.OperationVisitor.cs
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs
@@ -7,7 +7,7 @@
namespace Microsoft.NetCore.Analyzers.InteropServices
{
- public sealed partial class RuntimePlatformCheckAnalyzer
+ public sealed partial class PlatformCompatabilityAnalyzer
{
private sealed class OperationVisitor : GlobalFlowStateDataFlowOperationVisitor
{
@@ -34,16 +34,39 @@ public override GlobalFlowStateAnalysisValueSet VisitInvocation_NonLambdaOrDeleg
{
var value = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(method, visitedInstance, visitedArguments, invokedAsDelegate, originalOperation, defaultValue);
- if (_platformCheckMethods.Contains(method.OriginalDefinition) &&
- !visitedArguments.IsEmpty)
+ if (_platformCheckMethods.Contains(method.OriginalDefinition))
{
- return RuntimeOSPlatformInfo.TryDecode(method, visitedArguments, DataFlowAnalysisContext.ValueContentAnalysisResult, _osPlatformType, out var platformInfo) ?
+ return PlatformMethodValue.TryDecode(method, visitedArguments, DataFlowAnalysisContext.ValueContentAnalysisResult, _osPlatformType, out var platformInfo) ?
new GlobalFlowStateAnalysisValueSet(platformInfo) :
GlobalFlowStateAnalysisValueSet.Unknown;
}
return GetValueOrDefault(value);
}
+
+ public override GlobalFlowStateAnalysisValueSet VisitPropertyReference(IPropertyReferenceOperation operation, object? argument)
+ {
+ return GetValueOrDefault(base.VisitPropertyReference(operation, argument));
+ }
+
+ public override GlobalFlowStateAnalysisValueSet VisitFieldReference(IFieldReferenceOperation operation, object? argument)
+ {
+ return GetValueOrDefault(base.VisitFieldReference(operation, argument));
+ }
+
+ public override GlobalFlowStateAnalysisValueSet VisitObjectCreation(IObjectCreationOperation operation, object? argument)
+ {
+ return GetValueOrDefault(base.VisitObjectCreation(operation, argument));
+ }
+
+ public override GlobalFlowStateAnalysisValueSet VisitEventReference(IEventReferenceOperation operation, object? argument)
+ {
+ return GetValueOrDefault(base.VisitEventReference(operation, argument));
+ }
+ public override GlobalFlowStateAnalysisValueSet VisitMethodReference(IMethodReferenceOperation operation, object? argument)
+ {
+ return GetValueOrDefault(base.VisitMethodReference(operation, argument));
+ }
}
}
}
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs
new file mode 100644
index 0000000000..9fd08d8390
--- /dev/null
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs
@@ -0,0 +1,242 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Analyzer.Utilities;
+using Analyzer.Utilities.Extensions;
+using Analyzer.Utilities.PooledObjects;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
+using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis;
+using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis;
+using Microsoft.CodeAnalysis.Operations;
+
+namespace Microsoft.NetCore.Analyzers.InteropServices
+{
+ using ValueContentAnalysisResult = DataFlowAnalysisResult;
+
+ public sealed partial class PlatformCompatabilityAnalyzer
+ {
+ private readonly struct PlatformMethodValue : IAbstractAnalysisValue, IEquatable
+ {
+ private PlatformMethodValue(string invokedPlatformCheckMethodName, string platformPropertyName, Version version, bool negated)
+ {
+ InvokedMethodName = invokedPlatformCheckMethodName ?? throw new ArgumentNullException(nameof(invokedPlatformCheckMethodName));
+ PlatformName = platformPropertyName ?? throw new ArgumentNullException(nameof(platformPropertyName));
+ Version = version ?? throw new ArgumentNullException(nameof(version));
+ Negated = negated;
+ }
+
+ public string InvokedMethodName { get; }
+ public string PlatformName { get; }
+ public Version Version { get; }
+ public bool Negated { get; }
+
+ public IAbstractAnalysisValue GetNegatedValue()
+ => new PlatformMethodValue(InvokedMethodName, PlatformName, Version, !Negated);
+
+ public static bool TryDecode(
+ IMethodSymbol invokedPlatformCheckMethod,
+ ImmutableArray arguments,
+ ValueContentAnalysisResult? valueContentAnalysisResult,
+ INamedTypeSymbol osPlatformType,
+ [NotNullWhen(returnValue: true)] out PlatformMethodValue? info)
+ {
+ // Accelerators like OperatingSystem.IsPlatformName()
+ if (arguments.IsEmpty)
+ {
+ if (TryExtractPlatformName(invokedPlatformCheckMethod.Name, out var platformName))
+ {
+ info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, platformName, new Version(0, 0), negated: false);
+ return true;
+ }
+ }
+ else
+ {
+ if (TryDecodeRuntimeInformationIsOSPlatform(arguments[0].Value, osPlatformType, out string? osPlatformName))
+ {
+ info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, osPlatformName, new Version(0, 0), negated: false);
+ return true;
+ }
+
+ if (arguments.GetArgumentForParameterAtIndex(0).Value is ILiteralOperation literal)
+ {
+ if (literal.Type?.SpecialType == SpecialType.System_String &&
+ literal.ConstantValue.HasValue)
+ {
+ // OperatingSystem.IsOSPlatform(string platform)
+ if (invokedPlatformCheckMethod.Name == IsOSPlatform &&
+ TryParsePlatformNameAndVersion(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version))
+ {
+ info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false);
+ return true;
+ }
+ else if (TryDecodeOSVersion(arguments, valueContentAnalysisResult, out version, 1))
+ {
+ // OperatingSystem.IsOSPlatformVersionAtLeast(string platform, int major, int minor = 0, int build = 0, int revision = 0)
+ Debug.Assert(invokedPlatformCheckMethod.Name == "IsOSPlatformVersionAtLeast");
+ info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, literal.ConstantValue.Value.ToString(), version, negated: false);
+ return true;
+ }
+ }
+ else if (literal.Type?.SpecialType == SpecialType.System_Int32)
+ {
+ // Accelerators like OperatingSystem.IsPlatformNameVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0)
+ if (TryExtractPlatformName(invokedPlatformCheckMethod.Name, out var platformName) &&
+ TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var version))
+ {
+ info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false);
+ return true;
+ }
+ }
+ }
+ }
+
+ info = default;
+ return false;
+ }
+
+ private static bool TryExtractPlatformName(string methodName, [NotNullWhen(true)] out string? platformName)
+ {
+ if (!methodName.StartsWith(IsPrefix, StringComparison.Ordinal))
+ {
+ platformName = null;
+ return false;
+ }
+
+ if (methodName.EndsWith(OptionalSuffix, StringComparison.Ordinal))
+ {
+ platformName = methodName.Substring(2, methodName.Length - 2 - OptionalSuffix.Length);
+ return true;
+ }
+
+ platformName = methodName.Substring(2);
+ return true;
+ }
+
+ private static bool TryDecodeRuntimeInformationIsOSPlatform(
+ IOperation argumentValue,
+ INamedTypeSymbol osPlatformType,
+ [NotNullWhen(returnValue: true)] out string? osPlatformName)
+ {
+ if ((argumentValue is IPropertyReferenceOperation propertyReference) &&
+ propertyReference.Property.ContainingType.Equals(osPlatformType))
+ {
+ osPlatformName = propertyReference.Property.Name;
+ return true;
+ }
+
+ osPlatformName = null;
+ return false;
+ }
+
+ private static bool TryDecodeOSVersion(
+ ImmutableArray arguments,
+ ValueContentAnalysisResult? valueContentAnalysisResult,
+ [NotNullWhen(returnValue: true)] out Version? osVersion,
+ int skip = 0)
+ {
+
+ using var versionBuilder = ArrayBuilder.GetInstance(4, fillWithValue: 0);
+ var index = 0;
+
+ foreach (var argument in arguments.GetArgumentsInParameterOrder().Skip(skip))
+ {
+ if (!TryDecodeOSVersionPart(argument, valueContentAnalysisResult, out var osVersionPart))
+ {
+ osVersion = null;
+ return false;
+ }
+
+ versionBuilder[index++] = osVersionPart;
+ }
+
+ osVersion = CreateVersion(versionBuilder);
+ return true;
+
+ static bool TryDecodeOSVersionPart(IArgumentOperation argument, ValueContentAnalysisResult? valueContentAnalysisResult, out int osVersionPart)
+ {
+ if (argument.Value.ConstantValue.HasValue &&
+ argument.Value.ConstantValue.Value is int versionPart)
+ {
+ osVersionPart = versionPart;
+ return true;
+ }
+
+ if (valueContentAnalysisResult != null)
+ {
+ var valueContentValue = valueContentAnalysisResult[argument.Value];
+ if (valueContentValue.IsLiteralState &&
+ valueContentValue.LiteralValues.Count == 1 &&
+ valueContentValue.LiteralValues.Single() is int part)
+ {
+ osVersionPart = part;
+ return true;
+ }
+ }
+
+ osVersionPart = default;
+ return false;
+ }
+
+ static Version CreateVersion(ArrayBuilder versionBuilder)
+ {
+ if (versionBuilder[3] == 0)
+ {
+ if (versionBuilder[2] == 0)
+ {
+ return new Version(versionBuilder[0], versionBuilder[1]);
+ }
+ else
+ {
+ return new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2]);
+ }
+ }
+ else
+ {
+ return new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2], versionBuilder[3]);
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ var result = $"{InvokedMethodName};{PlatformName};{Version}";
+ if (Negated)
+ {
+ result = $"!{result}";
+ }
+
+ return result;
+ }
+
+ public bool Equals(PlatformMethodValue other)
+ => InvokedMethodName.Equals(other.InvokedMethodName, StringComparison.OrdinalIgnoreCase) &&
+ PlatformName.Equals(other.PlatformName, StringComparison.OrdinalIgnoreCase) &&
+ Version.Equals(other.Version) &&
+ Negated == other.Negated;
+
+ public override bool Equals(object obj)
+ => obj is PlatformMethodValue otherInfo && Equals(otherInfo);
+
+ public override int GetHashCode()
+ => HashUtilities.Combine(InvokedMethodName.GetHashCode(), PlatformName.GetHashCode(), Version.GetHashCode(), Negated.GetHashCode());
+
+ bool IEquatable.Equals(IAbstractAnalysisValue other)
+ => other is PlatformMethodValue otherInfo && Equals(otherInfo);
+
+ public static bool operator ==(PlatformMethodValue left, PlatformMethodValue right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(PlatformMethodValue left, PlatformMethodValue right)
+ {
+ return !(left == right);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs
new file mode 100644
index 0000000000..d05b349d70
--- /dev/null
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs
@@ -0,0 +1,1020 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Threading;
+using Analyzer.Utilities;
+using Analyzer.Utilities.Extensions;
+using Analyzer.Utilities.PooledObjects;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
+using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis;
+using Microsoft.CodeAnalysis.Operations;
+
+namespace Microsoft.NetCore.Analyzers.InteropServices
+{
+ ///
+ /// CA1416: Analyzer that informs developers when they use platform-specific APIs from call sites where the API might not be available
+ ///
+ /// It finds usage of platform-specific or unsupported APIs and diagnoses if the
+ /// API is guarded by platform check or if it is annotated with corresponding platform specific attribute.
+ /// If using the platform-specific API is not safe it reports diagnostics.
+ ///
+ ///
+ [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
+ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer
+ {
+ internal const string RuleId = "CA1416";
+ private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(SupportedOSPlatformAttribute, UnsupportedOSPlatformAttribute);
+
+ private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
+ private static readonly LocalizableString s_localizableSupportedOsMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckSupportedOsMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
+ private static readonly LocalizableString s_localizableSupportedOsVersionMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckSupportedOsVersionMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
+ private static readonly LocalizableString s_localizableUnsupportedOsMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckUnsupportedOsMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
+ private static readonly LocalizableString s_localizableUnsupportedOsVersionMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckUnsupportedOsVersionMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
+ private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckDescription), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
+
+ // We are adding the new attributes into older versions of .Net 5.0, so there could be multiple referenced assemblies each with their own
+ // version of internal attribute type which will cause ambiguity, to avoid that we are comparing the attributes by their name
+ private const string SupportedOSPlatformAttribute = nameof(SupportedOSPlatformAttribute);
+ private const string UnsupportedOSPlatformAttribute = nameof(UnsupportedOSPlatformAttribute);
+
+ // Platform guard method name, prefix, suffix
+ private const string IsOSPlatform = nameof(IsOSPlatform);
+ private const string IsPrefix = "Is";
+ private const string OptionalSuffix = "VersionAtLeast";
+
+ internal static DiagnosticDescriptor SupportedOsVersionRule = DiagnosticDescriptorHelper.Create(RuleId,
+ s_localizableTitle,
+ s_localizableSupportedOsVersionMessage,
+ DiagnosticCategory.Interoperability,
+ RuleLevel.BuildWarning,
+ description: s_localizableDescription,
+ isPortedFxCopRule: false,
+ isDataflowRule: false);
+
+ internal static DiagnosticDescriptor SupportedOsRule = DiagnosticDescriptorHelper.Create(RuleId,
+ s_localizableTitle,
+ s_localizableSupportedOsMessage,
+ DiagnosticCategory.Interoperability,
+ RuleLevel.BuildWarning,
+ description: s_localizableDescription,
+ isPortedFxCopRule: false,
+ isDataflowRule: false);
+
+ internal static DiagnosticDescriptor UnsupportedOsRule = DiagnosticDescriptorHelper.Create(RuleId,
+ s_localizableTitle,
+ s_localizableUnsupportedOsMessage,
+ DiagnosticCategory.Interoperability,
+ RuleLevel.BuildWarning,
+ description: s_localizableDescription,
+ isPortedFxCopRule: false,
+ isDataflowRule: false);
+
+ internal static DiagnosticDescriptor UnsupportedOsVersionRule = DiagnosticDescriptorHelper.Create(RuleId,
+ s_localizableTitle,
+ s_localizableUnsupportedOsVersionMessage,
+ DiagnosticCategory.Interoperability,
+ RuleLevel.BuildWarning,
+ description: s_localizableDescription,
+ isPortedFxCopRule: false,
+ isDataflowRule: false);
+
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(
+ SupportedOsRule, SupportedOsVersionRule, UnsupportedOsRule, UnsupportedOsVersionRule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.EnableConcurrentExecution();
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
+
+ context.RegisterCompilationStartAction(context =>
+ {
+ var typeName = WellKnownTypeNames.SystemOperatingSystem;
+
+ if (!context.Compilation.TryGetOrCreateTypeByMetadataName(typeName + "Helper", out var operatingSystemType))
+ {
+ // TODO: remove 'typeName + "Helper"' after tests able to consume the real new APIs
+ operatingSystemType = context.Compilation.GetOrCreateTypeByMetadataName(typeName);
+ }
+ if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesOSPlatform, out var osPlatformType) ||
+ !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesRuntimeInformation, out var runtimeInformationType))
+ {
+ return;
+ }
+
+ var msBuildPlatforms = GetSupportedPlatforms(context.Options, context.Compilation, context.CancellationToken);
+ var runtimeIsOSPlatformMethod = runtimeInformationType.GetMembers().OfType().Where(m =>
+ IsOSPlatform == m.Name &&
+ m.IsStatic &&
+ m.ReturnType.SpecialType == SpecialType.System_Boolean &&
+ m.Parameters.Length == 1 &&
+ m.Parameters[0].Type.Equals(osPlatformType)).FirstOrDefault();
+
+ var guardMethods = GetOperatingSystemGuardMethods(runtimeIsOSPlatformMethod, operatingSystemType!);
+ var platformSpecificMembers = new ConcurrentDictionary?>();
+
+ context.RegisterOperationBlockStartAction(context => AnalyzeOperationBlock(context, guardMethods, osPlatformType, platformSpecificMembers, msBuildPlatforms));
+ });
+
+ static ImmutableArray GetOperatingSystemGuardMethods(IMethodSymbol? runtimeIsOSPlatformMethod, INamedTypeSymbol operatingSystemType)
+ {
+ var methods = operatingSystemType.GetMembers().OfType().Where(m =>
+ m.IsStatic &&
+ m.ReturnType.SpecialType == SpecialType.System_Boolean &&
+ (IsOSPlatform == m.Name) || NameAndParametersValid(m)).
+ ToImmutableArray();
+
+ if (runtimeIsOSPlatformMethod != null)
+ {
+ return methods.Add(runtimeIsOSPlatformMethod);
+ }
+ return methods;
+ }
+
+ static ImmutableArray GetSupportedPlatforms(AnalyzerOptions options, Compilation compilation, CancellationToken cancellationToken) =>
+ options.GetMSBuildItemMetadataValues(MSBuildItemOptionNames.SupportedPlatform, compilation, cancellationToken);
+
+ static bool NameAndParametersValid(IMethodSymbol method) => method.Name.StartsWith(IsPrefix, StringComparison.Ordinal) &&
+ (method.Parameters.Length == 0 || method.Name.EndsWith(OptionalSuffix, StringComparison.Ordinal));
+ }
+
+ private void AnalyzeOperationBlock(
+ OperationBlockStartAnalysisContext context,
+ ImmutableArray guardMethods,
+ INamedTypeSymbol osPlatformType,
+ ConcurrentDictionary?> platformSpecificMembers,
+ ImmutableArray msBuildPlatforms)
+ {
+ var platformSpecificOperations = PooledConcurrentDictionary>.GetInstance();
+
+ context.RegisterOperationAction(context =>
+ {
+ AnalyzeOperation(context.Operation, context, platformSpecificOperations, platformSpecificMembers, msBuildPlatforms);
+ },
+ OperationKind.MethodReference,
+ OperationKind.EventReference,
+ OperationKind.FieldReference,
+ OperationKind.Invocation,
+ OperationKind.ObjectCreation,
+ OperationKind.PropertyReference);
+
+ context.RegisterOperationBlockEndAction(context =>
+ {
+ try
+ {
+ if (platformSpecificOperations.IsEmpty)
+ {
+ return;
+ }
+
+ if (guardMethods.IsEmpty || !(context.OperationBlocks.GetControlFlowGraph(out var topmostBlock) is { } cfg))
+ {
+ ReportDiagnosticsForAll(platformSpecificOperations, context);
+ return;
+ }
+
+ var performValueContentAnalysis = ComputeNeedsValueContentAnalysis(topmostBlock!, guardMethods);
+ var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation);
+ var analysisResult = GlobalFlowStateAnalysis.TryGetOrComputeResult(
+ cfg, context.OwningSymbol, CreateOperationVisitor, wellKnownTypeProvider,
+ context.Options, SupportedOsRule, performValueContentAnalysis,
+ context.CancellationToken, out var valueContentAnalysisResult);
+
+ if (analysisResult == null)
+ {
+ return;
+ }
+
+ foreach (var (platformSpecificOperation, attributes) in platformSpecificOperations)
+ {
+ var value = analysisResult[platformSpecificOperation.Kind, platformSpecificOperation.Syntax];
+
+ if ((value.Kind == GlobalFlowStateAnalysisValueSetKind.Known && IsKnownValueGuarded(attributes, value)) ||
+ (value.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown && HasInterproceduralResult(platformSpecificOperation, attributes, analysisResult)))
+ {
+ continue;
+ }
+
+ ReportDiagnostics(platformSpecificOperation, attributes, context);
+ }
+ }
+ finally
+ {
+ // Workaround for https://github.com/dotnet/roslyn/issues/46859
+ // Do not free in presence of cancellation.
+ if (!context.CancellationToken.IsCancellationRequested)
+ {
+ platformSpecificOperations.Free();
+ }
+ }
+
+ return;
+
+ OperationVisitor CreateOperationVisitor(GlobalFlowStateAnalysisContext context) => new OperationVisitor(guardMethods, osPlatformType, context);
+ });
+ }
+
+ private static bool HasInterproceduralResult(IOperation platformSpecificOperation, SmallDictionary attributes,
+ DataFlowAnalysisResult analysisResult)
+ {
+ if (platformSpecificOperation.IsWithinLambdaOrLocalFunction())
+ {
+ var results = analysisResult.TryGetInterproceduralResults();
+ if (results != null)
+ {
+ foreach (var localResult in results)
+ {
+ var localValue = localResult[platformSpecificOperation.Kind, platformSpecificOperation.Syntax];
+ if (localValue.Kind == GlobalFlowStateAnalysisValueSetKind.Known && IsKnownValueGuarded(attributes, localValue))
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static bool ComputeNeedsValueContentAnalysis(IBlockOperation operationBlock, ImmutableArray guardMethods)
+ {
+ foreach (var operation in operationBlock.Descendants())
+ {
+ if (operation is IInvocationOperation invocation &&
+ guardMethods.Contains(invocation.TargetMethod))
+ {
+ // Check if any integral parameter to guard method invocation has non-constant value.
+ foreach (var argument in invocation.Arguments)
+ {
+ if (argument.Parameter.Type.SpecialType == SpecialType.System_Int32 &&
+ !argument.Value.ConstantValue.HasValue)
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static bool IsKnownValueGuarded(SmallDictionary attributes, GlobalFlowStateAnalysisValueSet value)
+ {
+ using var capturedVersions = PooledDictionary.GetInstance(StringComparer.OrdinalIgnoreCase);
+ return IsKnownValueGuarded(attributes, value, capturedVersions);
+
+ static bool IsKnownValueGuarded(
+ SmallDictionary attributes,
+ GlobalFlowStateAnalysisValueSet value,
+ PooledDictionary capturedVersions)
+ {
+ // 'GlobalFlowStateAnalysisValueSet.AnalysisValues' represent the && of values.
+ foreach (var analysisValue in value.AnalysisValues)
+ {
+ if (analysisValue is PlatformMethodValue info)
+ {
+ if (attributes.TryGetValue(info.PlatformName, out var attribute))
+ {
+ if (info.Negated)
+ {
+ if (attribute.UnsupportedFirst != null)
+ {
+ if (attribute.UnsupportedFirst >= info.Version)
+ {
+ if (DenyList(attribute))
+ {
+ attribute.SupportedFirst = null;
+ attribute.SupportedSecond = null;
+ attribute.UnsupportedSecond = null;
+ }
+ attribute.UnsupportedFirst = null;
+ }
+ }
+
+ if (attribute.UnsupportedSecond != null)
+ {
+ if (attribute.UnsupportedSecond <= info.Version)
+ {
+ attribute.UnsupportedSecond = null;
+ }
+ }
+
+ if (!IsEmptyVersion(info.Version))
+ {
+ capturedVersions[info.PlatformName] = info.Version;
+ }
+ }
+ else
+ {
+ if (capturedVersions.Any())
+ {
+ if (attribute.UnsupportedFirst != null &&
+ capturedVersions.TryGetValue(info.PlatformName, out var version) &&
+ attribute.UnsupportedFirst >= version)
+ {
+ attribute.UnsupportedFirst = null;
+ }
+
+ if (attribute.UnsupportedSecond != null &&
+ capturedVersions.TryGetValue(info.PlatformName, out version) &&
+ attribute.UnsupportedSecond <= version)
+ {
+ attribute.UnsupportedSecond = null;
+ }
+ }
+
+ if (attribute.SupportedFirst != null &&
+ attribute.SupportedFirst <= info.Version)
+ {
+ attribute.SupportedFirst = null;
+ RemoveUnsupportedWithLessVersion(info.Version, attribute);
+ RemoveOtherSupportsOnDifferentPlatforms(attributes, info.PlatformName);
+ }
+
+ if (attribute.SupportedSecond != null &&
+ attribute.SupportedSecond <= info.Version)
+ {
+ attribute.SupportedSecond = null;
+ RemoveUnsupportedWithLessVersion(info.Version, attribute);
+ RemoveOtherSupportsOnDifferentPlatforms(attributes, info.PlatformName);
+ }
+
+ RemoveUnsupportsOnDifferentPlatforms(attributes, info.PlatformName);
+ }
+ }
+ else
+ {
+ if (!info.Negated)
+ {
+ // it is checking one exact platform, other unsupported should be suppressed
+ RemoveUnsupportsOnDifferentPlatforms(attributes, info.PlatformName);
+ }
+ }
+ }
+ }
+
+ if (value.Parents.IsEmpty)
+ {
+ foreach (var attribute in attributes)
+ {
+ // if any of the attributes is not suppressed
+ if (attribute.Value.HasAttribute())
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // 'GlobalFlowStateAnalysisValueSet.Parents' represent || of values on different flow paths.
+ // We are guarded only if values are guarded on *all flow paths**.
+ foreach (var parent in value.Parents)
+ {
+ // NOTE: IsKnownValueGuarded mutates the input values, so we pass in cloned values
+ // to ensure that evaluation of each part of || is independent of evaluation of other parts.
+ var parentAttributes = CopyAttributes(attributes);
+ using var parentCapturedVersions = PooledDictionary.GetInstance(capturedVersions);
+
+ if (!IsKnownValueGuarded(parentAttributes, parent, parentCapturedVersions))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ static void RemoveUnsupportsOnDifferentPlatforms(SmallDictionary attributes, string platformName)
+ {
+ foreach (var (name, attribute) in attributes)
+ {
+ if (!name.Equals(platformName, StringComparison.OrdinalIgnoreCase) &&
+ DenyList(attribute))
+ {
+ attribute.UnsupportedFirst = null;
+ attribute.UnsupportedSecond = null;
+ attribute.SupportedFirst = null;
+ attribute.SupportedSecond = null;
+ }
+ }
+ }
+
+ static void RemoveUnsupportedWithLessVersion(Version supportedVersion, PlatformAttributes attribute)
+ {
+ if (attribute.UnsupportedFirst != null &&
+ attribute.UnsupportedFirst <= supportedVersion)
+ {
+ attribute.UnsupportedFirst = null;
+ }
+ }
+
+ static void RemoveOtherSupportsOnDifferentPlatforms(SmallDictionary attributes, string platformName)
+ {
+ foreach (var (name, attribute) in attributes)
+ {
+ if (!name.Equals(platformName, StringComparison.OrdinalIgnoreCase))
+ {
+ attribute.SupportedFirst = null;
+ attribute.SupportedSecond = null;
+ }
+ }
+ }
+ }
+
+ private static bool IsEmptyVersion(Version version) => version.Major == 0 && version.Minor == 0;
+
+ private static void ReportDiagnosticsForAll(PooledConcurrentDictionary> platformSpecificOperations, OperationBlockAnalysisContext context)
+ {
+ foreach (var platformSpecificOperation in platformSpecificOperations)
+ {
+ ReportDiagnostics(platformSpecificOperation.Key, platformSpecificOperation.Value, context);
+ }
+ }
+
+ private static void ReportDiagnostics(IOperation operation, SmallDictionary attributes, OperationBlockAnalysisContext context)
+ {
+ var symbol = operation is IObjectCreationOperation creation ? creation.Constructor.ContainingType : GetOperationSymbol(operation);
+
+ if (symbol == null)
+ {
+ return;
+ }
+
+ var operationName = symbol.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat);
+
+ foreach (var platformName in attributes.Keys)
+ {
+ var attribute = attributes[platformName];
+
+ if (attribute.SupportedSecond != null)
+ {
+ ReportSupportedDiagnostic(operation, context, operationName, platformName, VersionToString(attribute.SupportedSecond));
+ }
+ else if (attribute.SupportedFirst != null)
+ {
+ ReportSupportedDiagnostic(operation, context, operationName, platformName, VersionToString(attribute.SupportedFirst));
+ }
+
+ if (attribute.UnsupportedFirst != null)
+ {
+ ReportUnsupportedDiagnostic(operation, context, operationName, platformName, VersionToString(attribute.UnsupportedFirst));
+ }
+ else if (attribute.UnsupportedSecond != null)
+ {
+ ReportUnsupportedDiagnostic(operation, context, operationName, platformName, VersionToString(attribute.UnsupportedSecond));
+ }
+ }
+
+ static void ReportSupportedDiagnostic(IOperation operation, OperationBlockAnalysisContext context, string name, string platformName, string? version = null) =>
+ context.ReportDiagnostic(version == null ? operation.CreateDiagnostic(SupportedOsRule, name, platformName) :
+ operation.CreateDiagnostic(SupportedOsVersionRule, name, platformName, version));
+
+ static void ReportUnsupportedDiagnostic(IOperation operation, OperationBlockAnalysisContext context, string name, string platformName, string? version = null) =>
+ context.ReportDiagnostic(version == null ? operation.CreateDiagnostic(UnsupportedOsRule, name, platformName) :
+ operation.CreateDiagnostic(UnsupportedOsVersionRule, name, platformName, version));
+ }
+
+ private static string? VersionToString(Version version) => IsEmptyVersion(version) ? null : version.ToString();
+
+ private static ISymbol? GetOperationSymbol(IOperation operation)
+ => operation switch
+ {
+ IInvocationOperation iOperation => iOperation.TargetMethod,
+ IObjectCreationOperation cOperation => cOperation.Constructor,
+ IFieldReferenceOperation fOperation => IsWithinConditionalOperation(fOperation) ? null : fOperation.Field,
+ IMemberReferenceOperation mOperation => mOperation.Member,
+ _ => null,
+ };
+
+ private static void AnalyzeOperation(IOperation operation, OperationAnalysisContext context,
+ PooledConcurrentDictionary> platformSpecificOperations,
+ ConcurrentDictionary?> platformSpecificMembers, ImmutableArray msBuildPlatforms)
+ {
+ var symbol = GetOperationSymbol(operation);
+
+ if (symbol == null)
+ {
+ return;
+ }
+
+ if (TryGetOrCreatePlatformAttributes(symbol, platformSpecificMembers, out var operationAttributes))
+ {
+ if (TryGetOrCreatePlatformAttributes(context.ContainingSymbol, platformSpecificMembers, out var callSiteAttributes))
+ {
+ if (IsNotSuppressedByCallSite(operationAttributes, callSiteAttributes, msBuildPlatforms, out var notSuppressedAttributes))
+ {
+ platformSpecificOperations.TryAdd(operation, notSuppressedAttributes);
+ }
+ }
+ else
+ {
+ if (TryCopyAttributesNotSuppressedByMsBuild(operationAttributes, msBuildPlatforms, out var copiedAttributes))
+ {
+ platformSpecificOperations.TryAdd(operation, copiedAttributes);
+ }
+ }
+ }
+ }
+
+ private static bool TryCopyAttributesNotSuppressedByMsBuild(SmallDictionary operationAttributes,
+ ImmutableArray msBuildPlatforms, out SmallDictionary copiedAttributes)
+ {
+ copiedAttributes = new SmallDictionary(StringComparer.OrdinalIgnoreCase);
+ foreach (var (platformName, attributes) in operationAttributes)
+ {
+ if (AllowList(attributes) || msBuildPlatforms.IndexOf(platformName, 0, StringComparer.OrdinalIgnoreCase) != -1)
+ {
+ copiedAttributes.Add(platformName, CopyAllAttributes(new PlatformAttributes(), attributes));
+ }
+ }
+
+ return copiedAttributes.Any();
+ }
+
+ private static SmallDictionary CopyAttributes(SmallDictionary copyAttributes)
+ {
+ var copy = new SmallDictionary(StringComparer.OrdinalIgnoreCase);
+ foreach (var (platformName, attributes) in copyAttributes)
+ {
+ copy.Add(platformName, CopyAllAttributes(new PlatformAttributes(), attributes));
+ }
+
+ return copy;
+ }
+
+ ///
+ /// The semantics of the platform specific attributes are :
+ /// - 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], the analyzer will produce a warning on the API definition because the API is attributed inconsistently.
+ /// - 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.
+ ///
+ /// Platform specific attributes applied to the invoked member
+ /// Platform specific attributes applied to the call site where the member invoked
+ /// true if all attributes applied to the operation is suppressed, false otherwise
+
+ private static bool IsNotSuppressedByCallSite(SmallDictionary operationAttributes,
+ SmallDictionary callSiteAttributes, ImmutableArray msBuildPlatforms,
+ out SmallDictionary notSuppressedAttributes)
+ {
+ notSuppressedAttributes = new SmallDictionary(StringComparer.OrdinalIgnoreCase);
+ bool? supportedOnlyList = null;
+ bool mandatoryMatchFound = false;
+ using var supportedOnlyPlatforms = PooledHashSet.GetInstance(StringComparer.OrdinalIgnoreCase);
+ foreach (var (platformName, attribute) in operationAttributes)
+ {
+ var diagnosticAttribute = new PlatformAttributes();
+
+ if (attribute.SupportedFirst != null)
+ {
+ if (attribute.UnsupportedFirst == null || attribute.UnsupportedFirst > attribute.SupportedFirst)
+ {
+ // If only supported for current platform
+ if (supportedOnlyList.HasValue && !supportedOnlyList.Value)
+ {
+ return true; // invalid state, do not need to add this API to the list
+ }
+
+ supportedOnlyPlatforms.Add(platformName);
+ supportedOnlyList = true;
+
+ if (callSiteAttributes.TryGetValue(platformName, out var callSiteAttribute))
+ {
+ var attributeToCheck = attribute.SupportedSecond ?? attribute.SupportedFirst;
+ if (MandatoryOsVersionsSuppressed(callSiteAttribute, attributeToCheck))
+ {
+ mandatoryMatchFound = true;
+ }
+ else
+ {
+ diagnosticAttribute.SupportedSecond = (Version)attributeToCheck.Clone();
+ }
+
+ if (attribute.UnsupportedFirst != null &&
+ !(SuppressedByCallSiteSupported(attribute, callSiteAttribute.SupportedFirst) ||
+ SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst)))
+ {
+ diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone();
+ }
+ }
+ }
+ else if (attribute.UnsupportedFirst != null) // also means Unsupported <= Supported, optional list
+ {
+ if (supportedOnlyList.HasValue && supportedOnlyList.Value)
+ {
+ return true; // do not need to add this API to the list
+ }
+
+ supportedOnlyList = false;
+
+ if (callSiteAttributes.TryGetValue(platformName, out var callSiteAttribute))
+ {
+ if (callSiteAttribute.SupportedFirst != null)
+ {
+ if (!OptionalOsSupportSuppressed(callSiteAttribute, attribute))
+ {
+ diagnosticAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone();
+ }
+
+ if (!UnsupportedFirstSuppressed(attribute, callSiteAttribute))
+ {
+ diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone();
+ }
+
+ if (attribute.UnsupportedSecond != null &&
+ !UnsupportedSecondSuppressed(attribute, callSiteAttribute))
+ {
+ diagnosticAttribute.UnsupportedSecond = (Version)attribute.UnsupportedSecond.Clone();
+ }
+ }
+ }
+ else
+ {
+ // Call site has no attributes for this platform, check if MsBuild list has it,
+ // then if call site has deny list, it should support its later support
+ if (msBuildPlatforms.Contains(platformName) &&
+ callSiteAttributes.Any(ca => DenyList(ca.Value)))
+ {
+ diagnosticAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone();
+ }
+ }
+ }
+ }
+ else
+ {
+ if (supportedOnlyList.HasValue && supportedOnlyList.Value)
+ {
+ return true; // do not need to add this API to the list
+ }
+
+ supportedOnlyList = false;
+
+ if (attribute.UnsupportedFirst != null) // Unsupported for this but supported all other
+ {
+ if (callSiteAttributes.TryGetValue(platformName, out var callSiteAttribute))
+ {
+ if (callSiteAttribute.SupportedFirst != null)
+ {
+ if (!SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst))
+ {
+ diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone();
+ }
+
+ if (attribute.UnsupportedSecond != null &&
+ !SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedSecond))
+ {
+ diagnosticAttribute.UnsupportedSecond = (Version)attribute.UnsupportedSecond.Clone();
+ }
+ }
+ }
+ else if (msBuildPlatforms.Contains(platformName) &&
+ !callSiteAttributes.Values.Any(v => v.SupportedFirst != null))
+ {
+ // if MsBuild list contain the platform and call site has no any other supported attribute it means global, so need to warn
+ diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone();
+ }
+ }
+ }
+
+ if (diagnosticAttribute.HasAttribute())
+ {
+ notSuppressedAttributes[platformName] = diagnosticAttribute;
+ }
+ }
+
+ if (supportedOnlyList.HasValue && supportedOnlyList.Value)
+ {
+ if (!mandatoryMatchFound)
+ {
+ foreach (var (name, attributes) in operationAttributes)
+ {
+ if (attributes.SupportedFirst != null)
+ {
+ if (!notSuppressedAttributes.TryGetValue(name, out var diagnosticAttribute))
+ {
+ diagnosticAttribute = new PlatformAttributes();
+ }
+ CopyAllAttributes(diagnosticAttribute, attributes);
+ notSuppressedAttributes[name] = diagnosticAttribute;
+ }
+ }
+ }
+
+ // if supportedOnlyList then call site should not have any platform not listed in the support list
+ foreach (var (platform, csAttributes) in callSiteAttributes)
+ {
+ if (csAttributes.SupportedFirst != null &&
+ !supportedOnlyPlatforms.Contains(platform))
+ {
+ foreach (var (name, version) in operationAttributes)
+ {
+ AddOrUpdatedDiagnostic(operationAttributes[name], notSuppressedAttributes, name);
+ }
+ }
+ }
+ }
+ return notSuppressedAttributes.Any();
+
+ static void AddOrUpdatedDiagnostic(PlatformAttributes operationAttributes,
+ SmallDictionary notSuppressedAttributes, string name)
+ {
+ if (operationAttributes.SupportedFirst != null)
+ {
+ if (!notSuppressedAttributes.TryGetValue(name, out var diagnosticAttribute))
+ {
+ diagnosticAttribute = new PlatformAttributes();
+ }
+ diagnosticAttribute.SupportedFirst = (Version)operationAttributes.SupportedFirst.Clone();
+ notSuppressedAttributes[name] = diagnosticAttribute;
+ }
+ }
+
+ static bool UnsupportedSecondSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) =>
+ SuppressedByCallSiteSupported(attribute, callSiteAttribute.SupportedFirst) ||
+ SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedSecond!);
+
+ static bool SuppressedByCallSiteUnsupported(PlatformAttributes callSiteAttribute, Version unsupporteAttribute) =>
+ callSiteAttribute.UnsupportedFirst != null && unsupporteAttribute >= callSiteAttribute.UnsupportedFirst ||
+ callSiteAttribute.UnsupportedSecond != null && unsupporteAttribute >= callSiteAttribute.UnsupportedSecond;
+
+ static bool SuppressedByCallSiteSupported(PlatformAttributes attribute, Version? callSiteSupportedFirst) =>
+ callSiteSupportedFirst != null && callSiteSupportedFirst >= attribute.SupportedFirst! &&
+ attribute.SupportedSecond != null && callSiteSupportedFirst >= attribute.SupportedSecond;
+
+ static bool UnsupportedFirstSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) =>
+ callSiteAttribute.SupportedFirst != null && callSiteAttribute.SupportedFirst >= attribute.SupportedFirst ||
+ SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst!);
+
+ // As optianal if call site supports that platform, their versions should match
+ static bool OptionalOsSupportSuppressed(PlatformAttributes callSiteAttribute, PlatformAttributes attribute) =>
+ (callSiteAttribute.SupportedFirst == null || attribute.SupportedFirst <= callSiteAttribute.SupportedFirst) &&
+ (callSiteAttribute.SupportedSecond == null || attribute.SupportedFirst <= callSiteAttribute.SupportedSecond);
+
+ static bool MandatoryOsVersionsSuppressed(PlatformAttributes callSitePlatforms, Version checkingVersion) =>
+ callSitePlatforms.SupportedFirst != null && checkingVersion <= callSitePlatforms.SupportedFirst ||
+ callSitePlatforms.SupportedSecond != null && checkingVersion <= callSitePlatforms.SupportedSecond;
+ }
+
+ private static PlatformAttributes CopyAllAttributes(PlatformAttributes copyTo, PlatformAttributes copyFrom)
+ {
+ copyTo.SupportedFirst = (Version?)copyFrom.SupportedFirst?.Clone();
+ copyTo.SupportedSecond = (Version?)copyFrom.SupportedSecond?.Clone();
+ copyTo.UnsupportedFirst = (Version?)copyFrom.UnsupportedFirst?.Clone();
+ copyTo.UnsupportedSecond = (Version?)copyFrom.UnsupportedSecond?.Clone();
+ return copyTo;
+ }
+
+ // Do not warn if platform specific enum/field value is used in conditional check, like: 'if (value == FooEnum.WindowsOnlyValue)'
+ private static bool IsWithinConditionalOperation(IFieldReferenceOperation pOperation) =>
+ pOperation.ConstantValue.HasValue &&
+ pOperation.Parent is IBinaryOperation bo &&
+ (bo.OperatorKind == BinaryOperatorKind.Equals ||
+ bo.OperatorKind == BinaryOperatorKind.NotEquals ||
+ bo.OperatorKind == BinaryOperatorKind.GreaterThan ||
+ bo.OperatorKind == BinaryOperatorKind.LessThan ||
+ bo.OperatorKind == BinaryOperatorKind.GreaterThanOrEqual ||
+ bo.OperatorKind == BinaryOperatorKind.LessThanOrEqual);
+
+ private static bool TryGetOrCreatePlatformAttributes(
+ ISymbol symbol,
+ ConcurrentDictionary?> platformSpecificMembers,
+ [NotNullWhen(true)] out SmallDictionary? attributes)
+ {
+ if (!platformSpecificMembers.TryGetValue(symbol, out attributes))
+ {
+ var container = symbol.ContainingSymbol;
+
+ // Namespaces do not have attributes
+ while (container is INamespaceSymbol)
+ {
+ container = container.ContainingSymbol;
+ }
+
+ if (container != null &&
+ TryGetOrCreatePlatformAttributes(container, platformSpecificMembers, out var containerAttributes))
+ {
+ attributes = CopyAttributes(containerAttributes);
+ }
+
+ AddPlatformAttributes(symbol.GetAttributes(), ref attributes);
+
+ attributes = platformSpecificMembers.GetOrAdd(symbol, attributes);
+ }
+
+ return attributes != null;
+
+ static bool AddPlatformAttributes(ImmutableArray immediateAttributes, [NotNullWhen(true)] ref SmallDictionary? attributes)
+ {
+ foreach (AttributeData attribute in immediateAttributes)
+ {
+ if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name))
+ {
+ TryAddValidAttribute(ref attributes, attribute);
+ }
+ }
+ return attributes != null;
+ }
+ }
+
+ private static bool TryAddValidAttribute([NotNullWhen(true)] ref SmallDictionary? attributes, AttributeData attribute)
+ {
+ if (!attribute.ConstructorArguments.IsEmpty &&
+ attribute.ConstructorArguments[0] is { } argument &&
+ argument.Kind == TypedConstantKind.Primitive &&
+ argument.Type.SpecialType == SpecialType.System_String &&
+ !argument.IsNull &&
+ !argument.Value.Equals(string.Empty) &&
+ TryParsePlatformNameAndVersion(argument.Value.ToString(), out string platformName, out Version? version))
+ {
+ attributes ??= new SmallDictionary(StringComparer.OrdinalIgnoreCase);
+
+ if (!attributes.TryGetValue(platformName, out var _))
+ {
+ attributes[platformName] = new PlatformAttributes();
+ }
+
+ AddAttribute(attribute.AttributeClass.Name, version, attributes, platformName);
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool TryParsePlatformNameAndVersion(string osString, out string osPlatformName, [NotNullWhen(true)] out Version? version)
+ {
+ version = null;
+ osPlatformName = string.Empty;
+ for (int i = 0; i < osString.Length; i++)
+ {
+ if (char.IsDigit(osString[i]))
+ {
+ if (i > 0 && Version.TryParse(osString.Substring(i), out Version? parsedVersion))
+ {
+ osPlatformName = osString.Substring(0, i);
+ version = parsedVersion;
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ osPlatformName = osString;
+ version = new Version(0, 0);
+ return true;
+ }
+
+ private static void AddAttribute(string name, Version version, SmallDictionary existingAttributes, string platformName)
+ {
+ if (name == SupportedOSPlatformAttribute)
+ {
+ AddOrUpdateSupportedAttribute(existingAttributes[platformName], version);
+ }
+ else
+ {
+ Debug.Assert(name == UnsupportedOSPlatformAttribute);
+ AddOrUpdateUnsupportedAttribute(platformName, existingAttributes[platformName], version, existingAttributes);
+ }
+
+ static void AddOrUpdateUnsupportedAttribute(string name, PlatformAttributes attributes,
+ Version version, SmallDictionary existingAttributes)
+ {
+ if (attributes.UnsupportedFirst != null)
+ {
+ if (attributes.UnsupportedFirst > version)
+ {
+ if (attributes.UnsupportedSecond != null)
+ {
+ if (attributes.UnsupportedSecond > attributes.UnsupportedFirst)
+ {
+ attributes.UnsupportedSecond = attributes.UnsupportedFirst;
+ }
+ }
+ else
+ {
+ attributes.UnsupportedSecond = attributes.UnsupportedFirst;
+ }
+
+ attributes.UnsupportedFirst = version;
+ }
+ else
+ {
+ if (attributes.UnsupportedSecond != null)
+ {
+ if (attributes.UnsupportedSecond > version)
+ {
+ attributes.UnsupportedSecond = version;
+ }
+ }
+ else
+ {
+ if (attributes.SupportedFirst != null && attributes.SupportedFirst < version)
+ {
+ // We should ignore second attribute in case like [UnsupportedOSPlatform(""windows""),
+ // [UnsupportedOSPlatform(""windows11.0"")] which doesn't have supported in between
+ attributes.UnsupportedSecond = version;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (attributes.SupportedFirst != null && attributes.SupportedFirst >= version)
+ {
+ // Override needed
+ if (attributes.SupportedSecond != null)
+ {
+ attributes.SupportedFirst = attributes.SupportedSecond;
+ attributes.SupportedSecond = null;
+ }
+ else
+ {
+ attributes.SupportedFirst = null;
+ }
+ if (!HasAnySupportedOnlyAttribute(name, existingAttributes))
+ {
+ attributes.UnsupportedFirst = version;
+ }
+ }
+ else
+ {
+ attributes.UnsupportedFirst = version;
+ }
+ }
+
+ static bool HasAnySupportedOnlyAttribute(string name, SmallDictionary existingAttributes) =>
+ existingAttributes.Any(a => !a.Key.Equals(name, StringComparison.OrdinalIgnoreCase) &&
+ AllowList(a.Value));
+ }
+
+ static void AddOrUpdateSupportedAttribute(PlatformAttributes attributes, Version version)
+ {
+ if (attributes.SupportedFirst != null)
+ {
+ if (attributes.SupportedFirst > version)
+ {
+ if (attributes.SupportedSecond != null)
+ {
+ if (attributes.SupportedSecond < attributes.SupportedFirst)
+ {
+ attributes.SupportedSecond = attributes.SupportedFirst;
+ }
+ }
+ else
+ {
+ attributes.SupportedSecond = attributes.SupportedFirst;
+ }
+
+ attributes.SupportedFirst = version;
+ }
+ else
+ {
+ if (attributes.SupportedSecond != null)
+ {
+ if (attributes.SupportedSecond < version)
+ {
+ attributes.SupportedSecond = version;
+ }
+ }
+ else
+ {
+ attributes.SupportedSecond = version;
+ }
+ }
+ }
+ else
+ {
+ attributes.SupportedFirst = version;
+ }
+ }
+ }
+
+ ///
+ /// Determines if the attributes supported only for the platform (allow list)
+ ///
+ /// PlatformAttributes being checked
+ /// true if it is allow list
+ private static bool AllowList(PlatformAttributes attributes) =>
+ attributes.SupportedFirst != null &&
+ (attributes.UnsupportedFirst == null || attributes.SupportedFirst < attributes.UnsupportedFirst);
+
+ ///
+ /// Determines if the attributes unsupported only for the platform (deny list)
+ ///
+ /// PlatformAttributes being checked
+ /// true if it is deny list
+ private static bool DenyList(PlatformAttributes attributes) =>
+ attributes.UnsupportedFirst != null &&
+ (attributes.SupportedFirst == null || attributes.UnsupportedFirst <= attributes.SupportedFirst);
+ }
+}
\ No newline at end of file
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.RuntimeOSPlatformInfo.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.RuntimeOSPlatformInfo.cs
deleted file mode 100644
index d66f0f1285..0000000000
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.RuntimeOSPlatformInfo.cs
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Collections.Immutable;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using Analyzer.Utilities;
-using Analyzer.Utilities.PooledObjects;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
-using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis;
-using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis;
-using Microsoft.CodeAnalysis.Operations;
-
-namespace Microsoft.NetCore.Analyzers.InteropServices
-{
- using ValueContentAnalysisResult = DataFlowAnalysisResult;
-
- public sealed partial class RuntimePlatformCheckAnalyzer
- {
- private struct RuntimeOSPlatformInfo : IAbstractAnalysisValue, IEquatable
- {
- private RuntimeOSPlatformInfo(string invokedPlatformCheckMethodName, string platformPropertyName, Version version, bool negated)
- {
- InvokedPlatformCheckMethodName = invokedPlatformCheckMethodName ?? throw new ArgumentNullException(nameof(invokedPlatformCheckMethodName));
- PlatformPropertyName = platformPropertyName ?? throw new ArgumentNullException(nameof(platformPropertyName));
- Version = version ?? throw new ArgumentNullException(nameof(version));
- Negated = negated;
- }
-
- public string InvokedPlatformCheckMethodName { get; }
- public string PlatformPropertyName { get; }
- public Version Version { get; }
- public bool Negated { get; }
-
- public IAbstractAnalysisValue GetNegatedValue()
- => new RuntimeOSPlatformInfo(InvokedPlatformCheckMethodName, PlatformPropertyName, Version, !Negated);
-
- public static bool TryDecode(
- IMethodSymbol invokedPlatformCheckMethod,
- ImmutableArray arguments,
- ValueContentAnalysisResult? valueContentAnalysisResult,
- INamedTypeSymbol osPlatformType,
- [NotNullWhen(returnValue: true)] out RuntimeOSPlatformInfo? info)
- {
- if (!TryDecodeOSPlatform(arguments, osPlatformType, out var osPlatformProperty) ||
- !TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var osVersion))
- {
- // Bail out
- info = default;
- return false;
- }
-
- info = new RuntimeOSPlatformInfo(invokedPlatformCheckMethod.Name, osPlatformProperty.Name, osVersion, negated: false);
- return true;
- }
-
- private static bool TryDecodeOSPlatform(
- ImmutableArray arguments,
- INamedTypeSymbol osPlatformType,
- [NotNullWhen(returnValue: true)] out IPropertySymbol? osPlatformProperty)
- {
- Debug.Assert(!arguments.IsEmpty);
- return TryDecodeOSPlatform(arguments[0].Value, osPlatformType, out osPlatformProperty);
- }
-
- private static bool TryDecodeOSPlatform(
- IOperation argumentValue,
- INamedTypeSymbol osPlatformType,
- [NotNullWhen(returnValue: true)] out IPropertySymbol? osPlatformProperty)
- {
- if ((argumentValue is IPropertyReferenceOperation propertyReference) &&
- propertyReference.Property.ContainingType.Equals(osPlatformType))
- {
- osPlatformProperty = propertyReference.Property;
- return true;
- }
-
- osPlatformProperty = null;
- return false;
- }
-
- private static bool TryDecodeOSVersion(
- ImmutableArray arguments,
- ValueContentAnalysisResult? valueContentAnalysisResult,
- [NotNullWhen(returnValue: true)] out Version? osVersion)
- {
- using var versionBuilder = ArrayBuilder.GetInstance(4, fillWithValue: 0);
- var index = 0;
- foreach (var argument in arguments.Skip(1))
- {
- if (!TryDecodeOSVersionPart(argument, valueContentAnalysisResult, out var osVersionPart))
- {
- osVersion = null;
- return false;
- }
-
- versionBuilder[index++] = osVersionPart;
- }
-
- osVersion = new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2], versionBuilder[3]);
- return true;
-
- static bool TryDecodeOSVersionPart(IArgumentOperation argument, ValueContentAnalysisResult? valueContentAnalysisResult, out int osVersionPart)
- {
- if (argument.Value.ConstantValue.HasValue &&
- argument.Value.ConstantValue.Value is int versionPart)
- {
- osVersionPart = versionPart;
- return true;
- }
-
- if (valueContentAnalysisResult != null)
- {
- var valueContentValue = valueContentAnalysisResult[argument.Value];
- if (valueContentValue.IsLiteralState &&
- valueContentValue.LiteralValues.Count == 1 &&
- valueContentValue.LiteralValues.Single() is int part)
- {
- osVersionPart = part;
- return true;
- }
- }
-
- osVersionPart = default;
- return false;
- }
- }
-
- public override string ToString()
- {
- var versionStr = Version.ToString(fieldCount: GetVersionFieldCount(Version));
- var result = $"{InvokedPlatformCheckMethodName};{PlatformPropertyName};{versionStr}";
- if (Negated)
- {
- result = $"!{result}";
- }
-
- return result;
-
- static int GetVersionFieldCount(Version version)
- {
- if (version.Revision != 0)
- {
- return 4;
- }
-
- if (version.Build != 0)
- {
- return 3;
- }
-
- if (version.Minor != 0)
- {
- return 2;
- }
-
- return 1;
- }
- }
-
- public bool Equals(RuntimeOSPlatformInfo other)
- => InvokedPlatformCheckMethodName.Equals(other.InvokedPlatformCheckMethodName, StringComparison.OrdinalIgnoreCase) &&
- PlatformPropertyName.Equals(other.PlatformPropertyName, StringComparison.OrdinalIgnoreCase) &&
- Version.Equals(other.Version) &&
- Negated == other.Negated;
-
- public override bool Equals(object obj)
- => obj is RuntimeOSPlatformInfo otherInfo && Equals(otherInfo);
-
- public override int GetHashCode()
- => HashUtilities.Combine(InvokedPlatformCheckMethodName.GetHashCode(), PlatformPropertyName.GetHashCode(), Version.GetHashCode(), Negated.GetHashCode());
-
- bool IEquatable.Equals(IAbstractAnalysisValue other)
- => other is RuntimeOSPlatformInfo otherInfo && Equals(otherInfo);
-
- public static bool operator ==(RuntimeOSPlatformInfo left, RuntimeOSPlatformInfo right)
- {
- return left.Equals(right);
- }
-
- public static bool operator !=(RuntimeOSPlatformInfo left, RuntimeOSPlatformInfo right)
- {
- return !(left == right);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.cs
deleted file mode 100644
index 32f5177850..0000000000
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.cs
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Collections.Immutable;
-using System.Diagnostics;
-using System.Linq;
-using Analyzer.Utilities;
-using Analyzer.Utilities.Extensions;
-using Analyzer.Utilities.PooledObjects;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis;
-using Microsoft.CodeAnalysis.Operations;
-
-namespace Microsoft.NetCore.Analyzers.InteropServices
-{
-#pragma warning disable RS1001 // Missing diagnostic analyzer attribute - TODO: fix and enable analyzer.
- public sealed partial class RuntimePlatformCheckAnalyzer : DiagnosticAnalyzer
-#pragma warning restore RS1001 // Missing diagnostic analyzer attribute.
- {
- internal const string RuleId = "CA1416";
- private static readonly ImmutableArray s_platformCheckMethods = ImmutableArray.Create("IsOSPlatformOrLater", "IsOSPlatformEarlierThan");
-
- // TODO: Define resource strings for title, message and description.
- private static readonly LocalizableString s_localizableTitle = "RuntimePlatformCheckTitle"; //new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.RuntimePlatformCheckTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
- private static readonly LocalizableString s_localizableMessage = "Platform checks:'{0}'"; //new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.RuntimePlatformCheckMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
- private static readonly LocalizableString s_localizableDescription = "RuntimePlatformCheckDescription."; //new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.RuntimePlatformCheckMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
-
- internal static DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId,
- s_localizableTitle,
- s_localizableMessage,
- DiagnosticCategory.Interoperability,
- RuleLevel.IdeSuggestion,
- description: s_localizableDescription,
- isPortedFxCopRule: false,
- isDataflowRule: false);
- public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
-
- public override void Initialize(AnalysisContext context)
- {
- context.EnableConcurrentExecution();
- context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
-
- context.RegisterCompilationStartAction(context =>
- {
- // TODO: Remove the below temporary hack once new APIs are available.
- var typeName = WellKnownTypeNames.SystemRuntimeInteropServicesRuntimeInformation + "Helper";
-
- if (!context.Compilation.TryGetOrCreateTypeByMetadataName(typeName, out var runtimeInformationType) ||
- !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesOSPlatform, out var osPlatformType))
- {
- return;
- }
-
- var platformCheckMethods = GetPlatformCheckMethods(runtimeInformationType, osPlatformType);
- if (platformCheckMethods.IsEmpty)
- {
- return;
- }
-
- context.RegisterOperationBlockStartAction(context => AnalyzerOperationBlock(context, platformCheckMethods, osPlatformType));
- return;
-
- static ImmutableArray GetPlatformCheckMethods(INamedTypeSymbol runtimeInformationType, INamedTypeSymbol osPlatformType)
- {
- using var builder = ArrayBuilder.GetInstance();
- var methods = runtimeInformationType.GetMembers().OfType();
- foreach (var method in methods)
- {
- if (s_platformCheckMethods.Contains(method.Name) &&
- !method.Parameters.IsEmpty &&
- method.Parameters[0].Type.Equals(osPlatformType) &&
- method.Parameters.Skip(1).All(p => p.Type.SpecialType == SpecialType.System_Int32))
- {
- builder.Add(method);
- }
- }
-
- return builder.ToImmutable();
- }
- });
- }
-
- private static void AnalyzerOperationBlock(
- OperationBlockStartAnalysisContext context,
- ImmutableArray platformCheckMethods,
- INamedTypeSymbol osPlatformType)
- {
-#pragma warning disable CA2000 // Dispose objects before losing scope - disposed in OperationBlockEndAction.
- var platformSpecificOperations = PooledConcurrentSet.GetInstance();
-#pragma warning restore CA2000 // Dispose objects before losing scope
- var needsValueContentAnalysis = false;
-
- context.RegisterOperationAction(context =>
- {
- var invocation = (IInvocationOperation)context.Operation;
- if (platformCheckMethods.Contains(invocation.TargetMethod))
- {
- needsValueContentAnalysis = needsValueContentAnalysis || ComputeNeedsValueContentAnalysis(invocation);
- }
- else
- {
- // TODO: Add real platform specific operations that need runtime OS platform validation.
- platformSpecificOperations.Add(invocation);
- }
- }, OperationKind.Invocation);
-
- context.RegisterOperationBlockEndAction(context =>
- {
- try
- {
- if (platformSpecificOperations.IsEmpty ||
- !(context.OperationBlocks.GetControlFlowGraph() is { } cfg))
- {
- return;
- }
-
- var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation);
- var analysisResult = GlobalFlowStateAnalysis.TryGetOrComputeResult(
- cfg, context.OwningSymbol, CreateOperationVisitor,
- wellKnownTypeProvider, context.Options, Rule,
- performValueContentAnalysis: needsValueContentAnalysis, context.CancellationToken,
- out var valueContentAnalysisResult);
- if (analysisResult == null)
- {
- return;
- }
-
- Debug.Assert(valueContentAnalysisResult == null || needsValueContentAnalysis);
-
- foreach (var platformSpecificOperation in platformSpecificOperations)
- {
- var value = analysisResult[platformSpecificOperation.Kind, platformSpecificOperation.Syntax];
- if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown)
- {
- continue;
- }
-
- // TODO: Add real checks.
-
- // TODO Platform checks:'{0}'
- context.ReportDiagnostic(platformSpecificOperation.CreateDiagnostic(Rule, value));
- }
- }
- finally
- {
- platformSpecificOperations.Free();
- }
-
- return;
-
- OperationVisitor CreateOperationVisitor(GlobalFlowStateAnalysisContext context)
- => new OperationVisitor(platformCheckMethods, osPlatformType, context);
- });
- }
-
- private static bool ComputeNeedsValueContentAnalysis(IInvocationOperation invocation)
- {
- Debug.Assert(!invocation.Arguments.IsEmpty);
- foreach (var argument in invocation.Arguments.Skip(1))
- {
- if (!argument.Value.ConstantValue.HasValue)
- {
- return true;
- }
- }
-
- return false;
- }
- }
-}
\ No newline at end of file
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
index 1df3ac003e..3a2393de18 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
@@ -1425,6 +1425,18 @@
Use `{0}` instead of Range-based indexers on an array
+
+ Validate platform compatibility
+
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+ '{0}' is supported on '{1}' {2} and later
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+
String parameters passed by value with the 'OutAttribute' can destabilize the runtime if the string is an interned string.
@@ -1452,4 +1464,10 @@
'{0}' will throw for assemblies embedded in a single-file app
+
+ '{0}' is unsupported on '{1}'
+
+
+ '{0}' is supported on '{1}'
+
\ No newline at end of file
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf
index 68fb6e60cb..34b8ac9900 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf
@@ -1512,6 +1512,36 @@
Metody P/Invoke nemají být viditelné
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.Zkontrolujte kód, který zpracovává nedůvěryhodná deserializovaná data pro zpracování neočekávaných cyklů odkazů. Neočekávaný cyklus odkazů by neměl způsobit, aby se kód zasekl do nekonečné smyčky. V opačném případě může neočekávaný cyklus odkazů při deserializaci nedůvěryhodných dat umožnit útočníkovi útok typu odepření služby (DOS) nebo vyčerpání paměti procesu.
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf
index b34f6df262..03337c34de 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf
@@ -1512,6 +1512,36 @@
P/Invokes dürfen nicht sichtbar sein
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.Überprüfen Sie, auf welche Weise der Code zum Verarbeiten von nicht vertrauenswürdigen deserialisierten Daten unerwartete Verweiszyklen behandelt. Ein unerwarteter Verweiszyklus sollte nicht dazu führen, dass der Code in eine Endlosschleife eintritt. Andernfalls kann ein unerwarteter Verweiszyklus durch einen Angreifer für einen DoS-Angriff oder eine Arbeitsspeicherüberlastung des Prozesses genutzt werden, während nicht vertrauenswürdige Daten deserialisiert werden.
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf
index 29c6946874..67cba7a62e 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf
@@ -1512,6 +1512,36 @@
Los elementos P/Invoke no deben estar visibles
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.Revise el código que procesa datos deserializados que no son de confianza para controlar ciclos de referencia inesperados. Un ciclo de referencia inesperado no debe hacer que el código entre en un bucle infinito. De lo contrario, este tipo de ciclo puede permitir un ataque a DOS o agotar la memoria del proceso al deserializar datos que no son de confianza.
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf
index ed7224d70b..af384ee4a3 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf
@@ -1512,6 +1512,36 @@
Les P/Invoke ne doivent pas être visibles
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.Passez en revue le code qui traite les données désérialisées non approuvées pour gérer les cycles de référence inattendus. Un cycle de référence inattendu ne doit pas entraîner l'entrée du code dans une boucle infinie. Sinon, le cycle de référence inattendu risque de permettre à une personne de lancer une attaque DoS ou de saturer la mémoire du processus pendant la désérialisation des données non approuvées.
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf
index 7042d84ef3..f41044208d 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf
@@ -1512,6 +1512,36 @@
I metodi P/Invoke non devono essere visibili
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.Rivedere il codice che elabora dati deserializzati non attendibili per la gestione di cicli di riferimento imprevisti. Un ciclo di riferimento imprevisto non deve causare un ciclo infinito del codice. Un ciclo di riferimento imprevisto può invece consentire attacchi di tipo DoS da parte di un utente malintenzionato o l'esaurimento della memoria del processo durante la deserializzazione di dati non attendibili.
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf
index 077913c80c..c003dc6b47 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf
@@ -1512,6 +1512,36 @@
P/Invokes は参照可能にすることはできません
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.信頼されていない逆シリアル化データを処理するコードで、予期しない参照サイクルの処理を確認してください。予期しない参照サイクルが原因でコードが無限ループに入ることのないようにしてください。そうしないと、信頼されていないデータを逆シリアル化するときに、予期しない参照サイクルのせいで攻撃者が DoS 攻撃をしたり、プロセスのメモリを使い果たしたりできるようになりかねません。
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf
index 6f07b60653..91a241277c 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf
@@ -1512,6 +1512,36 @@
P/Invokes를 표시하지 않아야 합니다.
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.신뢰할 수 없는 역직렬화된 데이터를 처리하는 코드를 검토하여 예기치 않은 참조 주기를 처리합니다. 예기치 않은 참조 주기로 인해 코드의 무한 루프가 시작되어서는 안 됩니다. 참조 주기를 처리하지 않으면 공격자가 신뢰할 수 없는 데이터를 역직렬화할 때 프로세스의 메모리를 고갈시키거나 DOS를 수행할 수 있습니다.
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf
index 0d5b616630..d76871845b 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf
@@ -1513,6 +1513,36 @@
Elementy P/Invoke nie powinny być widoczne
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.Przejrzyj kod, który przetwarza zdeserializowane niezaufane dane, pod kątem obsługi nieoczekiwanych cykli odwołań. Nieoczekiwany cykl odwołań nie powinien powodować wejścia kodu w nieskończoną pętlę. W przeciwnym razie nieoczekiwany cykl odwołań może pozwolić osobie atakującej na przeprowadzenie ataku DoS lub wyczerpanie pamięci procesu podczas deserializacji niezaufanych danych.
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf
index 6434bdd99c..b44005b6fe 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf
@@ -1512,6 +1512,36 @@
P/Invokes não deve ser visível
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.Examine o código que processa dados desserializados não confiáveis para o tratamento de ciclos de referência inesperados. Um ciclo de referência inesperado não deve fazer com que o código entre em um loop infinito. Caso contrário, um ciclo de referência inesperado pode permitir um invasor do DOS ou esgotar a memória do processo ao desserializar dados não confiáveis.
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf
index 63bc5ba859..d4b7a45cd7 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf
@@ -1512,6 +1512,36 @@
Методы P/Invoke не должны быть видимыми
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.Проверьте код, который обрабатывает недоверенные десериализованные данные, на наличие обработки непредвиденного зацикливания ссылок. Непредвиденное зацикливание ссылок не должно приводить к переводу кода в бесконечный цикл. В противном случае непредвиденное зацикливание ссылок может позволить злоумышленнику провести атаку типа "отказ в обслуживании" или исчерпать память процесса при десериализации недоверенных данных.
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf
index e2f810f296..cc0c4f17fb 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf
@@ -1512,6 +1512,36 @@
P/Invokes görünür olmamalıdır
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.Beklenmeyen başvuru döngülerinin işlenmesi için, seri durumdan çıkarılmış güvenilmeyen verileri işleyen kodu inceleyin. Beklenmeyen başvuru döngüsü, kodun sonsuz bir döngüye girmesine neden olmamalıdır. Buna neden olursa beklenmeyen başvuru döngüsü, güvenilmeyen veriler seri durumdan çıkarılırken saldırganın DOS'a erişmesine izin verebilir veya işlem belleğini tüketebilir.
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf
index 5d5cbce535..7ca32c0e15 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf
@@ -1512,6 +1512,36 @@
P/Invokes 应该是不可见的
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.查看处理不受信任的反序列化数据(为了处理意外引用循环)的代码。意外引用循环不应导致代码进入无限循环。否则,当反序列化不受信任的数据时,意外的引用循环可能导致攻击者 DOS 或耗尽进程的内存。
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf
index c392a04e44..8993b80679 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf
@@ -1512,6 +1512,36 @@
不應看得見 P/Invoke
+
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+ Using platform dependent API on a component makes the code no longer work across all platforms.
+
+
+
+ Validate platform compatibility
+ Validate platform compatibility
+
+
+
+ '{0}' is unsupported on '{1}'
+ '{0}' is unsupported on '{1}'
+
+
+
+ '{0}' is unsupported on '{1}' {2} and later
+ '{0}' is unsupported on '{1}' {2} and later
+
+
+
+ '{0}' is supported on '{1}'
+ '{0}' is supported on '{1}'
+
+
+
+ '{0}' is supported on '{1}' {2} and later
+ '{0}' is supported on '{1}' {2} and later
+
+ Review code that processes untrusted deserialized data for handling of unexpected reference cycles. An unexpected reference cycle should not cause the code to enter an infinite loop. Otherwise, an unexpected reference cycle can allow an attacker to DOS or exhaust the memory of the process when deserializing untrusted data.檢閱會處理未受信任之還原序列化資料的程式碼,以處理未預期的參考迴圈。未預期的參考迴圈不應導致程式碼進入無限迴圈,否則,未預期的參考週期可能讓攻擊者得以 DOS,或在將未受信任的資料還原序列化時耗盡處理序的記憶體。
diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs
new file mode 100644
index 0000000000..bd9fdc5d72
--- /dev/null
+++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs
@@ -0,0 +1,2595 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Xunit;
+
+using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
+ Microsoft.NetCore.Analyzers.InteropServices.PlatformCompatabilityAnalyzer,
+ Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>;
+
+namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests
+{
+ public partial class PlatformCompatabilityAnalyzerTests
+ {
+ public static IEnumerable