From 9e5e8f13e8993eff1cf4ec9efcbdd60ffa0775c9 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 7 Jul 2020 15:53:25 -0700 Subject: [PATCH 01/48] Platform compatability analyzer draft --- .../Core/AnalyzerReleases.Unshipped.md | 1 + .../MockOsPlatformAttribute.cs | 121 ++++ ...mCompatabilityAnalyzer.OperationVisitor.cs | 48 ++ ...CompatabilityAnalyzer.RuntimeMethodInfo.cs | 202 ++++++ .../PlatformCompatabilityAnalyzer.cs | 486 +++++++++++++ .../MicrosoftNetCoreAnalyzersResources.resx | 17 +- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 35 +- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 35 +- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 35 +- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 35 +- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 35 +- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 35 +- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 35 +- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 35 +- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 35 +- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 35 +- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 35 +- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 35 +- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 35 +- .../PlatformCompatabilityAnalyzerTests.cs | 673 ++++++++++++++++++ src/Test.Utilities/CSharpCodeFixVerifier`2.cs | 1 + .../DiagnosticCategoryAndIdRanges.txt | 2 +- src/Utilities/Compiler/WellKnownTypeNames.cs | 1 + 23 files changed, 1872 insertions(+), 135 deletions(-) create mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/MockOsPlatformAttribute.cs create mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs create mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.RuntimeMethodInfo.cs create mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs create mode 100644 src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index ec7d1d7a75..d6c0058efc 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -10,6 +10,7 @@ CA1047 | Design | Info | DoNotDeclareProtectedMembersInSealedTypes, [Documentati CA1069 | Design | Info | EnumShouldNotHaveDuplicatedValues, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1069) CA1070 | Design | Info | DoNotDeclareEventFieldsAsVirtual, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1070) CA1416 | Interoperability | Info | RuntimePlatformCheckAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1416) +CA1417 | Interoperability | Warning | PlatformCompatabilityAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1417) CA1700 | Naming | Disabled | DoNotNameEnumValuesReserved, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1700) CA1713 | Naming | Disabled | EventsShouldNotHaveBeforeOrAfterPrefix, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1713) CA1805 | Performance | Info | DoNotInitializeUnnecessarilyAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1805) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/MockOsPlatformAttribute.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/MockOsPlatformAttribute.cs new file mode 100644 index 0000000000..0e512303db --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/MockOsPlatformAttribute.cs @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace System.Runtime.Versioning +{ + public abstract class OSPlatformAttribute : Attribute + { + private protected OSPlatformAttribute(string platformName) + { + PlatformName = platformName; + } + + public string PlatformName { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, + AllowMultiple = false, Inherited = false)] + public sealed class TargetPlatformAttribute : OSPlatformAttribute + { + public TargetPlatformAttribute(string platformName) : base(platformName) + { } + } + + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Event | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] + public sealed class MinimumOSPlatformAttribute : OSPlatformAttribute + { + public MinimumOSPlatformAttribute(string platformName) : base(platformName) + { } + } + + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Event | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] + public sealed class RemovedInOSPlatformAttribute : OSPlatformAttribute + { + public RemovedInOSPlatformAttribute(string platformName) : base(platformName) + { } + } + + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Event | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] + public sealed class ObsoletedInOSPlatformAttribute : OSPlatformAttribute + { + public ObsoletedInOSPlatformAttribute(string platformName) : base(platformName) + { } + public ObsoletedInOSPlatformAttribute(string platformName, string message) : base(platformName) + { + Message = message; + } + public string? Message { get; } + public string? Url { get; set; } + } +} + +namespace System.Runtime.InteropServices +{ + public static class RuntimeInformationHelper + { +#pragma warning disable CA1801, IDE0060 // Review unused parameters + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major) + { + return RuntimeInformation.IsOSPlatform(osPlatform); + } + + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor) + { + return RuntimeInformation.IsOSPlatform(osPlatform); + } + + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build) + { + return RuntimeInformation.IsOSPlatform(osPlatform); + } + + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build, int revision) + { + return RuntimeInformation.IsOSPlatform(osPlatform); + } + + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major) + { + return !RuntimeInformation.IsOSPlatform(osPlatform); + } + + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor) + { + return !RuntimeInformation.IsOSPlatform(osPlatform); + } + + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build) + { + return !RuntimeInformation.IsOSPlatform(osPlatform); + } + + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build, int revision) + { + return !RuntimeInformation.IsOSPlatform(osPlatform); + } +#pragma warning restore CA1801, IDE0060 // Review unused parameters + } +} diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs new file mode 100644 index 0000000000..1dc44e39de --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs @@ -0,0 +1,48 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.NetCore.Analyzers.InteropServices +{ + public partial class PlatformCompatabilityAnalyzer + { + private sealed class OperationVisitor : GlobalFlowStateDataFlowOperationVisitor + { + private readonly ImmutableArray _platformCheckMethods; + private readonly INamedTypeSymbol _osPlatformType; + + public OperationVisitor( + ImmutableArray platformCheckMethods, + INamedTypeSymbol osPlatformType, + GlobalFlowStateAnalysisContext analysisContext) + : base(analysisContext, hasPredicatedGlobalState: true) + { + _platformCheckMethods = platformCheckMethods; + _osPlatformType = osPlatformType; + } + + public override GlobalFlowStateAnalysisValueSet VisitInvocation_NonLambdaOrDelegateOrLocalFunction( + IMethodSymbol method, + IOperation? visitedInstance, + ImmutableArray visitedArguments, + bool invokedAsDelegate, + IOperation originalOperation, + GlobalFlowStateAnalysisValueSet defaultValue) + { + var value = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(method, visitedInstance, visitedArguments, invokedAsDelegate, originalOperation, defaultValue); + + if (_platformCheckMethods.Contains(method.OriginalDefinition) && visitedArguments.Length > 0) + { + return RuntimeMethodInfo.TryDecode(method, visitedArguments, DataFlowAnalysisContext.ValueContentAnalysisResultOpt, _osPlatformType, out var platformInfo) ? + new GlobalFlowStateAnalysisValueSet(platformInfo) : + GlobalFlowStateAnalysisValueSet.Unknown; + } + + return GetValueOrDefault(value); + } + } + } +} diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.RuntimeMethodInfo.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.RuntimeMethodInfo.cs new file mode 100644 index 0000000000..0b4f9fc3d3 --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.RuntimeMethodInfo.cs @@ -0,0 +1,202 @@ +// 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 System.Text; +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 partial class PlatformCompatabilityAnalyzer + { + private struct RuntimeMethodInfo : IAbstractAnalysisValue, IEquatable + { + private RuntimeMethodInfo(string invokedPlatformCheckMethodName, string platformPropertyName, int[] 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; } +#pragma warning disable CA1819 // Properties should not return arrays + public int[] Version { get; } +#pragma warning restore CA1819 // Properties should not return arrays + public bool Negated { get; } + + public IAbstractAnalysisValue GetNegatedValue() + => new RuntimeMethodInfo(InvokedPlatformCheckMethodName, PlatformPropertyName, Version, !Negated); + + public static bool TryDecode( + IMethodSymbol invokedPlatformCheckMethod, + ImmutableArray arguments, + ValueContentAnalysisResult? valueContentAnalysisResult, + INamedTypeSymbol osPlatformType, + [NotNullWhen(returnValue: true)] out RuntimeMethodInfo? info) + { + if (!TryDecodeOSPlatform(arguments, osPlatformType, out var osPlatformProperty) || + !TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var osVersion)) + { + // Bail out + info = default; + return false; + } + + info = new RuntimeMethodInfo(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 int[]? 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 = versionBuilder.ToArray(); + 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 = GetVersionString(Version, GetVersionFieldCount(Version)); + var result = $"{InvokedPlatformCheckMethodName};{PlatformPropertyName};{versionStr}"; + if (Negated) + { + result = $"!{result}"; + } + + return result; + + static int GetVersionFieldCount(int[] version) + { + if (version[3] != 0) + { + return 4; + } + + if (version[2] != 0) + { + return 3; + } + + if (version[1] != 0) + { + return 2; + } + + return 1; + } + } + + private static string GetVersionString(int[] version, int count) + { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < count; i++) + { + builder.Append(version[i]); + } + return builder.ToString(); + } + + public bool Equals(RuntimeMethodInfo 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 RuntimeMethodInfo otherInfo && Equals(otherInfo); + + public override int GetHashCode() + => HashUtilities.Combine(InvokedPlatformCheckMethodName.GetHashCode(), PlatformPropertyName.GetHashCode(), Version.GetHashCode(), Negated.GetHashCode()); + + bool IEquatable.Equals(IAbstractAnalysisValue other) + => other is RuntimeMethodInfo otherInfo && Equals(otherInfo); + + public static bool operator ==(RuntimeMethodInfo left, RuntimeMethodInfo right) + { + return left.Equals(right); + } + + public static bool operator !=(RuntimeMethodInfo left, RuntimeMethodInfo right) + { + return !(left == right); + } + } + } +} 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..c065e28a3c --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -0,0 +1,486 @@ +// 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.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Runtime.Versioning; +using System.Text.RegularExpressions; +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.GlobalFlowStateAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.NetCore.Analyzers.InteropServices +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer + { + internal const string RuleId = "CA1417"; + private static readonly ImmutableArray s_platformCheckMethods = ImmutableArray.Create("IsOSPlatformOrLater", "IsOSPlatformEarlierThan"); + private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private static readonly LocalizableString s_localizableAddedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckAddedMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private static readonly LocalizableString s_localizableObsoleteMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckObsoleteMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private static readonly LocalizableString s_localizableRemovedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckRemovedMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckDescription), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private const char SeparatorDash = '-'; + private const char SeparatorSemicolon = ';'; + private const char SeparatorDot = '.'; + private const string MinimumOsAttributeName = nameof(MinimumOSPlatformAttribute); + private const string ObsoleteAttributeName = nameof(ObsoletedInOSPlatformAttribute); + private const string RemovedAttributeName = nameof(RemovedInOSPlatformAttribute); + private const string Windows = nameof(Windows); + private static readonly Regex s_neutralTfmRegex = new Regex(@"^net([5-9]|standard\d|coreapp\d)\.\d$", RegexOptions.IgnoreCase); + private static readonly Regex s_osParseRegex = new Regex(@"([a-z]{3,7})((\d{1,2})\.?(\d)?\.?(\d)?\.?(\d)?)*", RegexOptions.IgnoreCase); + + internal static DiagnosticDescriptor AddedRule = DiagnosticDescriptorHelper.Create(RuleId, + s_localizableTitle, + s_localizableAddedMessage, + DiagnosticCategory.Interoperability, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: false, + isDataflowRule: false); + + internal static DiagnosticDescriptor ObsoleteRule = DiagnosticDescriptorHelper.Create(RuleId, + s_localizableTitle, + s_localizableAddedMessage, + DiagnosticCategory.Interoperability, + RuleLevel.BuildWarning, + description: s_localizableObsoleteMessage, + isPortedFxCopRule: false, + isDataflowRule: false); + internal static DiagnosticDescriptor RemovedRule = DiagnosticDescriptorHelper.Create(RuleId, + s_localizableTitle, + s_localizableAddedMessage, + DiagnosticCategory.Interoperability, + RuleLevel.BuildWarning, + description: s_localizableRemovedMessage, + isPortedFxCopRule: false, + isDataflowRule: false); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(AddedRule, ObsoleteRule, RemovedRule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(context => + { + var typeName = WellKnownTypeNames.SystemRuntimeInteropServicesRuntimeInformation + "Helper"; + + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(typeName, out var runtimeInformationType) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesOSPlatform, out var osPlatformType) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeVersioningOSPlatformAttribute, out var osAttribute)) + { + return; + } + + context.RegisterOperationBlockStartAction(context => AnalyzerOperationBlock(context, osAttribute, runtimeInformationType, osPlatformType)); + }); + } + + private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, INamedTypeSymbol osAttribute, INamedTypeSymbol runtimeInformationType, INamedTypeSymbol osPlatformType) + { +#pragma warning disable CA2000 // Dispose objects before losing scope - disposed in OperationBlockEndAction. + var parsedTfms = ParseTfm(context.Options, context.OwningSymbol, context.Compilation, context.CancellationToken); +#pragma warning restore CA2000 + var platformSpecificOperations = PooledDictionary>.GetInstance(); + var needsValueContentAnalysis = false; + + context.RegisterOperationAction(context => + { + AnalyzeInvocationOperation((IInvocationOperation)context.Operation, osAttribute, context, parsedTfms, ref platformSpecificOperations); + } + , OperationKind.Invocation); + + context.RegisterOperationBlockEndAction(context => + { + try + { + if (platformSpecificOperations.Count == 0 || !(context.OperationBlocks.GetControlFlowGraph() is { } cfg)) + { + return; + } + + var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); + var analysisResult = GlobalFlowStateAnalysis.TryGetOrComputeResult( + cfg, context.OwningSymbol, CreateOperationVisitor, + wellKnownTypeProvider, context.Options, AddedRule, performPointsToAnalysis: needsValueContentAnalysis, + performValueContentAnalysis: needsValueContentAnalysis, context.CancellationToken, + out var pointsToAnalysisResult, out var valueContentAnalysisResult); + if (analysisResult == null) + { + return; + } + + Debug.Assert(valueContentAnalysisResult == null || needsValueContentAnalysis); + Debug.Assert(pointsToAnalysisResult == null || needsValueContentAnalysis); + + foreach (var platformSpecificOperation in platformSpecificOperations) + { + var value = analysisResult[platformSpecificOperation.Key.Kind, platformSpecificOperation.Key.Syntax]; + if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown) + { + continue; + } + + OsAttributeInfo parsedAttribute = platformSpecificOperation.Value.FirstOrDefault(); + if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Empty || value.Kind == GlobalFlowStateAnalysisValueSetKind.Unset) + { + context.ReportDiagnostic(platformSpecificOperation.Key.CreateDiagnostic(SwitchRule(parsedAttribute.AttributeType), platformSpecificOperation.Key.TargetMethod.Name, + parsedAttribute.OsPlatform!, $"{parsedAttribute.Version[0]}.{parsedAttribute.Version[1]}.{parsedAttribute.Version[2]}.{parsedAttribute.Version[3]}")); + } + else + { + if (value.AnalysisValues.FirstOrDefault() is RuntimeMethodInfo info) + { + if (!info.Negated) + { + OsAttributeInfo attribute = platformSpecificOperation.Value.First(); + if (attribute.OsPlatform!.Equals(info.PlatformPropertyName, StringComparison.InvariantCultureIgnoreCase)) + { + if (info.InvokedPlatformCheckMethodName.Equals(s_platformCheckMethods[0], StringComparison.InvariantCulture)) + { + if (AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) + { + continue; + } + } + } + } + } + context.ReportDiagnostic(platformSpecificOperation.Key.CreateDiagnostic(SwitchRule(parsedAttribute.AttributeType), platformSpecificOperation.Key.TargetMethod.Name, + parsedAttribute.OsPlatform!, $"{parsedAttribute.Version[0]}.{parsedAttribute.Version[1]}.{parsedAttribute.Version[2]}.{parsedAttribute.Version[3]}")); + } + } + } + finally + { + platformSpecificOperations.Free(); + parsedTfms?.Free(); + } + + return; + + OperationVisitor CreateOperationVisitor(GlobalFlowStateAnalysisContext context) + => new OperationVisitor(GetPlatformCheckMethods(runtimeInformationType, osPlatformType), osPlatformType, context); + }); + + 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.Length >= 1 && + 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 AnalyzeInvocationOperation(IInvocationOperation operation, INamedTypeSymbol osAttribute, OperationAnalysisContext context, + PooledConcurrentSet? parsedTfms, ref PooledDictionary> platformSpecificOperations) + { + var attributes = GetApplicableAttributes(operation.TargetMethod.GetAttributes(), operation.TargetMethod.ContainingType, osAttribute); + using var builder = ArrayBuilder.GetInstance(); + foreach (AttributeData attribute in attributes) + { + bool suppressed = false; + OsAttributeInfo parsedAttribute = OsAttributeInfo.ParseAttributeData(attribute); + if (parsedTfms != null) + { + foreach (OsAttributeInfo tfm in parsedTfms) + { + if (tfm.OsPlatform != null && tfm.OsPlatform.Equals(parsedAttribute.OsPlatform, StringComparison.InvariantCultureIgnoreCase)) + { + suppressed = AttributeVersionsMatch(parsedAttribute, tfm); + } + } + } + + suppressed = suppressed || Suppress(parsedAttribute, context.ContainingSymbol); + + if (!suppressed) + { + builder.Add(parsedAttribute); + } + } + + if (builder.Count > 0) + { + platformSpecificOperations.Add(operation, builder.ToImmutable()); + } + } + + private static DiagnosticDescriptor SwitchRule(OsAttrbiteType attributeType) + { + if (attributeType == OsAttrbiteType.MinimumOSPlatformAttribute) + return AddedRule; + if (attributeType == OsAttrbiteType.ObsoletedInOSPlatformAttribute) + return ObsoleteRule; + return RemovedRule; + } + + private static PooledConcurrentSet? ParseTfm(AnalyzerOptions options, ISymbol containingSymbol, Compilation compilation, CancellationToken cancellationToken) + { // ((net[5-9]|netstandard\d|netcoreapp\d)\.\d(-([a-z]{3,7})(\d{1,2}\.?\d?\.?\d?\.?\d?)*)?)+ + string? tfmString = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetFramework, AddedRule, containingSymbol, compilation, cancellationToken); + if (tfmString != null) + { + PooledConcurrentSet platformInfos = PooledConcurrentSet.GetInstance(); + var tfms = tfmString.Split(SeparatorSemicolon); + + foreach (var tfm in tfms) + { + var tokens = tfm.Split(SeparatorDash); + OsAttributeInfo platformInfo = new OsAttributeInfo(); + platformInfo.Version = new int[4]; + if (tokens.Length == 1) + { + if (!s_neutralTfmRegex.IsMatch(tokens[0])) + { + platformInfo.OsPlatform = Windows; + } + } + else + { + Debug.Assert(tokens.Length == 2); + Match match = s_osParseRegex.Match(tokens[1]); + if (match.Success) + { + platformInfo.OsPlatform = match.Groups[1].Value; + for (int i = 3; i < 7; i++) + { + if (!string.IsNullOrEmpty(match.Groups[i].Value)) + { + platformInfo.Version[i - 3] = int.Parse(match.Groups[i].Value, CultureInfo.InvariantCulture); + } + } + } + var tpmv = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetPlatformMinVersion, AddedRule, containingSymbol, compilation, cancellationToken); + if (tpmv != null) + { + var splitted = tpmv.Split(SeparatorDot); + int i = 0; + foreach (var token in splitted) + { + platformInfo.Version[i] = int.Parse(token, CultureInfo.InvariantCulture); + } + } + } + platformInfos.Add(platformInfo); + } + return platformInfos; + } + return null; + } + + private static List GetApplicableAttributes(ImmutableArray immediateAttributes, INamedTypeSymbol type, INamedTypeSymbol osAttribute) + { + var attributes = new List(); + foreach (AttributeData attribute in immediateAttributes) + { + if (attribute.AttributeClass.DerivesFromOrImplementsAnyConstructionOf(osAttribute)) + { + attributes.Add(attribute); + } + } + while (type != null) + { + var current = type.GetAttributes(); + foreach (var attribute in current) + { + if (attribute.AttributeClass.DerivesFromOrImplementsAnyConstructionOf(osAttribute)) + { + attributes.Add(attribute); + } + } + type = type.BaseType; + } + return attributes; + } + + private static bool Suppress(OsAttributeInfo diagnosingAttribute, ISymbol containingSymbol) + { + while (containingSymbol != null) + { + var attributes = containingSymbol.GetAttributes(); + if (attributes != null) + { + foreach (AttributeData attribute in attributes) + { + if (diagnosingAttribute.AttributeType.ToString().Equals(attribute.AttributeClass.Name, StringComparison.InvariantCulture)) + { + OsAttributeInfo parsedAttribute = OsAttributeInfo.ParseAttributeData(attribute); + if (diagnosingAttribute.OsPlatform!.Equals(parsedAttribute.OsPlatform, StringComparison.InvariantCultureIgnoreCase) && AttributeVersionsMatch(diagnosingAttribute, parsedAttribute)) + { + return true; + } + } + } + } + containingSymbol = containingSymbol.ContainingSymbol; + } + return false; + } + + private static bool AttributeVersionsMatch(OsAttributeInfo diagnosingAttribute, OsAttributeInfo tfm) + { + if (diagnosingAttribute.AttributeType == OsAttrbiteType.MinimumOSPlatformAttribute) + { + for (int i = 0; i < 4; i++) + { + if (diagnosingAttribute.Version[i] < tfm.Version[i]) + { + return true; + } + else if (diagnosingAttribute.Version[i] > tfm.Version[i]) + { + return false; + } + } + return true; + } + else + { + for (int i = 0; i < 4; i++) + { + if (diagnosingAttribute.Version[i] > tfm.Version[i]) + { + return true; + } + else if (diagnosingAttribute.Version[i] < tfm.Version[i]) + { + return false; + } + } + return true; + }; + } + + private static bool AttributeVersionsMatch(OsAttrbiteType attributeType, int[] diagnosingVersion, int[] suppressingVersion) + { + if (attributeType == OsAttrbiteType.MinimumOSPlatformAttribute) + { + for (int i = 0; i < 4; i++) + { + if (diagnosingVersion[i] < suppressingVersion[i]) + { + return true; + } + else if (diagnosingVersion[i] > suppressingVersion[i]) + { + return false; + } + } + return true; + } + else + { + for (int i = 0; i < 4; i++) + { + if (diagnosingVersion[i] > suppressingVersion[i]) + { + return true; + } + else if (diagnosingVersion[i] < suppressingVersion[i]) + { + return false; + } + } + return true; + }; + } + + private enum OsAttrbiteType + { + None, MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute + } + + private struct OsAttributeInfo : IEquatable + { + public OsAttrbiteType AttributeType { get; set; } + public string? OsPlatform { get; set; } +#pragma warning disable CA1819 // Properties should not return arrays + public int[] Version { get; set; } +#pragma warning restore CA1819 // Properties should not return arrays + + public static OsAttributeInfo ParseAttributeData(AttributeData osAttibute) + { + OsAttributeInfo platformInfo = new OsAttributeInfo(); + switch (osAttibute.AttributeClass.Name) + { + case MinimumOsAttributeName: + platformInfo.AttributeType = OsAttrbiteType.MinimumOSPlatformAttribute; break; + case ObsoleteAttributeName: + platformInfo.AttributeType = OsAttrbiteType.ObsoletedInOSPlatformAttribute; break; + case RemovedAttributeName: + platformInfo.AttributeType = OsAttrbiteType.RemovedInOSPlatformAttribute; break; + default: + platformInfo.AttributeType = OsAttrbiteType.None; break; + } + + platformInfo.Version = new int[4]; + Match match = s_osParseRegex.Match(osAttibute.ConstructorArguments[0].Value.ToString()); + if (match.Success) + { + platformInfo.OsPlatform = match.Groups[1].Value; + for (int i = 3; i < 7; i++) + { + if (!string.IsNullOrEmpty(match.Groups[i].Value)) + { + platformInfo.Version[i - 3] = int.Parse(match.Groups[i].Value, CultureInfo.InvariantCulture); + } + } + } + + return platformInfo; + } + + public override bool Equals(object obj) + { + if (obj is OsAttributeInfo info) + { + return Equals(info); + } + return false; + } + + public override int GetHashCode() => + HashUtilities.Combine(AttributeType.GetHashCode(), OsPlatform?.GetHashCode(), Version.GetHashCode()); + + public static bool operator ==(OsAttributeInfo left, OsAttributeInfo right) => left.Equals(right); + + public static bool operator !=(OsAttributeInfo left, OsAttributeInfo right) => !(left == right); + + public bool Equals(OsAttributeInfo other) + { + if (AttributeType == other.AttributeType && OsPlatform == other.OsPlatform) + { + for (int i = 0; i < 4; i++) + { + if (Version[i] != other.Version[i]) + { + return false; + } + } + 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 1c715ae195..daaef827de 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1359,13 +1359,22 @@ Use `{0}` instead of Range-based indexers on an array - - TODO + + Call of platform dependent API TODO Platform checks:'{0}' - - TODO + + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + '{0}' requires {1} {2} version or later. + + + '{0}' has been deprecated since {1} {2} version. + + + '{0}' has been removed since {1} {2} version. \ 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 72d64d9fdb..4dd2e514ce 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1377,6 +1377,31 @@ Metody P/Invoke nemají být viditelné + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1682,21 +1707,11 @@ Zkontrolujte dotazy SQL pro chyby zabezpečení - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie Nastavit HttpOnly na hodnotu true pro HttpCookie 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 58fcda4cdb..7227a8d577 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1377,6 +1377,31 @@ P/Invokes dürfen nicht sichtbar sein + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1682,21 +1707,11 @@ SQL-Abfragen auf Sicherheitsrisiken überprüfen - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie HttpOnly für HttpCookie auf TRUE festlegen 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 440c746924..68b46aeee5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1377,6 +1377,31 @@ Los elementos P/Invoke no deben estar visibles + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1682,21 +1707,11 @@ Revisar consultas SQL para comprobar si tienen vulnerabilidades de seguridad - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie Establecer HttpOnly en true para HttpCookie 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 28e6894628..bcb3c8b2f5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1377,6 +1377,31 @@ Les P/Invoke ne doivent pas être visibles + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1682,21 +1707,11 @@ Vérifier si les requêtes SQL présentent des failles de sécurité - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie Affectez la valeur true à HttpOnly pour HttpCookie 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 b2f360edac..75d6617b6a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1377,6 +1377,31 @@ I metodi P/Invoke non devono essere visibili + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1682,21 +1707,11 @@ Controllare l'eventuale vulnerabilità di sicurezza delle query SQL - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie Impostare HttpOnly su true per HttpCookie 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 3326f35a69..f694608263 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1377,6 +1377,31 @@ P/Invokes は参照可能にすることはできません + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1682,21 +1707,11 @@ SQL クエリのセキュリティ脆弱性を確認 - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie HttpCookie で HttpOnly を true に設定する 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 a18ce01c45..d88c0abc06 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1377,6 +1377,31 @@ P/Invokes를 표시하지 않아야 합니다. + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1682,21 +1707,11 @@ 보안상 취약한 부분이 있는지 SQL 쿼리를 검토하십시오. - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie HttpCookie에 대해 HttpOnly를 true로 설정 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 5f773eaca1..7426768594 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1378,6 +1378,31 @@ Elementy P/Invoke nie powinny być widoczne + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1683,21 +1708,11 @@ Sprawdź zapytania SQL pod kątem luk w zabezpieczeniach - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie Ustaw element HttpOnly na wartość true dla elementu HttpCookie 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 1bfe6780a5..7636455b88 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 @@ -1377,6 +1377,31 @@ P/Invokes não deve ser visível + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1682,21 +1707,11 @@ Revisar as consultas SQL em busca de vulnerabilidades de segurança - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie Definir HttpOnly como true para HttpCookie 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 25c7a0f23a..a08c0ec71b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1377,6 +1377,31 @@ Методы P/Invoke не должны быть видимыми + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1682,21 +1707,11 @@ Проверка запросов SQL на уязвимости безопасности - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie Установите для параметра HttpOnly объекта HttpCookie значение true 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 31d842c0df..392fb528c5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1377,6 +1377,31 @@ P/Invokes görünür olmamalıdır + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1682,21 +1707,11 @@ SQL sorgularını güvenlik açıkları için inceleyin - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie HttpCookie için HttpOnly'yi true olarak ayarlayın 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 3afa20144d..0446f530f2 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 @@ -1377,6 +1377,31 @@ P/Invokes 应该是不可见的 + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1682,21 +1707,11 @@ 检查 SQL 查询是否存在安全漏洞 - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie 将 HttpCookie 的 HttpOnly 设置为 true 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 b28d29514a..ffd94963b8 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 @@ -1377,6 +1377,31 @@ 不應看得見 P/Invoke + + Using platform dependendent API on a component makes the code no longer work across all platforms. + Using platform dependendent API on a component makes the code no longer work across all platforms. + + + + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + + + + '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version. + + + + Call of platform dependent API + Call of platform dependent API + + + + '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or 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. 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. @@ -1682,21 +1707,11 @@ 必須檢閱 SQL 查詢中是否有安全性弱點 - - TODO - TODO - - TODO Platform checks:'{0}' TODO Platform checks:'{0}' - - TODO - TODO - - Set HttpOnly to true for HttpCookie 針對 HttpCookie 將 HttpOnly 設為 true diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs new file mode 100644 index 0000000000..176b2bed36 --- /dev/null +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -0,0 +1,673 @@ +// 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.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.NetCore.Analyzers.InteropServices.PlatformCompatabilityAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests +{ + public class Test + { + public void M1() + { + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 1, 1)) + M2(); + } + [MinimumOSPlatform("Windows10.1.1.1")] + public void M2() + { + } + } + public class PlatformCompatabilityAnalyzerTests + { + [Fact] + public async Task OsDependentMethodCalledWithoutSuppressionWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + public void M1() + { + [|M2()|]; + } + [MinimumOSPlatform(""Windows10.1.1.1"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task OsDependentMethodCalledFromIntanceWithoutSuppressionWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + private B field = new B(); + public void M1() + { + [|field.M2()|]; + } +} +public class B +{ + [MinimumOSPlatform(""Windows10.1.1.1"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task OsDependentMethodCalledFromOtherNsIntanceWarns() + { + var source = @" +using System.Runtime.Versioning; +using Ns; + +public class Test +{ + private B field = new B(); + public void M1() + { + [|field.M2()|]; + } +} + +namespace Ns +{ + public class B + { + [MinimumOSPlatform(""Windows10.1.1.1"")] + public void M2() + { + } + } +} +" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task MethodOfOsDependentClassCalledWithoutSuppressionWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + public void M1() + { + OsDependentClass odc = new OsDependentClass(); + [|odc.M2()|]; + } +} +[MinimumOSPlatform(""Windows10.1.2.3"")] +public class OsDependentClass +{ + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + /* [Fact] TODO find out how to pass 2 sources + public async Task MethodOfOsDependentAssemblyCalledWithoutSuppressionWarns() + { + var source = @" + using System.Runtime.Versioning; + using ns; + public class Test + { + public void M1() + { + OsDependentClass odc = new OsDependentClass(); + odc.M2(); + } + } + [assembly:MinimumOSPlatform(""Windows10.1.2.3"")] + namespace ns + { + public class OsDependentClass + { + public void M2() + { + } + } + } +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source, + VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer2.Rule).WithSpan(10, 21, 10, 29).WithArguments("M2", "Windows", "10.1.2.3")); + }*/ + + [Theory] + [InlineData("10.1.2.3", "10.1.2.3", false)] + [InlineData("10.1.2.3", "10.1.3.3", false)] + [InlineData("10.1.2.3", "10.1.3.1", false)] + [InlineData("10.1.2.3", "11.1.2.3", false)] + [InlineData("10.1.2.3", "10.2.2.0", false)] + [InlineData("10.1.2.3", "10.1.1.3", true)] + [InlineData("10.1.2.3", "10.1.1.4", true)] + [InlineData("10.1.2.3", "10.0.1.9", true)] + [InlineData("10.1.2.3", "8.2.3.3", true)] + public async Task MethodOfOsDependentClassSuppressedWithAddedAttribute(string dependentVersion, string suppressingVersion, bool warn) + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + [MinimumOSPlatform(""Windows" + suppressingVersion + @""")] + public void M1() + { + OsDependentClass odc = new OsDependentClass(); + odc.M2(); + } +} + +[MinimumOSPlatform(""Windows" + dependentVersion + @""")] +public class OsDependentClass +{ + public void M2() + { + } +} +" + MockPlatformApiSource; + + if (warn) + await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.AddedRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + else + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Theory] + [InlineData("10.1.2.3", "10.1.2.3", false)] + [InlineData("10.1.2.3", "10.1.3.3", true)] + [InlineData("10.1.2.3", "10.1.3.1", true)] + [InlineData("10.1.2.3", "11.1.2.3", true)] + [InlineData("10.1.2.3", "10.2.2.0", true)] + [InlineData("10.1.2.3", "10.1.1.3", false)] + [InlineData("10.1.2.3", "10.1.1.4", false)] + [InlineData("10.1.2.3", "10.1.0.1", false)] + [InlineData("10.1.2.3", "8.2.3.4", false)] + public async Task MethodOfOsDependentClassSuppressedWithObsoleteAttribute(string dependentVersion, string suppressingVersion, bool warn) + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + [ObsoletedInOSPlatform(""Windows" + suppressingVersion + @""")] + public void M1() + { + OsDependentClass odc = new OsDependentClass(); + odc.M2(); + } + } + +[ObsoletedInOSPlatform(""Windows" + dependentVersion + @""")] +public class OsDependentClass +{ + public void M2() + { + } +} +" + MockPlatformApiSource; + + if (warn) + await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + else + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Theory] + [InlineData("10.1.2.3", "10.1.2.3", false)] + [InlineData("10.1.2.3", "10.1.3.3", true)] + [InlineData("10.1.2.3", "10.1.3.1", true)] + [InlineData("10.1.2.3", "11.1.2.3", true)] + [InlineData("10.1.2.3", "10.2.2.0", true)] + [InlineData("10.1.2.3", "10.1.1.3", false)] + [InlineData("10.1.2.3", "10.1.1.4", false)] + [InlineData("10.1.2.3", "10.1.0.1", false)] + [InlineData("10.1.2.3", "8.2.3.4", false)] + public async Task MethodOfOsDependentClassSuppressedWithRemovedAttribute(string dependentVersion, string suppressingVersion, bool warn) + { + var source = @" + using System.Runtime.Versioning; + +public class Test +{ + [RemovedInOSPlatform(""Windows" + suppressingVersion + @""")] + public void M1() + { + OsDependentClass odc = new OsDependentClass(); + odc.M2(); + } +} + +[RemovedInOSPlatform(""Windows" + dependentVersion + @""")] +public class OsDependentClass +{ + public void M2() + { + } +} +" + MockPlatformApiSource; + + if (warn) + await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RemovedRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + else + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Theory] + [InlineData("", true)] + [InlineData("build_property.TargetFramework=net5.0", true)] + [InlineData("build_property.TargetFramework=net472", true)] + [InlineData("build_property.TargetFramework=net5.0-linux", true)] + [InlineData("build_property.TargetFramework=net5.0-windows", true)] + [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.1", false)] + [InlineData("build_property.TargetFramework=net5.0-windows10.2", false)] + [InlineData("build_property.TargetFramework=net5.0-windows11", false)] + [InlineData("build_property.TargetFramework=net5.0-windows10", true)] + [InlineData("build_property.TargetFramework=net5.0-windows11\nbuild_property.TargetPlatformMinVersion=10;", true)] + [InlineData("build_property.TargetFramework=net5.0-windows10.2\nbuild_property.TargetPlatformMinVersion=10.1;", true)] + [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.2\nbuild_property.TargetPlatformMinVersion=10.0.0.1;", true)] + [InlineData("build_property.TargetFramework=net5.0-windows10.2.1\nbuild_property.TargetPlatformMinVersion=9.1.1.1;", true)] + public async Task TfmAndTargetPlatformMinVersionWithAddedAttribute(string editorConfigText, bool expectDiagnostic) + { + var invocation = expectDiagnostic ? @"[|M2()|]" : "M2()"; + await new VerifyCS.Test + { + TestState = + { + Sources = + { + $@" +using System.Runtime.Versioning; + +public class Test +{{ + public void M1() + {{ + {invocation}; + }} + [MinimumOSPlatform(""Windows10.1.1.1"")] + public void M2() + {{ + }} +}} +" + MockPlatformApiSource + }, + AdditionalFiles = { (".editorconfig", editorConfigText) } + }, + MarkupOptions = MarkupOptions.UseFirstDescriptor + }.RunAsync(); + } + + [Theory] + [InlineData("", true)] + [InlineData("build_property.TargetFramework=net5.0", true)] + [InlineData("build_property.TargetFramework=net472", false)] // TODO because no version in TFM, version is set to 0.0.0.0 + [InlineData("build_property.TargetFramework=net5.0-linux", true)] + [InlineData("build_property.TargetFramework=net5.0-windows", false)] // Same here + [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.1", false)] + [InlineData("build_property.TargetFramework=net5.0-windows10.2", true)] + [InlineData("build_property.TargetFramework=net5.0-windows11", true)] + [InlineData("build_property.TargetFramework=net5.0-windows10", false)] + [InlineData("build_property.TargetFramework=net5.0-windows11\nbuild_property.TargetPlatformMinVersion=10;", false)] + [InlineData("build_property.TargetFramework=net5.0-windows10.2\nbuild_property.TargetPlatformMinVersion=10.1;", false)] + [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.2\nbuild_property.TargetPlatformMinVersion=10.0.0.1;", false)] + [InlineData("build_property.TargetFramework=net5.0-windows10.2.1\nbuild_property.TargetPlatformMinVersion=9.1.1.1;", false)] + public async Task TfmAndTargetPlatformMinVersionWithObsoleteAttribute(string editorConfigText, bool expectDiagnostic) + { + var invocation = expectDiagnostic ? @"[|M2()|]" : "M2()"; + await new VerifyCS.Test + { + TestState = + { + Sources = + { +$@" +using System.Runtime.Versioning; + +public class Test +{{ + public void M1() + {{ + {invocation}; + }} + [ObsoletedInOSPlatform(""Windows10.1.1.1"")] + public void M2() + {{ + }} +}} +" + MockPlatformApiSource + }, + AdditionalFiles = { (".editorconfig", editorConfigText) } + }, + MarkupOptions = MarkupOptions.UseFirstDescriptor + }.RunAsync(); + } + + [Fact] + public async Task GuardedCalled_SimpleIf_NotWarns() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + M2(); + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_MultipleSimpleIfTests() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + M2(); + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 10, 1, 2, 3)) + [|M2()|]; + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + M2(); + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 8, 1, 2, 3)) + [|M2()|]; + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseTest() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + [|M2()|]; + + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + M2(); + } + else + { + [|M2()|]; + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseIfElseTest() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + [|M2()|]; + + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + M2(); + } + else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 11)) + { + [|M2()|]; + } + else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + { + // M2(); TODO: this is bug, fix it + } + else + { + [|M2()|]; + } + + [|M2()|]; + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseTestWithNegation() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + [|M2()|]; + else + M2(); + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseIfElseTestWithNegation() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + [|M2()|]; + else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 1, 1)) + M2(); + else + M2(); + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + private readonly string MockPlatformApiSource = @" +namespace System.Runtime.Versioning +{ + public abstract class OSPlatformAttribute : Attribute + { + private protected OSPlatformAttribute(string platformName) + { + PlatformName = platformName; + } + + public string PlatformName { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, + AllowMultiple = false, Inherited = false)] + public sealed class TargetPlatformAttribute : OSPlatformAttribute + { + public TargetPlatformAttribute(string platformName) : base(platformName) + { } + } + + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Event | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] + public sealed class MinimumOSPlatformAttribute : OSPlatformAttribute + { + public MinimumOSPlatformAttribute(string platformName) : base(platformName) + { } + } + + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Event | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] + public sealed class RemovedInOSPlatformAttribute : OSPlatformAttribute + { + public RemovedInOSPlatformAttribute(string platformName) : base(platformName) + { } + } + + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Event | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] + public sealed class ObsoletedInOSPlatformAttribute: OSPlatformAttribute + { + public ObsoletedInOSPlatformAttribute(string platformName) : base(platformName) + { } + public ObsoletedInOSPlatformAttribute(string platformName, string message) : base(platformName) + { + Message = message; + } + public string Message { get; } + public string Url { get; set; } + } +} + +namespace System.Runtime.InteropServices +{ + public static class RuntimeInformationHelper + { +#pragma warning disable CA1801, IDE0060 // Review unused parameters + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major) + { + return true; + } + + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor) + { + return true; + } + + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build) + { + return true; + } + + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build, int revision) + { + return true; + } + + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major) + { + return false; + } + + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor) + { + return false; + } + + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build) + { + return false; + } + + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build, int revision) + { + return false; + } + +#pragma warning restore CA1801, IDE0060 // Review unused parameters + } +}"; + } +} diff --git a/src/Test.Utilities/CSharpCodeFixVerifier`2.cs b/src/Test.Utilities/CSharpCodeFixVerifier`2.cs index 466038c5cb..345bc3bacf 100644 --- a/src/Test.Utilities/CSharpCodeFixVerifier`2.cs +++ b/src/Test.Utilities/CSharpCodeFixVerifier`2.cs @@ -28,6 +28,7 @@ public static async Task VerifyAnalyzerAsync(string source, params DiagnosticRes var test = new Test { TestCode = source, + MarkupOptions = MarkupOptions.UseFirstDescriptor }; test.ExpectedDiagnostics.AddRange(expected); diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt index b88142e47c..84682972cb 100644 --- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt +++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -16,7 +16,7 @@ Performance: HA, CA1800-CA1836 Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5403 Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2249 Naming: CA1700-CA1726 -Interoperability: CA1400-CA1416 +Interoperability: CA1400-CA1417 Maintainability: CA1500-CA1509 Reliability: CA9999, CA2000-CA2016 Documentation: CA1200-CA1200 diff --git a/src/Utilities/Compiler/WellKnownTypeNames.cs b/src/Utilities/Compiler/WellKnownTypeNames.cs index ea739d1406..56a9805959 100644 --- a/src/Utilities/Compiler/WellKnownTypeNames.cs +++ b/src/Utilities/Compiler/WellKnownTypeNames.cs @@ -240,6 +240,7 @@ internal static class WellKnownTypeNames public const string SystemRuntimeSerializationOnSerializingAttribute = "System.Runtime.Serialization.OnSerializingAttribute"; public const string SystemRuntimeSerializationSerializationInfo = "System.Runtime.Serialization.SerializationInfo"; public const string SystemRuntimeSerializationStreamingContext = "System.Runtime.Serialization.StreamingContext"; + public const string SystemRuntimeVersioningOSPlatformAttribute = "System.Runtime.Versioning.OSPlatformAttribute"; public const string SystemSecurityAuthenticationSslProtocols = "System.Security.Authentication.SslProtocols"; public const string SystemSecurityCryptographyAesCcm = "System.Security.Cryptography.AesCcm"; public const string SystemSecurityCryptographyAesGcm = "System.Security.Cryptography.AesGcm"; From 176adea8577e40ae8367d4a0f3d47fd11d1c21bc Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Wed, 8 Jul 2020 19:00:20 -0700 Subject: [PATCH 02/48] Support more flow scenarios and add more tests --- .../PlatformCompatabilityAnalyzer.cs | 39 +- .../PlatformCompatabilityAnalyzerTests.cs | 551 +++++++++++++++++- 2 files changed, 566 insertions(+), 24 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index c065e28a3c..33a2c23139 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -139,25 +139,40 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, } else { - if (value.AnalysisValues.FirstOrDefault() is RuntimeMethodInfo info) + bool guarded = false; + foreach (var analysisValue in value.AnalysisValues) { - if (!info.Negated) + if (analysisValue is RuntimeMethodInfo info) { - OsAttributeInfo attribute = platformSpecificOperation.Value.First(); - if (attribute.OsPlatform!.Equals(info.PlatformPropertyName, StringComparison.InvariantCultureIgnoreCase)) + if (!info.Negated) { - if (info.InvokedPlatformCheckMethodName.Equals(s_platformCheckMethods[0], StringComparison.InvariantCulture)) + OsAttributeInfo attribute = platformSpecificOperation.Value.First(); + if (attribute.OsPlatform!.Equals(info.PlatformPropertyName, StringComparison.InvariantCultureIgnoreCase)) { - if (AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) + if (info.InvokedPlatformCheckMethodName.Equals(s_platformCheckMethods[0], StringComparison.InvariantCulture)) { - continue; + if (attribute.AttributeType == OsAttrbiteType.MinimumOSPlatformAttribute && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) + { + guarded = true; + } + } + else + { + if ((attribute.AttributeType == OsAttrbiteType.ObsoletedInOSPlatformAttribute || attribute.AttributeType == OsAttrbiteType.RemovedInOSPlatformAttribute) + && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) + { + guarded = true; + } } } } } } - context.ReportDiagnostic(platformSpecificOperation.Key.CreateDiagnostic(SwitchRule(parsedAttribute.AttributeType), platformSpecificOperation.Key.TargetMethod.Name, - parsedAttribute.OsPlatform!, $"{parsedAttribute.Version[0]}.{parsedAttribute.Version[1]}.{parsedAttribute.Version[2]}.{parsedAttribute.Version[3]}")); + if (!guarded) + { + context.ReportDiagnostic(platformSpecificOperation.Key.CreateDiagnostic(SwitchRule(parsedAttribute.AttributeType), platformSpecificOperation.Key.TargetMethod.Name, + parsedAttribute.OsPlatform!, $"{parsedAttribute.Version[0]}.{parsedAttribute.Version[1]}.{parsedAttribute.Version[2]}.{parsedAttribute.Version[3]}")); + } } } } @@ -212,7 +227,7 @@ private static void AnalyzeInvocationOperation(IInvocationOperation operation, I } } - suppressed = suppressed || Suppress(parsedAttribute, context.ContainingSymbol); + suppressed = suppressed || IsSuppressedByAttribute(parsedAttribute, context.ContainingSymbol); if (!suppressed) { @@ -313,7 +328,7 @@ private static List GetApplicableAttributes(ImmutableArray suppressingVersion[i]) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index 176b2bed36..3bc724cd96 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -11,18 +11,6 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests { - public class Test - { - public void M1() - { - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 1, 1)) - M2(); - } - [MinimumOSPlatform("Windows10.1.1.1")] - public void M2() - { - } - } public class PlatformCompatabilityAnalyzerTests { [Fact] @@ -475,7 +463,11 @@ void M1() } else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) { - // M2(); TODO: this is bug, fix it + [|M2()|]; + } + else if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12)) + { + M2(); } else { @@ -548,6 +540,539 @@ public void M2() await VerifyCS.VerifyAnalyzerAsync(source); } + [Fact] + public async Task GuardedCall_SimpleIfTestWithNegationAndReturn() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + return; + M2(); + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfTestWithLogicalAnd() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && + RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + { + M2(); + } + + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) && + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12)) + { + M2(); + } + + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) && + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 12)) + { + [|M2()|]; + } + + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && 1 == 1) + { + M2(); + } + + [|M2()|]; + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseTestWithLogicalAnd() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && + RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + { + M2(); + } + else + { + [|M2()|]; + } + + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) && + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 12)) + { + [|M2()|]; + } + else + { + [|M2()|]; + } + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfTestWithLogicalOr() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || + RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + { + [|M2()|]; + } + + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + { + [|M2()|]; + } + + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 12) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + { + [|M2()|]; + } + + [|M2()|]; + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseTestWithLogicalOr() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || + RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + { + [|M2()|]; + } + else + { + [|M2()|]; + } + + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + { + [|M2()|]; + } + else + { + [|M2()|]; + } + + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + [|M2()|]; // Even it is not meaningful check i think it is a bug, it shouldn't warn + } + else + { + [|M2()|]; + } + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseIfElseTestWithLogicalOr() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 5, 1)) + { + [|M2()|]; + } + else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 9)) + { + [|M2()|]; + } + else + [|M2()|]; + + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + { + [|M2()|]; + } + else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + M2(); + } + else + { + [|M2()|]; + } + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseIfTestWithLogicalOrAnd() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + [|M2()|]; + + if((RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1)) && + (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 2))) + { + [|M2()|]; + } + else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 13) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 3) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 4)) + { + [|M2()|]; + } + else + { + [|M2()|]; + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" ++ MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedWith_ControlFlowAndMultipleChecks() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + [|M2()|]; + + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 8)) + { + [|M2()|]; + + if (RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 2, 0)) + { + [|M2()|]; + } + else if (!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2, 1)) + { + [|M2()|]; + } + else + { + M2(); + } + + [|M2()|]; + } + else + { + [|M2()|]; + } + + if (IsWindows11OrLater()) + { + [|M2()|]; // TODO: support this + } + + [|M2()|]; + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } + + bool IsWindows11OrLater() + { + return RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11, 0, 0, 0); + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedWith_DebugAssertAnalysisTest() + { + var source = @" +using System.Diagnostics; +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + [|M2()|]; + + Debug.Assert(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)); + + M2(); + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedWith_ResultSavedInLocal() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + [|M2()|]; + + var x1 = RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11); + var x2 = RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1); + + if (x1) + { + M2(); + } + + if (x1 || x2) + { + [|M2()|]; + } + + if (x2) + [|M2()|]; + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedWith_VersionSavedInLocal() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + [|M2()|]; + + var v11 = 11; + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, v11)) + { + [|M2()|]; // TODO: fix this scenario + } + + [|M2()|]; + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task PlatformSavedInLocal_NotYetSupported() // TODO do we want to support it? + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + [|M2()|]; + + var platform = OSPlatform.Windows; + if (RuntimeInformationHelper.IsOSPlatformOrLater(platform, 11)) + { + [|M2()|]; + } + + [|M2()|]; + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task UnrelatedConditionCheckDoesNotInvalidateState() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1(bool flag1, bool flag2) + { + [|M2()|]; + + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + M2(); + + if (flag1 || flag2) + { + M2(); + } + else + { + M2(); + } + + M2(); + } + + if (flag1 || flag2) + { + [|M2()|]; + } + else + { + [|M2()|]; + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + private readonly string MockPlatformApiSource = @" namespace System.Runtime.Versioning { From 9859abe162294dd9069fde0eb94f51dffab5603d Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 9 Jul 2020 13:06:52 -0700 Subject: [PATCH 03/48] Enable value content analysis, add few more tests --- .../PlatformCompatabilityAnalyzer.cs | 8 +-- .../PlatformCompatabilityAnalyzerTests.cs | 63 +++++++++++++++---- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 33a2c23139..7abe4f6a9a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -92,7 +92,6 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, var parsedTfms = ParseTfm(context.Options, context.OwningSymbol, context.Compilation, context.CancellationToken); #pragma warning restore CA2000 var platformSpecificOperations = PooledDictionary>.GetInstance(); - var needsValueContentAnalysis = false; context.RegisterOperationAction(context => { @@ -112,17 +111,14 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); var analysisResult = GlobalFlowStateAnalysis.TryGetOrComputeResult( cfg, context.OwningSymbol, CreateOperationVisitor, - wellKnownTypeProvider, context.Options, AddedRule, performPointsToAnalysis: needsValueContentAnalysis, - performValueContentAnalysis: needsValueContentAnalysis, context.CancellationToken, + wellKnownTypeProvider, context.Options, AddedRule, performPointsToAnalysis: true, + performValueContentAnalysis: true, context.CancellationToken, out var pointsToAnalysisResult, out var valueContentAnalysisResult); if (analysisResult == null) { return; } - Debug.Assert(valueContentAnalysisResult == null || needsValueContentAnalysis); - Debug.Assert(pointsToAnalysisResult == null || needsValueContentAnalysis); - foreach (var platformSpecificOperation in platformSpecificOperations) { var value = analysisResult[platformSpecificOperation.Key.Kind, platformSpecificOperation.Key.Syntax]; diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index 3bc724cd96..d72f29bbe4 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -874,11 +874,6 @@ void M1() [|M2()|]; } - if (IsWindows11OrLater()) - { - [|M2()|]; // TODO: support this - } - [|M2()|]; } @@ -886,11 +881,6 @@ void M1() void M2() { } - - bool IsWindows11OrLater() - { - return RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11, 0, 0, 0); - } }" + MockPlatformApiSource; await VerifyCS.VerifyAnalyzerAsync(source); @@ -979,7 +969,7 @@ void M1() var v11 = 11; if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, v11)) { - [|M2()|]; // TODO: fix this scenario + M2(); } [|M2()|]; @@ -1073,6 +1063,57 @@ void M2() await VerifyCS.VerifyAnalyzerAsync(source); } + [Theory] + [InlineData("dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive")] + public async Task InterproceduralAnalysisTest(string editorconfig) + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + {|#0:M2()|}; + + if (IsWindows11OrLater()) + { + M2(); + } + + {|#1:M2()|}; + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } + + bool IsWindows11OrLater() + { + return RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11); + } +}" + MockPlatformApiSource; + + var test = new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (".editorconfig", editorconfig) } + } + }; + + test.ExpectedDiagnostics.AddRange(new[] + { + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.AddedRule).WithLocation(0).WithArguments("M2", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.AddedRule).WithLocation(1).WithArguments("M2", "Windows", "10.1.2.3") + }); + + await test.RunAsync(); + } + private readonly string MockPlatformApiSource = @" namespace System.Runtime.Versioning { From c7a4bc1b05781834f5dcf758fefb6a5690110dd4 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 9 Jul 2020 17:37:08 -0700 Subject: [PATCH 04/48] Fix bug caused from merge --- .../PlatformCompatabilityAnalyzer.OperationVisitor.cs | 2 +- .../InteropServices/PlatformCompatabilityAnalyzer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs index 01f2815bf7..00b7db8f36 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs @@ -34,7 +34,7 @@ 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) && !visitedArguments.IsEmpty) { return RuntimeMethodInfo.TryDecode(method, visitedArguments, DataFlowAnalysisContext.ValueContentAnalysisResultOpt, _osPlatformType, out var platformInfo) ? new GlobalFlowStateAnalysisValueSet(platformInfo) : diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 728824100e..69dcd3e720 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -190,7 +190,7 @@ static ImmutableArray GetPlatformCheckMethods(INamedTypeSymbol ru foreach (var method in methods) { if (s_platformCheckMethods.Contains(method.Name) && - method.Parameters.IsEmpty && + !method.Parameters.IsEmpty && method.Parameters[0].Type.Equals(osPlatformType) && method.Parameters.Skip(1).All(p => p.Type.SpecialType == SpecialType.System_Int32)) { From 42245ba4bbe45b84a77d7dc11838cef9e131a81b Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Sun, 12 Jul 2020 21:32:13 -0700 Subject: [PATCH 05/48] Enum, property support. Use string comparison ever type, support string overload of guard method --- .../MockOsPlatformAttribute.cs | 9 +- ... => PlatformCompatabilityAnalyzer.Data.cs} | 162 +++++--- ...mCompatabilityAnalyzer.OperationVisitor.cs | 12 + .../PlatformCompatabilityAnalyzer.cs | 361 +++++++----------- ...ePlatformCheckAnalyzer.OperationVisitor.cs | 3 +- .../PlatformCompatabilityAnalyzerTests.cs | 336 +++++++++++++--- src/Utilities/Compiler/WellKnownTypeNames.cs | 1 - 7 files changed, 549 insertions(+), 335 deletions(-) rename src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/{PlatformCompatabilityAnalyzer.RuntimeMethodInfo.cs => PlatformCompatabilityAnalyzer.Data.cs} (57%) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/MockOsPlatformAttribute.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/MockOsPlatformAttribute.cs index 0e512303db..785211e896 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/MockOsPlatformAttribute.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/MockOsPlatformAttribute.cs @@ -27,7 +27,8 @@ public TargetPlatformAttribute(string platformName) : base(platformName) AttributeTargets.Method | AttributeTargets.Module | AttributeTargets.Property | - AttributeTargets.Struct, + AttributeTargets.Struct | + AttributeTargets.Field, AllowMultiple = true, Inherited = false)] public sealed class MinimumOSPlatformAttribute : OSPlatformAttribute { @@ -42,7 +43,8 @@ public MinimumOSPlatformAttribute(string platformName) : base(platformName) AttributeTargets.Method | AttributeTargets.Module | AttributeTargets.Property | - AttributeTargets.Struct, + AttributeTargets.Struct | + AttributeTargets.Field, AllowMultiple = true, Inherited = false)] public sealed class RemovedInOSPlatformAttribute : OSPlatformAttribute { @@ -57,7 +59,8 @@ public RemovedInOSPlatformAttribute(string platformName) : base(platformName) AttributeTargets.Method | AttributeTargets.Module | AttributeTargets.Property | - AttributeTargets.Struct, + AttributeTargets.Struct | + AttributeTargets.Field, AllowMultiple = true, Inherited = false)] public sealed class ObsoletedInOSPlatformAttribute : OSPlatformAttribute { diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.RuntimeMethodInfo.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs similarity index 57% rename from src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.RuntimeMethodInfo.cs rename to src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index 0b4f9fc3d3..a424bb5c3a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.RuntimeMethodInfo.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Text; using Analyzer.Utilities; using Analyzer.Utilities.PooledObjects; using Microsoft.CodeAnalysis; @@ -20,9 +19,103 @@ namespace Microsoft.NetCore.Analyzers.InteropServices public partial class PlatformCompatabilityAnalyzer { + private enum PlatformAttrbiteType + { + None, MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute, TargetPlatformAttribute + } + + private struct PlatformAttrbiuteInfo : IEquatable + { + public PlatformAttrbiteType AttributeType { get; set; } + public string OsPlatformName { get; set; } + public Version Version { get; set; } + + public static bool TryParseAttributeData(AttributeData osAttibute, out PlatformAttrbiuteInfo parsedAttribute) + { + parsedAttribute = new PlatformAttrbiuteInfo(); + switch (osAttibute.AttributeClass.Name) + { + case MinimumOsAttributeName: + parsedAttribute.AttributeType = PlatformAttrbiteType.MinimumOSPlatformAttribute; break; + case ObsoleteAttributeName: + parsedAttribute.AttributeType = PlatformAttrbiteType.ObsoletedInOSPlatformAttribute; break; + case RemovedAttributeName: + parsedAttribute.AttributeType = PlatformAttrbiteType.RemovedInOSPlatformAttribute; break; + case TargetPlatformAttributeName: + parsedAttribute.AttributeType = PlatformAttrbiteType.TargetPlatformAttribute; break; + default: + parsedAttribute.AttributeType = PlatformAttrbiteType.None; break; + } + + if (TryParsePlatformString(osAttibute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version)) + { + parsedAttribute.OsPlatformName = platformName; + parsedAttribute.Version = version; + return true; + } + + return false; + } + + public override bool Equals(object obj) + { + if (obj is PlatformAttrbiuteInfo info) + { + return Equals(info); + } + return false; + } + + public override int GetHashCode() => HashUtilities.Combine(AttributeType.GetHashCode(), OsPlatformName.GetHashCode(), Version.GetHashCode()); + + public static bool operator ==(PlatformAttrbiuteInfo left, PlatformAttrbiuteInfo right) => left.Equals(right); + + public static bool operator !=(PlatformAttrbiuteInfo left, PlatformAttrbiuteInfo right) => !(left == right); + + public bool Equals(PlatformAttrbiuteInfo other) => + AttributeType == other.AttributeType && OsPlatformName == other.OsPlatformName && Version.Equals(other.Version); + + internal static bool TryParseTfmString(string osString, out PlatformAttrbiuteInfo parsedTfm) + { + parsedTfm = new PlatformAttrbiuteInfo(); + parsedTfm.AttributeType = PlatformAttrbiteType.None; + + if (TryParsePlatformString(osString, out string platformName, out Version? version)) + { + parsedTfm.OsPlatformName = platformName; + parsedTfm.Version = version; + } + return parsedTfm.Version != null; + } + } + + private static bool TryParsePlatformString(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; + } + else + { + break; + } + } + } + + return false; + } + private struct RuntimeMethodInfo : IAbstractAnalysisValue, IEquatable { - private RuntimeMethodInfo(string invokedPlatformCheckMethodName, string platformPropertyName, int[] version, bool negated) + private RuntimeMethodInfo(string invokedPlatformCheckMethodName, string platformPropertyName, Version version, bool negated) { InvokedPlatformCheckMethodName = invokedPlatformCheckMethodName ?? throw new ArgumentNullException(nameof(invokedPlatformCheckMethodName)); PlatformPropertyName = platformPropertyName ?? throw new ArgumentNullException(nameof(platformPropertyName)); @@ -32,9 +125,7 @@ private RuntimeMethodInfo(string invokedPlatformCheckMethodName, string platform public string InvokedPlatformCheckMethodName { get; } public string PlatformPropertyName { get; } -#pragma warning disable CA1819 // Properties should not return arrays - public int[] Version { get; } -#pragma warning restore CA1819 // Properties should not return arrays + public Version Version { get; } public bool Negated { get; } public IAbstractAnalysisValue GetNegatedValue() @@ -47,7 +138,17 @@ public static bool TryDecode( INamedTypeSymbol osPlatformType, [NotNullWhen(returnValue: true)] out RuntimeMethodInfo? info) { - if (!TryDecodeOSPlatform(arguments, osPlatformType, out var osPlatformProperty) || + Debug.Assert(!arguments.IsEmpty); + if (arguments[0].Value is ILiteralOperation literal && literal.Type.SpecialType == SpecialType.System_String) + { + if (literal.ConstantValue.HasValue && TryParsePlatformString(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) + { + info = new RuntimeMethodInfo(invokedPlatformCheckMethod.Name, platformName, version, negated: false); + return true; + } + } + + if (!TryDecodeOSPlatform(arguments, osPlatformType, out var osPlatformName) || !TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var osVersion)) { // Bail out @@ -55,39 +156,38 @@ public static bool TryDecode( return false; } - info = new RuntimeMethodInfo(invokedPlatformCheckMethod.Name, osPlatformProperty.Name, osVersion, negated: false); + info = new RuntimeMethodInfo(invokedPlatformCheckMethod.Name, osPlatformName, osVersion, negated: false); return true; } private static bool TryDecodeOSPlatform( ImmutableArray arguments, INamedTypeSymbol osPlatformType, - [NotNullWhen(returnValue: true)] out IPropertySymbol? osPlatformProperty) + [NotNullWhen(returnValue: true)] out string? osPlatformName) { - Debug.Assert(!arguments.IsEmpty); - return TryDecodeOSPlatform(arguments[0].Value, osPlatformType, out osPlatformProperty); + return TryDecodeOSPlatform(arguments[0].Value, osPlatformType, out osPlatformName); } private static bool TryDecodeOSPlatform( IOperation argumentValue, INamedTypeSymbol osPlatformType, - [NotNullWhen(returnValue: true)] out IPropertySymbol? osPlatformProperty) + [NotNullWhen(returnValue: true)] out string? osPlatformName) { if ((argumentValue is IPropertyReferenceOperation propertyReference) && propertyReference.Property.ContainingType.Equals(osPlatformType)) { - osPlatformProperty = propertyReference.Property; + osPlatformName = propertyReference.Property.Name; return true; } - osPlatformProperty = null; + osPlatformName = null; return false; } private static bool TryDecodeOSVersion( ImmutableArray arguments, ValueContentAnalysisResult? valueContentAnalysisResult, - [NotNullWhen(returnValue: true)] out int[]? osVersion) + [NotNullWhen(returnValue: true)] out Version? osVersion) { using var versionBuilder = ArrayBuilder.GetInstance(4, fillWithValue: 0); var index = 0; @@ -102,7 +202,7 @@ private static bool TryDecodeOSVersion( versionBuilder[index++] = osVersionPart; } - osVersion = versionBuilder.ToArray(); + osVersion = new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2], versionBuilder[3]); return true; static bool TryDecodeOSVersionPart(IArgumentOperation argument, ValueContentAnalysisResult? valueContentAnalysisResult, out int osVersionPart) @@ -133,7 +233,7 @@ static bool TryDecodeOSVersionPart(IArgumentOperation argument, ValueContentAnal public override string ToString() { - var versionStr = GetVersionString(Version, GetVersionFieldCount(Version)); + var versionStr = Version.ToString(); var result = $"{InvokedPlatformCheckMethodName};{PlatformPropertyName};{versionStr}"; if (Negated) { @@ -141,36 +241,6 @@ public override string ToString() } return result; - - static int GetVersionFieldCount(int[] version) - { - if (version[3] != 0) - { - return 4; - } - - if (version[2] != 0) - { - return 3; - } - - if (version[1] != 0) - { - return 2; - } - - return 1; - } - } - - private static string GetVersionString(int[] version, int count) - { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < count; i++) - { - builder.Append(version[i]); - } - return builder.ToString(); } public bool Equals(RuntimeMethodInfo other) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs index 00b7db8f36..7c0929d041 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs @@ -43,6 +43,18 @@ public override GlobalFlowStateAnalysisValueSet VisitInvocation_NonLambdaOrDeleg return GetValueOrDefault(value); } + + public override GlobalFlowStateAnalysisValueSet VisitPropertyReference(IPropertyReferenceOperation operation, object? argument) + { + var value = base.VisitPropertyReference(operation, argument); + return GetValueOrDefault(value); + } + + public override GlobalFlowStateAnalysisValueSet VisitFieldReference(IFieldReferenceOperation operation, object? argument) + { + var value = base.VisitFieldReference(operation, argument); + return GetValueOrDefault(value); + } } } } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 69dcd3e720..3861e07b7e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -1,12 +1,9 @@ // 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.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Globalization; using System.Linq; -using System.Runtime.Versioning; using System.Text.RegularExpressions; using System.Threading; using Analyzer.Utilities; @@ -24,6 +21,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA1417"; private static readonly ImmutableArray s_platformCheckMethods = ImmutableArray.Create("IsOSPlatformOrLater", "IsOSPlatformEarlierThan"); + private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create("MinimumOSPlatformAttribute", "ObsoletedInOSPlatformAttribute", "RemovedInOSPlatformAttribute"); private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableAddedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckAddedMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableObsoleteMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckObsoleteMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); @@ -31,15 +29,14 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckDescription), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private const char SeparatorDash = '-'; private const char SeparatorSemicolon = ';'; - private const char SeparatorDot = '.'; - private const string MinimumOsAttributeName = nameof(MinimumOSPlatformAttribute); - private const string ObsoleteAttributeName = nameof(ObsoletedInOSPlatformAttribute); - private const string RemovedAttributeName = nameof(RemovedInOSPlatformAttribute); + private const string MinimumOsAttributeName = nameof(PlatformAttrbiteType.MinimumOSPlatformAttribute); + private const string ObsoleteAttributeName = nameof(PlatformAttrbiteType.ObsoletedInOSPlatformAttribute); + private const string RemovedAttributeName = nameof(PlatformAttrbiteType.RemovedInOSPlatformAttribute); + private const string TargetPlatformAttributeName = nameof(PlatformAttrbiteType.TargetPlatformAttribute); private const string Windows = nameof(Windows); private static readonly Regex s_neutralTfmRegex = new Regex(@"^net([5-9]|standard\d|coreapp\d)\.\d$", RegexOptions.IgnoreCase); - private static readonly Regex s_osParseRegex = new Regex(@"([a-z]{3,7})((\d{1,2})\.?(\d)?\.?(\d)?\.?(\d)?)*", RegexOptions.IgnoreCase); - internal static DiagnosticDescriptor AddedRule = DiagnosticDescriptorHelper.Create(RuleId, + internal static DiagnosticDescriptor MinimumOsRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, s_localizableAddedMessage, DiagnosticCategory.Interoperability, @@ -64,7 +61,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer description: s_localizableRemovedMessage, isPortedFxCopRule: false, isDataflowRule: false); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(AddedRule, ObsoleteRule, RemovedRule); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(MinimumOsRule, ObsoleteRule, RemovedRule); public override void Initialize(AnalysisContext context) { @@ -76,28 +73,27 @@ public override void Initialize(AnalysisContext context) var typeName = WellKnownTypeNames.SystemRuntimeInteropServicesRuntimeInformation + "Helper"; if (!context.Compilation.TryGetOrCreateTypeByMetadataName(typeName, out var runtimeInformationType) || - !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesOSPlatform, out var osPlatformType) || - !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeVersioningOSPlatformAttribute, out var osAttribute)) + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesOSPlatform, out var osPlatformType)) { return; } - context.RegisterOperationBlockStartAction(context => AnalyzerOperationBlock(context, osAttribute, runtimeInformationType, osPlatformType)); + context.RegisterOperationBlockStartAction(context => AnalyzerOperationBlock(context, runtimeInformationType, osPlatformType)); }); } - private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, INamedTypeSymbol osAttribute, INamedTypeSymbol runtimeInformationType, INamedTypeSymbol osPlatformType) + private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, INamedTypeSymbol runtimeInformationType, INamedTypeSymbol osPlatformType) { #pragma warning disable CA2000 // Dispose objects before losing scope - disposed in OperationBlockEndAction. var parsedTfms = ParseTfm(context.Options, context.OwningSymbol, context.Compilation, context.CancellationToken); #pragma warning restore CA2000 - var platformSpecificOperations = PooledDictionary>.GetInstance(); + var platformSpecificOperations = PooledDictionary>.GetInstance(); context.RegisterOperationAction(context => { - AnalyzeInvocationOperation((IInvocationOperation)context.Operation, osAttribute, context, parsedTfms, ref platformSpecificOperations); + AnalyzeInvocationOperation(context.Operation, context, parsedTfms, ref platformSpecificOperations); } - , OperationKind.Invocation); + , OperationKind.Invocation, OperationKind.PropertyReference, OperationKind.FieldReference); context.RegisterOperationBlockEndAction(context => { @@ -111,7 +107,7 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); var analysisResult = GlobalFlowStateAnalysis.TryGetOrComputeResult( cfg, context.OwningSymbol, CreateOperationVisitor, wellKnownTypeProvider, - context.Options, AddedRule, performValueContentAnalysis: true, + context.Options, MinimumOsRule, performValueContentAnalysis: true, context.CancellationToken, out var valueContentAnalysisResult); if (analysisResult == null) { @@ -121,16 +117,37 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, foreach (var platformSpecificOperation in platformSpecificOperations) { var value = analysisResult[platformSpecificOperation.Key.Kind, platformSpecificOperation.Key.Syntax]; + if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown) { continue; } - OsAttributeInfo parsedAttribute = platformSpecificOperation.Value.FirstOrDefault(); + string operationName; + + if (platformSpecificOperation.Key is IInvocationOperation iOperation) + { + operationName = iOperation.TargetMethod.Name; + } + else if (platformSpecificOperation.Key is IPropertyReferenceOperation pOperation) + { + operationName = pOperation.Property.Name; + } + else if (platformSpecificOperation.Key is IFieldReferenceOperation fOperation) + { + operationName = fOperation.Field.Name; + + } + else + { + Debug.Fail("Should never happen"); + return; + } + + PlatformAttrbiuteInfo attribute = platformSpecificOperation.Value.FirstOrDefault(); if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Empty || value.Kind == GlobalFlowStateAnalysisValueSetKind.Unset) { - context.ReportDiagnostic(platformSpecificOperation.Key.CreateDiagnostic(SwitchRule(parsedAttribute.AttributeType), platformSpecificOperation.Key.TargetMethod.Name, - parsedAttribute.OsPlatform!, $"{parsedAttribute.Version[0]}.{parsedAttribute.Version[1]}.{parsedAttribute.Version[2]}.{parsedAttribute.Version[3]}")); + context.ReportDiagnostic(platformSpecificOperation.Key.CreateDiagnostic(SwitchRule(attribute.AttributeType), operationName, attribute.OsPlatformName, attribute.Version.ToString())); } else { @@ -141,19 +158,18 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, { if (!info.Negated) { - OsAttributeInfo attribute = platformSpecificOperation.Value.First(); - if (attribute.OsPlatform!.Equals(info.PlatformPropertyName, StringComparison.InvariantCultureIgnoreCase)) + if (attribute.OsPlatformName.Equals(info.PlatformPropertyName, StringComparison.InvariantCultureIgnoreCase)) { if (info.InvokedPlatformCheckMethodName.Equals(s_platformCheckMethods[0], StringComparison.InvariantCulture)) { - if (attribute.AttributeType == OsAttrbiteType.MinimumOSPlatformAttribute && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) + if (attribute.AttributeType == PlatformAttrbiteType.MinimumOSPlatformAttribute && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) { guarded = true; } } else { - if ((attribute.AttributeType == OsAttrbiteType.ObsoletedInOSPlatformAttribute || attribute.AttributeType == OsAttrbiteType.RemovedInOSPlatformAttribute) + if ((attribute.AttributeType == PlatformAttrbiteType.ObsoletedInOSPlatformAttribute || attribute.AttributeType == PlatformAttrbiteType.RemovedInOSPlatformAttribute) && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) { guarded = true; @@ -165,8 +181,7 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, } if (!guarded) { - context.ReportDiagnostic(platformSpecificOperation.Key.CreateDiagnostic(SwitchRule(parsedAttribute.AttributeType), platformSpecificOperation.Key.TargetMethod.Name, - parsedAttribute.OsPlatform!, $"{parsedAttribute.Version[0]}.{parsedAttribute.Version[1]}.{parsedAttribute.Version[2]}.{parsedAttribute.Version[3]}")); + context.ReportDiagnostic(platformSpecificOperation.Key.CreateDiagnostic(SwitchRule(attribute.AttributeType), operationName, attribute.OsPlatformName, attribute.Version.ToString())); } } } @@ -180,53 +195,56 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, return; OperationVisitor CreateOperationVisitor(GlobalFlowStateAnalysisContext context) - => new OperationVisitor(GetPlatformCheckMethods(runtimeInformationType, osPlatformType), osPlatformType, context); + => new OperationVisitor(GetPlatformCheckMethods(runtimeInformationType), osPlatformType, context); }); - static ImmutableArray GetPlatformCheckMethods(INamedTypeSymbol runtimeInformationType, INamedTypeSymbol osPlatformType) + static ImmutableArray GetPlatformCheckMethods(INamedTypeSymbol runtimeInformationType) { - 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(); + return runtimeInformationType.GetMembers().OfType().Where(m => s_platformCheckMethods.Contains(m.Name) && !m.Parameters.IsEmpty).ToImmutableArray(); } } - private static void AnalyzeInvocationOperation(IInvocationOperation operation, INamedTypeSymbol osAttribute, OperationAnalysisContext context, - PooledConcurrentSet? parsedTfms, ref PooledDictionary> platformSpecificOperations) + private static void AnalyzeInvocationOperation(IOperation operation, OperationAnalysisContext context, + PooledConcurrentSet? parsedTfms, ref PooledDictionary> platformSpecificOperations) { - var attributes = GetApplicableAttributes(operation.TargetMethod.GetAttributes(), operation.TargetMethod.ContainingType, osAttribute); - using var builder = ArrayBuilder.GetInstance(); + using var builder = ArrayBuilder.GetInstance(); + ImmutableArray attributes; + + if (operation is IInvocationOperation iOperation) + { + attributes = FindPlatformAttributes(iOperation.TargetMethod.GetAttributes(), iOperation.TargetMethod.ContainingType); + } + else if (operation is IPropertyReferenceOperation pOperation) + { + attributes = FindPlatformAttributes(pOperation.Property.GetAttributes(), pOperation.Property.ContainingType); + } + else if (operation is IFieldReferenceOperation fOperation) + { + attributes = FindPlatformAttributes(fOperation.Field.GetAttributes(), fOperation.Field.ContainingType); + } + foreach (AttributeData attribute in attributes) { bool suppressed = false; - OsAttributeInfo parsedAttribute = OsAttributeInfo.ParseAttributeData(attribute); - if (parsedTfms != null) + if (PlatformAttrbiuteInfo.TryParseAttributeData(attribute, out PlatformAttrbiuteInfo parsedAttribute)) { - foreach (OsAttributeInfo tfm in parsedTfms) + if (parsedTfms != null) { - if (tfm.OsPlatform != null && tfm.OsPlatform.Equals(parsedAttribute.OsPlatform, StringComparison.InvariantCultureIgnoreCase)) + foreach (PlatformAttrbiuteInfo tfm in parsedTfms) { - suppressed = AttributeVersionsMatch(parsedAttribute, tfm); + if (tfm.OsPlatformName.Equals(parsedAttribute.OsPlatformName, StringComparison.InvariantCultureIgnoreCase)) + { + suppressed = AttributeVersionsMatch(parsedAttribute, tfm); + } } } - } - suppressed = suppressed || IsSuppressedByAttribute(parsedAttribute, context.ContainingSymbol); + suppressed = suppressed || IsSuppressedByAttribute(parsedAttribute, context.ContainingSymbol); - if (!suppressed) - { - builder.Add(parsedAttribute); + if (!suppressed) + { + builder.Add(parsedAttribute); + } } } @@ -236,94 +254,82 @@ private static void AnalyzeInvocationOperation(IInvocationOperation operation, I } } - private static DiagnosticDescriptor SwitchRule(OsAttrbiteType attributeType) + private static DiagnosticDescriptor SwitchRule(PlatformAttrbiteType attributeType) { - if (attributeType == OsAttrbiteType.MinimumOSPlatformAttribute) - return AddedRule; - if (attributeType == OsAttrbiteType.ObsoletedInOSPlatformAttribute) + if (attributeType == PlatformAttrbiteType.MinimumOSPlatformAttribute) + return MinimumOsRule; + if (attributeType == PlatformAttrbiteType.ObsoletedInOSPlatformAttribute) return ObsoleteRule; return RemovedRule; } - private static PooledConcurrentSet? ParseTfm(AnalyzerOptions options, ISymbol containingSymbol, Compilation compilation, CancellationToken cancellationToken) - { // ((net[5-9]|netstandard\d|netcoreapp\d)\.\d(-([a-z]{3,7})(\d{1,2}\.?\d?\.?\d?\.?\d?)*)?)+ - string? tfmString = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetFramework, AddedRule, containingSymbol, compilation, cancellationToken); + private static PooledConcurrentSet? ParseTfm(AnalyzerOptions options, ISymbol containingSymbol, Compilation compilation, CancellationToken cancellationToken) + { + string? tfmString = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetFramework, MinimumOsRule, containingSymbol, compilation, cancellationToken); if (tfmString != null) { - PooledConcurrentSet platformInfos = PooledConcurrentSet.GetInstance(); + PooledConcurrentSet platformInfos = PooledConcurrentSet.GetInstance(); var tfms = tfmString.Split(SeparatorSemicolon); foreach (var tfm in tfms) { var tokens = tfm.Split(SeparatorDash); - OsAttributeInfo platformInfo = new OsAttributeInfo(); - platformInfo.Version = new int[4]; + PlatformAttrbiuteInfo platformInfo; if (tokens.Length == 1) { - if (!s_neutralTfmRegex.IsMatch(tokens[0])) - { - platformInfo.OsPlatform = Windows; - } + platformInfo = new PlatformAttrbiuteInfo(); + platformInfo.Version = new Version(); + platformInfo.OsPlatformName = s_neutralTfmRegex.IsMatch(tokens[0]) ? string.Empty : Windows; + platformInfos.Add(platformInfo); } else { Debug.Assert(tokens.Length == 2); - Match match = s_osParseRegex.Match(tokens[1]); - if (match.Success) + if (PlatformAttrbiuteInfo.TryParseTfmString(tokens[1], out PlatformAttrbiuteInfo parsedTfm)) { - platformInfo.OsPlatform = match.Groups[1].Value; - for (int i = 3; i < 7; i++) + var tpmv = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetPlatformMinVersion, MinimumOsRule, containingSymbol, compilation, cancellationToken); + if (tpmv != null) { - if (!string.IsNullOrEmpty(match.Groups[i].Value)) + if (Version.TryParse(tpmv, out Version version)) { - platformInfo.Version[i - 3] = int.Parse(match.Groups[i].Value, CultureInfo.InvariantCulture); + parsedTfm.Version = version; } } - } - var tpmv = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetPlatformMinVersion, AddedRule, containingSymbol, compilation, cancellationToken); - if (tpmv != null) - { - var splitted = tpmv.Split(SeparatorDot); - int i = 0; - foreach (var token in splitted) - { - platformInfo.Version[i] = int.Parse(token, CultureInfo.InvariantCulture); - } + platformInfos.Add(parsedTfm); } } - platformInfos.Add(platformInfo); } return platformInfos; } return null; } - private static List GetApplicableAttributes(ImmutableArray immediateAttributes, INamedTypeSymbol type, INamedTypeSymbol osAttribute) + private static ImmutableArray FindPlatformAttributes(ImmutableArray immediateAttributes, INamedTypeSymbol parent) { - var attributes = new List(); + using var builder = ArrayBuilder.GetInstance(); foreach (AttributeData attribute in immediateAttributes) { - if (attribute.AttributeClass.DerivesFromOrImplementsAnyConstructionOf(osAttribute)) + if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name)) { - attributes.Add(attribute); + builder.Add(attribute); } } - while (type != null) + while (parent != null) { - var current = type.GetAttributes(); + var current = parent.GetAttributes(); foreach (var attribute in current) { - if (attribute.AttributeClass.DerivesFromOrImplementsAnyConstructionOf(osAttribute)) + if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name) && !TargetPlatformAttributeName.Equals(attribute.AttributeClass.Name, StringComparison.InvariantCulture)) { - attributes.Add(attribute); + builder.Add(attribute); } } - type = type.BaseType; + parent = parent.BaseType; } - return attributes; + return builder.ToImmutableArray(); } - private static bool IsSuppressedByAttribute(OsAttributeInfo diagnosingAttribute, ISymbol containingSymbol) + private static bool IsSuppressedByAttribute(PlatformAttrbiuteInfo diagnosingAttribute, ISymbol containingSymbol) { while (containingSymbol != null) { @@ -334,10 +340,12 @@ private static bool IsSuppressedByAttribute(OsAttributeInfo diagnosingAttribute, { if (diagnosingAttribute.AttributeType.ToString().Equals(attribute.AttributeClass.Name, StringComparison.InvariantCulture)) { - OsAttributeInfo parsedAttribute = OsAttributeInfo.ParseAttributeData(attribute); - if (diagnosingAttribute.OsPlatform!.Equals(parsedAttribute.OsPlatform, StringComparison.InvariantCultureIgnoreCase) && AttributeVersionsMatch(diagnosingAttribute, parsedAttribute)) + if (PlatformAttrbiuteInfo.TryParseAttributeData(attribute, out PlatformAttrbiuteInfo parsedAttribute)) { - return true; + if (diagnosingAttribute.OsPlatformName == parsedAttribute.OsPlatformName && AttributeVersionsMatch(diagnosingAttribute, parsedAttribute)) + { + return true; + } } } } @@ -347,151 +355,48 @@ private static bool IsSuppressedByAttribute(OsAttributeInfo diagnosingAttribute, return false; } - private static bool AttributeVersionsMatch(OsAttributeInfo diagnosingAttribute, OsAttributeInfo tfm) + private static bool AttributeVersionsMatch(PlatformAttrbiuteInfo diagnosingAttribute, PlatformAttrbiuteInfo osAttribute) + { + return AttributeVersionsMatch(diagnosingAttribute.AttributeType, diagnosingAttribute.Version, osAttribute.Version); + } + + private static bool AttributeVersionsMatch(PlatformAttrbiteType attributeType, Version diagnosingVersion, Version suppressingVersion) { - if (diagnosingAttribute.AttributeType == OsAttrbiteType.MinimumOSPlatformAttribute) + if (attributeType == PlatformAttrbiteType.MinimumOSPlatformAttribute) { - for (int i = 0; i < 4; i++) + if (diagnosingVersion.Major != suppressingVersion.Major) { - if (diagnosingAttribute.Version[i] < tfm.Version[i]) - { - return true; - } - else if (diagnosingAttribute.Version[i] > tfm.Version[i]) - { - return false; - } + return diagnosingVersion.Major < suppressingVersion.Major; } - return true; - } - else - { - for (int i = 0; i < 4; i++) + if (diagnosingVersion.Minor != suppressingVersion.Minor) { - if (diagnosingAttribute.Version[i] > tfm.Version[i]) - { - return true; - } - else if (diagnosingAttribute.Version[i] < tfm.Version[i]) - { - return false; - } + return diagnosingVersion.Minor < suppressingVersion.Minor; } - return true; - }; - } - - private static bool AttributeVersionsMatch(OsAttrbiteType attributeType, int[] diagnosingVersion, int[] suppressingVersion) - { - if (attributeType == OsAttrbiteType.MinimumOSPlatformAttribute) - { - for (int i = 0; i < 4; i++) + if (diagnosingVersion.Build != suppressingVersion.Build) { - if (diagnosingVersion[i] < suppressingVersion[i]) - { - return true; - } - else if (diagnosingVersion[i] > suppressingVersion[i]) - { - return false; - } + return diagnosingVersion.Build < suppressingVersion.Build; } - return true; + + return diagnosingVersion.Revision <= suppressingVersion.Revision; } else { - Debug.Assert(attributeType == OsAttrbiteType.ObsoletedInOSPlatformAttribute || attributeType == OsAttrbiteType.RemovedInOSPlatformAttribute); + Debug.Assert(attributeType == PlatformAttrbiteType.ObsoletedInOSPlatformAttribute || attributeType == PlatformAttrbiteType.RemovedInOSPlatformAttribute); - for (int i = 0; i < 4; i++) + if (diagnosingVersion.Major != suppressingVersion.Major) { - if (diagnosingVersion[i] > suppressingVersion[i]) - { - return true; - } - else if (diagnosingVersion[i] < suppressingVersion[i]) - { - return false; - } + return diagnosingVersion.Major > suppressingVersion.Major; } - return true; - }; - } - - private enum OsAttrbiteType - { - None, MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute - } - - private struct OsAttributeInfo : IEquatable - { - public OsAttrbiteType AttributeType { get; set; } - public string? OsPlatform { get; set; } -#pragma warning disable CA1819 // Properties should not return arrays - public int[] Version { get; set; } -#pragma warning restore CA1819 // Properties should not return arrays - - public static OsAttributeInfo ParseAttributeData(AttributeData osAttibute) - { - OsAttributeInfo platformInfo = new OsAttributeInfo(); - switch (osAttibute.AttributeClass.Name) - { - case MinimumOsAttributeName: - platformInfo.AttributeType = OsAttrbiteType.MinimumOSPlatformAttribute; break; - case ObsoleteAttributeName: - platformInfo.AttributeType = OsAttrbiteType.ObsoletedInOSPlatformAttribute; break; - case RemovedAttributeName: - platformInfo.AttributeType = OsAttrbiteType.RemovedInOSPlatformAttribute; break; - default: - platformInfo.AttributeType = OsAttrbiteType.None; break; - } - - platformInfo.Version = new int[4]; - Match match = s_osParseRegex.Match(osAttibute.ConstructorArguments[0].Value.ToString()); - if (match.Success) + if (diagnosingVersion.Minor != suppressingVersion.Minor) { - platformInfo.OsPlatform = match.Groups[1].Value; - for (int i = 3; i < 7; i++) - { - if (!string.IsNullOrEmpty(match.Groups[i].Value)) - { - platformInfo.Version[i - 3] = int.Parse(match.Groups[i].Value, CultureInfo.InvariantCulture); - } - } + return diagnosingVersion.Minor > suppressingVersion.Minor; } - - return platformInfo; - } - - public override bool Equals(object obj) - { - if (obj is OsAttributeInfo info) + if (diagnosingVersion.Build != suppressingVersion.Build) { - return Equals(info); + return diagnosingVersion.Build > suppressingVersion.Build; } - return false; - } - - public override int GetHashCode() => - HashUtilities.Combine(AttributeType.GetHashCode(), OsPlatform?.GetHashCode(), Version.GetHashCode()); - public static bool operator ==(OsAttributeInfo left, OsAttributeInfo right) => left.Equals(right); - - public static bool operator !=(OsAttributeInfo left, OsAttributeInfo right) => !(left == right); - - public bool Equals(OsAttributeInfo other) - { - if (AttributeType == other.AttributeType && OsPlatform == other.OsPlatform) - { - for (int i = 0; i < 4; i++) - { - if (Version[i] != other.Version[i]) - { - return false; - } - } - return true; - } - return false; + return diagnosingVersion.Revision > suppressingVersion.Revision; } } } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.OperationVisitor.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.OperationVisitor.cs index 55dbc5a6ce..3f39735593 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.OperationVisitor.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.OperationVisitor.cs @@ -34,8 +34,7 @@ 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) && !visitedArguments.IsEmpty) { return RuntimeOSPlatformInfo.TryDecode(method, visitedArguments, DataFlowAnalysisContext.ValueContentAnalysisResultOpt, _osPlatformType, out var platformInfo) ? new GlobalFlowStateAnalysisValueSet(platformInfo) : diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index d72f29bbe4..d2e873a97e 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests public class PlatformCompatabilityAnalyzerTests { [Fact] - public async Task OsDependentMethodCalledWithoutSuppressionWarns() + public async Task OsDependentMethodCalledWarns() { var source = @" using System.Runtime.Versioning; @@ -35,7 +35,131 @@ public void M2() } [Fact] - public async Task OsDependentMethodCalledFromIntanceWithoutSuppressionWarns() + public async Task OsDependentPropertyCalledWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + [MinimumOSPlatform(""Windows10.1.1"")] + public string WindowsStringProperty { get; set; } + [MinimumOSPlatform(""Windows10.1"")] + public int WindowsIntProperty { get; set; } + public void M1() + { + [|WindowsStringProperty|] = ""Hello""; + string s = [|WindowsStringProperty|]; + M2([|WindowsStringProperty|]); + M3([|WindowsIntProperty|]); + } + public string M2(string option) + { + return option; + } + public int M3(int option) + { + return option; + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task OsDependentEnumValueCalledWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test2 +{ + public void M1() + { + PlatformEnum val = [|PlatformEnum.Windows10|]; + M2([|PlatformEnum.Windows10|]); + M3([|PlatformEnum.Linux48|]); + M2(PlatformEnum.NoPlatform); + } + public PlatformEnum M2(PlatformEnum option) + { + return option; + } + public PlatformEnum M3(PlatformEnum option) + { + return option; + } +} + +public enum PlatformEnum +{ + [MinimumOSPlatform(""Windows10.0"")] + Windows10, + [MinimumOSPlatform(""Linux4.8"")] + Linux48, + NoPlatform +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task OsDependentFieldCalledWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + [MinimumOSPlatform(""Windows10.1.1.1"")] + string WindowsStringField; + [MinimumOSPlatform(""Windows10.1.1.1"")] + public int WindowsIntField { get; set; } + public void M1() + { + Test test = new Test(); + [|WindowsStringField|] = ""Hello""; + string s = [|WindowsStringField|]; + M2([|test.WindowsStringField|]); + M2([|WindowsStringField|]); + M3([|WindowsIntField|]); + } + public string M2(string option) + { + return option; + } + public int M3(int option) + { + return option; + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + /*[Fact] TODO: Add the attribute assembly level and test + public async Task MethodWithTargetPlatrofrmAttributeDoesNotWarn() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + public void M1() + { + M2(); + } + [TargetPlatform(""Windows10.1.1.1"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + }*/ + + [Fact] + public async Task OsDependentMethodCalledFromIntanceWarns() { var source = @" using System.Runtime.Versioning; @@ -91,7 +215,7 @@ public void M2() } [Fact] - public async Task MethodOfOsDependentClassCalledWithoutSuppressionWarns() + public async Task MethodOfOsDependentClassCalledWarns() { var source = @" using System.Runtime.Versioning; @@ -115,7 +239,7 @@ public void M2() await VerifyCS.VerifyAnalyzerAsync(source); } - /* [Fact] TODO find out how to pass 2 sources + /* [Fact] TODO find out how to pass 2 sources or wait until assembly level APIs merged public async Task MethodOfOsDependentAssemblyCalledWithoutSuppressionWarns() { var source = @" @@ -154,7 +278,7 @@ await VerifyCS.VerifyAnalyzerAsync(source, [InlineData("10.1.2.3", "10.1.1.4", true)] [InlineData("10.1.2.3", "10.0.1.9", true)] [InlineData("10.1.2.3", "8.2.3.3", true)] - public async Task MethodOfOsDependentClassSuppressedWithAddedAttribute(string dependentVersion, string suppressingVersion, bool warn) + public async Task MethodOfOsDependentClassSuppressedWithMinimumOsAttribute(string dependentVersion, string suppressingVersion, bool warn) { var source = @" using System.Runtime.Versioning; @@ -179,13 +303,13 @@ public void M2() " + MockPlatformApiSource; if (warn) - await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.AddedRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); else await VerifyCS.VerifyAnalyzerAsync(source); } [Theory] - [InlineData("10.1.2.3", "10.1.2.3", false)] + [InlineData("10.1.2.3", "10.1.2.3", true)] [InlineData("10.1.2.3", "10.1.3.3", true)] [InlineData("10.1.2.3", "10.1.3.1", true)] [InlineData("10.1.2.3", "11.1.2.3", true)] @@ -225,7 +349,7 @@ public void M2() } [Theory] - [InlineData("10.1.2.3", "10.1.2.3", false)] + [InlineData("10.1.2.3", "10.1.2.3", true)] [InlineData("10.1.2.3", "10.1.3.3", true)] [InlineData("10.1.2.3", "10.1.3.1", true)] [InlineData("10.1.2.3", "11.1.2.3", true)] @@ -272,13 +396,13 @@ public void M2() [InlineData("build_property.TargetFramework=net5.0-windows", true)] [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.1", false)] [InlineData("build_property.TargetFramework=net5.0-windows10.2", false)] - [InlineData("build_property.TargetFramework=net5.0-windows11", false)] - [InlineData("build_property.TargetFramework=net5.0-windows10", true)] - [InlineData("build_property.TargetFramework=net5.0-windows11\nbuild_property.TargetPlatformMinVersion=10;", true)] + [InlineData("build_property.TargetFramework=net5.0-windows11.0", false)] + [InlineData("build_property.TargetFramework=net5.0-windows10.0", true)] + [InlineData("build_property.TargetFramework=net5.0-windows11.0\nbuild_property.TargetPlatformMinVersion=10.0;", true)] [InlineData("build_property.TargetFramework=net5.0-windows10.2\nbuild_property.TargetPlatformMinVersion=10.1;", true)] [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.2\nbuild_property.TargetPlatformMinVersion=10.0.0.1;", true)] [InlineData("build_property.TargetFramework=net5.0-windows10.2.1\nbuild_property.TargetPlatformMinVersion=9.1.1.1;", true)] - public async Task TfmAndTargetPlatformMinVersionWithAddedAttribute(string editorConfigText, bool expectDiagnostic) + public async Task TfmAndTargetPlatformMinVersionWithMinimumOsAttribute(string editorConfigText, bool expectDiagnostic) { var invocation = expectDiagnostic ? @"[|M2()|]" : "M2()"; await new VerifyCS.Test @@ -312,14 +436,14 @@ public void M2() [Theory] [InlineData("", true)] [InlineData("build_property.TargetFramework=net5.0", true)] - [InlineData("build_property.TargetFramework=net472", false)] // TODO because no version in TFM, version is set to 0.0.0.0 + [InlineData("build_property.TargetFramework=net472", false)] // TODO: because no version part it is 0.0.0.0 [InlineData("build_property.TargetFramework=net5.0-linux", true)] - [InlineData("build_property.TargetFramework=net5.0-windows", false)] // Same here - [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.1", false)] + [InlineData("build_property.TargetFramework=net5.0-windows", true)] + [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.1", true)] [InlineData("build_property.TargetFramework=net5.0-windows10.2", true)] - [InlineData("build_property.TargetFramework=net5.0-windows11", true)] - [InlineData("build_property.TargetFramework=net5.0-windows10", false)] - [InlineData("build_property.TargetFramework=net5.0-windows11\nbuild_property.TargetPlatformMinVersion=10;", false)] + [InlineData("build_property.TargetFramework=net5.0-windows11.0", true)] + [InlineData("build_property.TargetFramework=net5.0-windows10.0", false)] + [InlineData("build_property.TargetFramework=net5.0-windows11.0\nbuild_property.TargetPlatformMinVersion=10.0;", false)] [InlineData("build_property.TargetFramework=net5.0-windows10.2\nbuild_property.TargetPlatformMinVersion=10.1;", false)] [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.2\nbuild_property.TargetPlatformMinVersion=10.0.0.1;", false)] [InlineData("build_property.TargetFramework=net5.0-windows10.2.1\nbuild_property.TargetPlatformMinVersion=9.1.1.1;", false)] @@ -409,7 +533,7 @@ public void M2() } [Fact] - public async Task GuardedCall_SimpleIfElseTest() + public async Task GuardedWith_IsOSPlatformOrLater_SimpleIfElseTest() { var source = @" using System.Runtime.Versioning; @@ -419,8 +543,6 @@ class Test { void M1() { - [|M2()|]; - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) { M2(); @@ -440,6 +562,132 @@ void M2() await VerifyCS.VerifyAnalyzerAsync(source); } + [Fact] + public async Task GuardedWith_IsOSPlatformEarlierThan_SimpleIfElseTest() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 10)) + { + [|M2()|]; + M3(); + } + else + { + [|M2()|]; + [|M3()|]; + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } + + [ObsoletedInOSPlatform(""Windows10.1.2.3"")] + void M3 () + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedWith_StringOverload_SimpleIfElseTest() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(""Windows10.1"")) + { + [|M2()|]; + M3(); + } + else + { + [|M2()|]; + [|M3()|]; + } + + if(RuntimeInformationHelper.IsOSPlatformOrLater(""Windows10.1.3"")) + { + [|M3()|]; + M2(); + } + else + { + [|M2()|]; + [|M3()|]; + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } + + [ObsoletedInOSPlatform(""Windows10.1.2.3"")] + void M3 () + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task OsDependentEnumValue_GuardedWith_SimpleIfElseTest() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test2 +{ + public void M1() + { + PlatformEnum val = [|PlatformEnum.Windows10|]; + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10)) + { + M2(PlatformEnum.Windows10); + } + else + { + M2([|PlatformEnum.Windows10|]); + } + M2([|PlatformEnum.Linux48|]); + M2(PlatformEnum.NoPlatform); + } + public PlatformEnum M2(PlatformEnum option) + { + return option; + } +} + +public enum PlatformEnum +{ + [MinimumOSPlatform(""Windows10.0"")] + Windows10, + [MinimumOSPlatform(""Linux4.8"")] + Linux48, + NoPlatform +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + [Fact] public async Task GuardedCall_SimpleIfElseIfElseTest() { @@ -473,8 +721,6 @@ void M1() { [|M2()|]; } - - [|M2()|]; } [MinimumOSPlatform(""Windows10.1.2.3"")] @@ -576,8 +822,6 @@ public class Test { public void M1() { - [|M2()|]; - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) { @@ -665,8 +909,6 @@ public class Test { public void M1() { - [|M2()|]; - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) { @@ -848,8 +1090,6 @@ class Test { void M1() { - [|M2()|]; - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 8)) { [|M2()|]; @@ -925,8 +1165,6 @@ class Test { void M1() { - [|M2()|]; - var x1 = RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11); var x2 = RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1); @@ -964,15 +1202,11 @@ class Test { void M1() { - [|M2()|]; - var v11 = 11; if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, v11)) { M2(); } - - [|M2()|]; } [MinimumOSPlatform(""Windows10.1.2.3"")] @@ -995,15 +1229,11 @@ class Test { void M1() { - [|M2()|]; - var platform = OSPlatform.Windows; if (RuntimeInformationHelper.IsOSPlatformOrLater(platform, 11)) { [|M2()|]; } - - [|M2()|]; } [MinimumOSPlatform(""Windows10.1.2.3"")] @@ -1063,9 +1293,8 @@ void M2() await VerifyCS.VerifyAnalyzerAsync(source); } - [Theory] - [InlineData("dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive")] - public async Task InterproceduralAnalysisTest(string editorconfig) + /* [Fact] TODO: Not working anymore, fix this + public async Task InterproceduralAnalysisTest() { var source = @" using System.Runtime.Versioning; @@ -1092,7 +1321,7 @@ void M2() bool IsWindows11OrLater() { - return RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11); + return RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows,10,2,3,4); } }" + MockPlatformApiSource; @@ -1101,18 +1330,18 @@ bool IsWindows11OrLater() TestState = { Sources = { source }, - AdditionalFiles = { (".editorconfig", editorconfig) } + AdditionalFiles = { (".editorconfig", "dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive") } } }; test.ExpectedDiagnostics.AddRange(new[] { - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.AddedRule).WithLocation(0).WithArguments("M2", "Windows", "10.1.2.3"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.AddedRule).WithLocation(1).WithArguments("M2", "Windows", "10.1.2.3") + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithLocation(0).WithArguments("M2", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithLocation(1).WithArguments("M2", "Windows", "10.1.2.3"), }); await test.RunAsync(); - } + }*/ private readonly string MockPlatformApiSource = @" namespace System.Runtime.Versioning @@ -1142,6 +1371,7 @@ public TargetPlatformAttribute(string platformName) : base(platformName) AttributeTargets.Method | AttributeTargets.Module | AttributeTargets.Property | + AttributeTargets.Field | AttributeTargets.Struct, AllowMultiple = true, Inherited = false)] public sealed class MinimumOSPlatformAttribute : OSPlatformAttribute @@ -1157,6 +1387,7 @@ public MinimumOSPlatformAttribute(string platformName) : base(platformName) AttributeTargets.Method | AttributeTargets.Module | AttributeTargets.Property | + AttributeTargets.Field | AttributeTargets.Struct, AllowMultiple = true, Inherited = false)] public sealed class RemovedInOSPlatformAttribute : OSPlatformAttribute @@ -1172,6 +1403,7 @@ public RemovedInOSPlatformAttribute(string platformName) : base(platformName) AttributeTargets.Method | AttributeTargets.Module | AttributeTargets.Property | + AttributeTargets.Field | AttributeTargets.Struct, AllowMultiple = true, Inherited = false)] public sealed class ObsoletedInOSPlatformAttribute: OSPlatformAttribute @@ -1196,42 +1428,36 @@ public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major) { return true; } - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor) { return true; } - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build) { return true; } - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build, int revision) { return true; } - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major) { return false; } - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor) { return false; } - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build) { return false; } - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build, int revision) { return false; } - + public static bool IsOSPlatformOrLater(string platformName) => true; + public static bool IsOSPlatformEarlierThan(string platformName) => true; #pragma warning restore CA1801, IDE0060 // Review unused parameters } }"; diff --git a/src/Utilities/Compiler/WellKnownTypeNames.cs b/src/Utilities/Compiler/WellKnownTypeNames.cs index 56a9805959..ea739d1406 100644 --- a/src/Utilities/Compiler/WellKnownTypeNames.cs +++ b/src/Utilities/Compiler/WellKnownTypeNames.cs @@ -240,7 +240,6 @@ internal static class WellKnownTypeNames public const string SystemRuntimeSerializationOnSerializingAttribute = "System.Runtime.Serialization.OnSerializingAttribute"; public const string SystemRuntimeSerializationSerializationInfo = "System.Runtime.Serialization.SerializationInfo"; public const string SystemRuntimeSerializationStreamingContext = "System.Runtime.Serialization.StreamingContext"; - public const string SystemRuntimeVersioningOSPlatformAttribute = "System.Runtime.Versioning.OSPlatformAttribute"; public const string SystemSecurityAuthenticationSslProtocols = "System.Security.Authentication.SslProtocols"; public const string SystemSecurityCryptographyAesCcm = "System.Security.Cryptography.AesCcm"; public const string SystemSecurityCryptographyAesGcm = "System.Security.Cryptography.AesGcm"; From 00ffd24010eb60d82e705dcd93ae016b0ae503d7 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 17 Jul 2020 09:58:59 -0700 Subject: [PATCH 06/48] Excclude conditional check, more refactoring --- .../MockOsPlatformAttribute.cs | 124 --- .../PlatformCompatabilityAnalyzer.Data.cs | 12 +- .../PlatformCompatabilityAnalyzer.cs | 241 +++-- ...CompatabilityAnalyserTests.GuardedCalls.cs | 874 ++++++++++++++++ .../PlatformCompatabilityAnalyzerTests.cs | 937 ++---------------- 5 files changed, 1086 insertions(+), 1102 deletions(-) delete mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/MockOsPlatformAttribute.cs create mode 100644 src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyserTests.GuardedCalls.cs diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/MockOsPlatformAttribute.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/MockOsPlatformAttribute.cs deleted file mode 100644 index 785211e896..0000000000 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/MockOsPlatformAttribute.cs +++ /dev/null @@ -1,124 +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. - -namespace System.Runtime.Versioning -{ - public abstract class OSPlatformAttribute : Attribute - { - private protected OSPlatformAttribute(string platformName) - { - PlatformName = platformName; - } - - public string PlatformName { get; } - } - - [AttributeUsage(AttributeTargets.Assembly, - AllowMultiple = false, Inherited = false)] - public sealed class TargetPlatformAttribute : OSPlatformAttribute - { - public TargetPlatformAttribute(string platformName) : base(platformName) - { } - } - - [AttributeUsage(AttributeTargets.Assembly | - AttributeTargets.Class | - AttributeTargets.Constructor | - AttributeTargets.Event | - AttributeTargets.Method | - AttributeTargets.Module | - AttributeTargets.Property | - AttributeTargets.Struct | - AttributeTargets.Field, - AllowMultiple = true, Inherited = false)] - public sealed class MinimumOSPlatformAttribute : OSPlatformAttribute - { - public MinimumOSPlatformAttribute(string platformName) : base(platformName) - { } - } - - [AttributeUsage(AttributeTargets.Assembly | - AttributeTargets.Class | - AttributeTargets.Constructor | - AttributeTargets.Event | - AttributeTargets.Method | - AttributeTargets.Module | - AttributeTargets.Property | - AttributeTargets.Struct | - AttributeTargets.Field, - AllowMultiple = true, Inherited = false)] - public sealed class RemovedInOSPlatformAttribute : OSPlatformAttribute - { - public RemovedInOSPlatformAttribute(string platformName) : base(platformName) - { } - } - - [AttributeUsage(AttributeTargets.Assembly | - AttributeTargets.Class | - AttributeTargets.Constructor | - AttributeTargets.Event | - AttributeTargets.Method | - AttributeTargets.Module | - AttributeTargets.Property | - AttributeTargets.Struct | - AttributeTargets.Field, - AllowMultiple = true, Inherited = false)] - public sealed class ObsoletedInOSPlatformAttribute : OSPlatformAttribute - { - public ObsoletedInOSPlatformAttribute(string platformName) : base(platformName) - { } - public ObsoletedInOSPlatformAttribute(string platformName, string message) : base(platformName) - { - Message = message; - } - public string? Message { get; } - public string? Url { get; set; } - } -} - -namespace System.Runtime.InteropServices -{ - public static class RuntimeInformationHelper - { -#pragma warning disable CA1801, IDE0060 // Review unused parameters - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major) - { - return RuntimeInformation.IsOSPlatform(osPlatform); - } - - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor) - { - return RuntimeInformation.IsOSPlatform(osPlatform); - } - - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build) - { - return RuntimeInformation.IsOSPlatform(osPlatform); - } - - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build, int revision) - { - return RuntimeInformation.IsOSPlatform(osPlatform); - } - - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major) - { - return !RuntimeInformation.IsOSPlatform(osPlatform); - } - - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor) - { - return !RuntimeInformation.IsOSPlatform(osPlatform); - } - - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build) - { - return !RuntimeInformation.IsOSPlatform(osPlatform); - } - - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build, int revision) - { - return !RuntimeInformation.IsOSPlatform(osPlatform); - } -#pragma warning restore CA1801, IDE0060 // Review unused parameters - } -} diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index a424bb5c3a..d08473bc3b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -35,13 +35,13 @@ public static bool TryParseAttributeData(AttributeData osAttibute, out PlatformA parsedAttribute = new PlatformAttrbiuteInfo(); switch (osAttibute.AttributeClass.Name) { - case MinimumOsAttributeName: + case MinimumOSPlatformAttribute: parsedAttribute.AttributeType = PlatformAttrbiteType.MinimumOSPlatformAttribute; break; - case ObsoleteAttributeName: + case ObsoletedInOSPlatformAttribute: parsedAttribute.AttributeType = PlatformAttrbiteType.ObsoletedInOSPlatformAttribute; break; - case RemovedAttributeName: + case RemovedInOSPlatformAttribute: parsedAttribute.AttributeType = PlatformAttrbiteType.RemovedInOSPlatformAttribute; break; - case TargetPlatformAttributeName: + case TargetPlatformAttribute: parsedAttribute.AttributeType = PlatformAttrbiteType.TargetPlatformAttribute; break; default: parsedAttribute.AttributeType = PlatformAttrbiteType.None; break; @@ -139,6 +139,7 @@ public static bool TryDecode( [NotNullWhen(returnValue: true)] out RuntimeMethodInfo? info) { Debug.Assert(!arguments.IsEmpty); + if (arguments[0].Value is ILiteralOperation literal && literal.Type.SpecialType == SpecialType.System_String) { if (literal.ConstantValue.HasValue && TryParsePlatformString(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) @@ -233,8 +234,7 @@ static bool TryDecodeOSVersionPart(IArgumentOperation argument, ValueContentAnal public override string ToString() { - var versionStr = Version.ToString(); - var result = $"{InvokedPlatformCheckMethodName};{PlatformPropertyName};{versionStr}"; + var result = $"{InvokedPlatformCheckMethodName};{PlatformPropertyName};{Version}"; if (Negated) { result = $"!{result}"; diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 3861e07b7e..ce1f7bffed 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -1,6 +1,7 @@ // 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.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; @@ -21,7 +22,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA1417"; private static readonly ImmutableArray s_platformCheckMethods = ImmutableArray.Create("IsOSPlatformOrLater", "IsOSPlatformEarlierThan"); - private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create("MinimumOSPlatformAttribute", "ObsoletedInOSPlatformAttribute", "RemovedInOSPlatformAttribute"); + private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute); private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableAddedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckAddedMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableObsoleteMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckObsoleteMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); @@ -29,10 +30,10 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckDescription), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private const char SeparatorDash = '-'; private const char SeparatorSemicolon = ';'; - private const string MinimumOsAttributeName = nameof(PlatformAttrbiteType.MinimumOSPlatformAttribute); - private const string ObsoleteAttributeName = nameof(PlatformAttrbiteType.ObsoletedInOSPlatformAttribute); - private const string RemovedAttributeName = nameof(PlatformAttrbiteType.RemovedInOSPlatformAttribute); - private const string TargetPlatformAttributeName = nameof(PlatformAttrbiteType.TargetPlatformAttribute); + private const string MinimumOSPlatformAttribute = nameof(MinimumOSPlatformAttribute); + private const string ObsoletedInOSPlatformAttribute = nameof(ObsoletedInOSPlatformAttribute); + private const string RemovedInOSPlatformAttribute = nameof(RemovedInOSPlatformAttribute); + private const string TargetPlatformAttribute = nameof(TargetPlatformAttribute); private const string Windows = nameof(Windows); private static readonly Regex s_neutralTfmRegex = new Regex(@"^net([5-9]|standard\d|coreapp\d)\.\d$", RegexOptions.IgnoreCase); @@ -78,15 +79,21 @@ public override void Initialize(AnalysisContext context) return; } - context.RegisterOperationBlockStartAction(context => AnalyzerOperationBlock(context, runtimeInformationType, osPlatformType)); + var guardMethods = GetRuntimePlatformGuardMethods(runtimeInformationType); + + context.RegisterOperationBlockStartAction(context => AnalyzerOperationBlock(context, guardMethods, osPlatformType)); }); + + static ImmutableArray GetRuntimePlatformGuardMethods(INamedTypeSymbol runtimeInformationType) + { + return runtimeInformationType.GetMembers().OfType().Where(m => + s_platformCheckMethods.Contains(m.Name) && !m.Parameters.IsEmpty).ToImmutableArray(); + } } - private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, INamedTypeSymbol runtimeInformationType, INamedTypeSymbol osPlatformType) + private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, ImmutableArray guardMethods, INamedTypeSymbol osPlatformType) { -#pragma warning disable CA2000 // Dispose objects before losing scope - disposed in OperationBlockEndAction. var parsedTfms = ParseTfm(context.Options, context.OwningSymbol, context.Compilation, context.CancellationToken); -#pragma warning restore CA2000 var platformSpecificOperations = PooledDictionary>.GetInstance(); context.RegisterOperationAction(context => @@ -99,18 +106,26 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, { try { - if (platformSpecificOperations.Count == 0 || !(context.OperationBlocks.GetControlFlowGraph() is { } cfg)) + if (platformSpecificOperations.Count == 0) { return; } + if (guardMethods.IsEmpty || !(context.OperationBlocks.GetControlFlowGraph() is { } cfg)) + { + ReportDiagnosticsForAll(platformSpecificOperations, context); + return; + } + var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); var analysisResult = GlobalFlowStateAnalysis.TryGetOrComputeResult( cfg, context.OwningSymbol, CreateOperationVisitor, wellKnownTypeProvider, context.Options, MinimumOsRule, performValueContentAnalysis: true, context.CancellationToken, out var valueContentAnalysisResult); + if (analysisResult == null) { + ReportDiagnosticsForAll(platformSpecificOperations, context); return; } @@ -123,128 +138,124 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, continue; } - string operationName; + ReportDiangosticsIfNotGuarded(platformSpecificOperation.Key, platformSpecificOperation.Value, value, context); + } + } + finally + { + platformSpecificOperations.Free(); + } - if (platformSpecificOperation.Key is IInvocationOperation iOperation) - { - operationName = iOperation.TargetMethod.Name; - } - else if (platformSpecificOperation.Key is IPropertyReferenceOperation pOperation) - { - operationName = pOperation.Property.Name; - } - else if (platformSpecificOperation.Key is IFieldReferenceOperation fOperation) - { - operationName = fOperation.Field.Name; + return; - } - else - { - Debug.Fail("Should never happen"); - return; - } + OperationVisitor CreateOperationVisitor(GlobalFlowStateAnalysisContext context) => new OperationVisitor(guardMethods, osPlatformType, context); + }); + } - PlatformAttrbiuteInfo attribute = platformSpecificOperation.Value.FirstOrDefault(); - if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Empty || value.Kind == GlobalFlowStateAnalysisValueSetKind.Unset) - { - context.ReportDiagnostic(platformSpecificOperation.Key.CreateDiagnostic(SwitchRule(attribute.AttributeType), operationName, attribute.OsPlatformName, attribute.Version.ToString())); - } - else + private static void ReportDiangosticsIfNotGuarded(IOperation operation, ImmutableArray attributes, GlobalFlowStateAnalysisValueSet value, OperationBlockAnalysisContext context) + { + PlatformAttrbiuteInfo attribute = attributes.FirstOrDefault(); + if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Empty || value.Kind == GlobalFlowStateAnalysisValueSetKind.Unset) + { + ReportDiagnostics(operation, attribute, context); + } + else + { + bool guarded = false; + foreach (var analysisValue in value.AnalysisValues) + { + if (analysisValue is RuntimeMethodInfo info) + { + if (!info.Negated) { - bool guarded = false; - foreach (var analysisValue in value.AnalysisValues) + if (attribute.OsPlatformName.Equals(info.PlatformPropertyName, StringComparison.InvariantCultureIgnoreCase)) { - if (analysisValue is RuntimeMethodInfo info) + if (info.InvokedPlatformCheckMethodName.Equals(s_platformCheckMethods[0], StringComparison.InvariantCulture)) { - if (!info.Negated) + if (attribute.AttributeType == PlatformAttrbiteType.MinimumOSPlatformAttribute && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) { - if (attribute.OsPlatformName.Equals(info.PlatformPropertyName, StringComparison.InvariantCultureIgnoreCase)) - { - if (info.InvokedPlatformCheckMethodName.Equals(s_platformCheckMethods[0], StringComparison.InvariantCulture)) - { - if (attribute.AttributeType == PlatformAttrbiteType.MinimumOSPlatformAttribute && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) - { - guarded = true; - } - } - else - { - if ((attribute.AttributeType == PlatformAttrbiteType.ObsoletedInOSPlatformAttribute || attribute.AttributeType == PlatformAttrbiteType.RemovedInOSPlatformAttribute) - && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) - { - guarded = true; - } - } - } + guarded = true; + } + } + else + { + if ((attribute.AttributeType == PlatformAttrbiteType.ObsoletedInOSPlatformAttribute || attribute.AttributeType == PlatformAttrbiteType.RemovedInOSPlatformAttribute) + && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) + { + guarded = true; } } - } - if (!guarded) - { - context.ReportDiagnostic(platformSpecificOperation.Key.CreateDiagnostic(SwitchRule(attribute.AttributeType), operationName, attribute.OsPlatformName, attribute.Version.ToString())); } } } } - finally + if (!guarded) { - platformSpecificOperations.Free(); - parsedTfms?.Free(); + ReportDiagnostics(operation, attribute, context); } + } + } - return; + private static void ReportDiagnosticsForAll(PooledDictionary> platformSpecificOperations, OperationBlockAnalysisContext context) + { + foreach (var platformSpecificOperation in platformSpecificOperations) + { + ReportDiagnostics(platformSpecificOperation.Key, platformSpecificOperation.Value.FirstOrDefault(), context); + } + } - OperationVisitor CreateOperationVisitor(GlobalFlowStateAnalysisContext context) - => new OperationVisitor(GetPlatformCheckMethods(runtimeInformationType), osPlatformType, context); - }); + private static void ReportDiagnostics(IOperation operation, PlatformAttrbiuteInfo attribute, OperationBlockAnalysisContext context) + { + string operationName = string.Empty; - static ImmutableArray GetPlatformCheckMethods(INamedTypeSymbol runtimeInformationType) + if (operation is IInvocationOperation iOperation) { - return runtimeInformationType.GetMembers().OfType().Where(m => s_platformCheckMethods.Contains(m.Name) && !m.Parameters.IsEmpty).ToImmutableArray(); + operationName = iOperation.TargetMethod.Name; } + else if (operation is IPropertyReferenceOperation pOperation) + { + operationName = pOperation.Property.Name; + } + else if (operation is IFieldReferenceOperation fOperation) + { + operationName = fOperation.Field.Name; + } + + context.ReportDiagnostic(operation.CreateDiagnostic(SwitchRule(attribute.AttributeType), operationName, attribute.OsPlatformName, attribute.Version.ToString())); } private static void AnalyzeInvocationOperation(IOperation operation, OperationAnalysisContext context, - PooledConcurrentSet? parsedTfms, ref PooledDictionary> platformSpecificOperations) + List? parsedTfms, ref PooledDictionary> platformSpecificOperations) { using var builder = ArrayBuilder.GetInstance(); - ImmutableArray attributes; + AttributeData[]? attributes = null; if (operation is IInvocationOperation iOperation) { - attributes = FindPlatformAttributes(iOperation.TargetMethod.GetAttributes(), iOperation.TargetMethod.ContainingType); + attributes = FindAllPlatformAttributesApplied(iOperation.TargetMethod.GetAttributes(), iOperation.TargetMethod.ContainingType); } else if (operation is IPropertyReferenceOperation pOperation) { - attributes = FindPlatformAttributes(pOperation.Property.GetAttributes(), pOperation.Property.ContainingType); + if (!(pOperation.Parent is IBinaryOperation bo && (bo.OperatorKind == BinaryOperatorKind.Equals || bo.OperatorKind == BinaryOperatorKind.NotEquals))) + attributes = FindAllPlatformAttributesApplied(pOperation.Property.GetAttributes(), pOperation.Property.ContainingType); } else if (operation is IFieldReferenceOperation fOperation) { - attributes = FindPlatformAttributes(fOperation.Field.GetAttributes(), fOperation.Field.ContainingType); + if (!(fOperation.Parent is IBinaryOperation bo && (bo.OperatorKind == BinaryOperatorKind.Equals || bo.OperatorKind == BinaryOperatorKind.NotEquals))) + attributes = FindAllPlatformAttributesApplied(fOperation.Field.GetAttributes(), fOperation.Field.ContainingType); } - foreach (AttributeData attribute in attributes) + if (attributes != null) { - bool suppressed = false; - if (PlatformAttrbiuteInfo.TryParseAttributeData(attribute, out PlatformAttrbiuteInfo parsedAttribute)) + foreach (AttributeData attribute in attributes) { - if (parsedTfms != null) + if (PlatformAttrbiuteInfo.TryParseAttributeData(attribute, out PlatformAttrbiuteInfo parsedAttribute)) { - foreach (PlatformAttrbiuteInfo tfm in parsedTfms) + if (!(IsSuppressedByTfm(parsedTfms, parsedAttribute) || IsSuppressedByAttribute(parsedAttribute, context.ContainingSymbol))) { - if (tfm.OsPlatformName.Equals(parsedAttribute.OsPlatformName, StringComparison.InvariantCultureIgnoreCase)) - { - suppressed = AttributeVersionsMatch(parsedAttribute, tfm); - } + builder.Add(parsedAttribute); } } - - suppressed = suppressed || IsSuppressedByAttribute(parsedAttribute, context.ContainingSymbol); - - if (!suppressed) - { - builder.Add(parsedAttribute); - } } } @@ -254,6 +265,22 @@ private static void AnalyzeInvocationOperation(IOperation operation, OperationAn } } + private static bool IsSuppressedByTfm(List? parsedTfms, PlatformAttrbiuteInfo parsedAttribute) + { + if (parsedTfms != null) + { + foreach (PlatformAttrbiuteInfo tfm in parsedTfms) + { + if (tfm.OsPlatformName.Equals(parsedAttribute.OsPlatformName, StringComparison.InvariantCultureIgnoreCase)) + { + return AttributeVersionsMatch(parsedAttribute, tfm); + } + } + } + + return false; + } + private static DiagnosticDescriptor SwitchRule(PlatformAttrbiteType attributeType) { if (attributeType == PlatformAttrbiteType.MinimumOSPlatformAttribute) @@ -263,24 +290,25 @@ private static DiagnosticDescriptor SwitchRule(PlatformAttrbiteType attributeTyp return RemovedRule; } - private static PooledConcurrentSet? ParseTfm(AnalyzerOptions options, ISymbol containingSymbol, Compilation compilation, CancellationToken cancellationToken) + private static List? ParseTfm(AnalyzerOptions options, ISymbol containingSymbol, Compilation compilation, CancellationToken cancellationToken) { string? tfmString = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetFramework, MinimumOsRule, containingSymbol, compilation, cancellationToken); if (tfmString != null) { - PooledConcurrentSet platformInfos = PooledConcurrentSet.GetInstance(); + using var builder = ArrayBuilder.GetInstance(); + var tfms = tfmString.Split(SeparatorSemicolon); foreach (var tfm in tfms) { var tokens = tfm.Split(SeparatorDash); - PlatformAttrbiuteInfo platformInfo; + if (tokens.Length == 1) { - platformInfo = new PlatformAttrbiuteInfo(); - platformInfo.Version = new Version(); - platformInfo.OsPlatformName = s_neutralTfmRegex.IsMatch(tokens[0]) ? string.Empty : Windows; - platformInfos.Add(platformInfo); + if (!s_neutralTfmRegex.IsMatch(tokens[0])) + { + builder.Add(CreateWindowsOnlyPlatformInfo()); + } } else { @@ -295,16 +323,24 @@ private static DiagnosticDescriptor SwitchRule(PlatformAttrbiteType attributeTyp parsedTfm.Version = version; } } - platformInfos.Add(parsedTfm); + builder.Add(parsedTfm); } } } - return platformInfos; + return builder.ToList(); } return null; } - private static ImmutableArray FindPlatformAttributes(ImmutableArray immediateAttributes, INamedTypeSymbol parent) + private static PlatformAttrbiuteInfo CreateWindowsOnlyPlatformInfo() + { + var platformInfo = new PlatformAttrbiuteInfo(); + platformInfo.Version = new Version(7, 0); + platformInfo.OsPlatformName = Windows; + return platformInfo; + } + + private static AttributeData[] FindAllPlatformAttributesApplied(ImmutableArray immediateAttributes, INamedTypeSymbol parent) { using var builder = ArrayBuilder.GetInstance(); foreach (AttributeData attribute in immediateAttributes) @@ -314,19 +350,20 @@ private static ImmutableArray FindPlatformAttributes(ImmutableArr builder.Add(attribute); } } + while (parent != null) { var current = parent.GetAttributes(); foreach (var attribute in current) { - if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name) && !TargetPlatformAttributeName.Equals(attribute.AttributeClass.Name, StringComparison.InvariantCulture)) + if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name) && !TargetPlatformAttribute.Equals(attribute.AttributeClass.Name, StringComparison.InvariantCulture)) { builder.Add(attribute); } } parent = parent.BaseType; } - return builder.ToImmutableArray(); + return builder.ToArray(); } private static bool IsSuppressedByAttribute(PlatformAttrbiuteInfo diagnosingAttribute, ISymbol containingSymbol) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyserTests.GuardedCalls.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyserTests.GuardedCalls.cs new file mode 100644 index 0000000000..76e3a28db5 --- /dev/null +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyserTests.GuardedCalls.cs @@ -0,0 +1,874 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.CodeAnalysis.NetAnalyzers.UnitTests.Microsoft.NetCore.Analyzers.InteropServices +{ + public partial class PlatformCompatabilityAnalyserTests + { + [Fact] + public async Task GuardedCalled_SimpleIf_NotWarns() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + M2(); + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_MultipleSimpleIfTests() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + M2(); + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 10, 1, 2, 3)) + [|M2()|]; + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + M2(); + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 8, 1, 2, 3)) + [|M2()|]; + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedWith_IsOSPlatformOrLater_SimpleIfElseTest() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + M2(); + } + else + { + [|M2()|]; + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedWith_IsOSPlatformEarlierThan_SimpleIfElseTest() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 10)) + { + [|M2()|]; + M3(); + } + else + { + [|M2()|]; + [|M3()|]; + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } + + [ObsoletedInOSPlatform(""Windows10.1.2.3"")] + void M3 () + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedWith_StringOverload_SimpleIfElseTest() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(""Windows10.1"")) + { + [|M2()|]; + M3(); + } + else + { + [|M2()|]; + [|M3()|]; + } + + if(RuntimeInformationHelper.IsOSPlatformOrLater(""Windows10.1.3"")) + { + [|M3()|]; + M2(); + } + else + { + [|M2()|]; + [|M3()|]; + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } + + [ObsoletedInOSPlatform(""Windows10.1.2.3"")] + void M3 () + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task OsDependentEnumValue_GuardedWith_SimpleIfElseTest() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test2 +{ + public void M1() + { + PlatformEnum val = [|PlatformEnum.Windows10|]; + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10)) + { + M2(PlatformEnum.Windows10); + } + else + { + M2([|PlatformEnum.Windows10|]); + } + M2([|PlatformEnum.Linux48|]); + M2(PlatformEnum.NoPlatform); + } + public PlatformEnum M2(PlatformEnum option) + { + return option; + } +} + +public enum PlatformEnum +{ + [MinimumOSPlatform(""Windows10.0"")] + Windows10, + [MinimumOSPlatform(""Linux4.8"")] + Linux48, + NoPlatform +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseIfElseTest() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + [|M2()|]; + + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + M2(); + } + else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 11)) + { + [|M2()|]; + } + else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + { + [|M2()|]; + } + else if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12)) + { + M2(); + } + else + { + [|M2()|]; + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseTestWithNegation() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + [|M2()|]; + else + M2(); + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseIfElseTestWithNegation() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + [|M2()|]; + else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 1, 1)) + M2(); + else + M2(); + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfTestWithNegationAndReturn() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + return; + M2(); + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfTestWithLogicalAnd() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && + RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + { + M2(); + } + + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) && + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12)) + { + M2(); + } + + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) && + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 12)) + { + [|M2()|]; + } + + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && 1 == 1) + { + M2(); + } + + [|M2()|]; + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseTestWithLogicalAnd() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && + RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + { + M2(); + } + else + { + [|M2()|]; + } + + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) && + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 12)) + { + [|M2()|]; + } + else + { + [|M2()|]; + } + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfTestWithLogicalOr() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || + RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + { + [|M2()|]; + } + + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + { + [|M2()|]; + } + + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 12) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + { + [|M2()|]; + } + + [|M2()|]; + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseTestWithLogicalOr() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || + RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + { + [|M2()|]; + } + else + { + [|M2()|]; + } + + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + { + [|M2()|]; + } + else + { + [|M2()|]; + } + + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + [|M2()|]; // Even it is not meaningful check i think it is a bug, it shouldn't warn + } + else + { + [|M2()|]; + } + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseIfElseTestWithLogicalOr() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + [|M2()|]; + + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 5, 1)) + { + [|M2()|]; + } + else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 9)) + { + [|M2()|]; + } + else + [|M2()|]; + + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + { + [|M2()|]; + } + else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + M2(); + } + else + { + [|M2()|]; + } + } + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedCall_SimpleIfElseIfTestWithLogicalOrAnd() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + [|M2()|]; + + if((RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1)) && + (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 2))) + { + [|M2()|]; + } + else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 13) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 3) || + RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 4)) + { + [|M2()|]; + } + else + { + [|M2()|]; + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" ++ MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedWith_ControlFlowAndMultipleChecks() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 8)) + { + [|M2()|]; + + if (RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 2, 0)) + { + [|M2()|]; + } + else if (!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2, 1)) + { + [|M2()|]; + } + else + { + M2(); + } + + [|M2()|]; + } + else + { + [|M2()|]; + } + + [|M2()|]; + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedWith_DebugAssertAnalysisTest() + { + var source = @" +using System.Diagnostics; +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + [|M2()|]; + + Debug.Assert(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)); + + M2(); + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedWith_ResultSavedInLocal() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + var x1 = RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11); + var x2 = RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1); + + if (x1) + { + M2(); + } + + if (x1 || x2) + { + [|M2()|]; + } + + if (x2) + [|M2()|]; + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task GuardedWith_VersionSavedInLocal() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + var v11 = 11; + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, v11)) + { + M2(); + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task PlatformSavedInLocal_NotYetSupported() // TODO do we want to support it? + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + var platform = OSPlatform.Windows; + if (RuntimeInformationHelper.IsOSPlatformOrLater(platform, 11)) + { + [|M2()|]; + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task UnrelatedConditionCheckDoesNotInvalidateState() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1(bool flag1, bool flag2) + { + [|M2()|]; + + if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + M2(); + + if (flag1 || flag2) + { + M2(); + } + else + { + M2(); + } + + M2(); + } + + if (flag1 || flag2) + { + [|M2()|]; + } + else + { + [|M2()|]; + } + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } +}" + MockPlatformApiSource; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + /* [Fact] TODO: Not working anymore, fix this + public async Task InterproceduralAnalysisTest() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +class Test +{ + void M1() + { + {|#0:M2()|}; + + if (IsWindows11OrLater()) + { + M2(); + } + + {|#1:M2()|}; + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } + + bool IsWindows11OrLater() + { + return RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows,10,2,3,4); + } +}" + MockPlatformApiSource; + + var test = new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (".editorconfig", "dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive") } + } + }; + + test.ExpectedDiagnostics.AddRange(new[] + { + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithLocation(0).WithArguments("M2", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithLocation(1).WithArguments("M2", "Windows", "10.1.2.3"), + }); + + await test.RunAsync(); + }*/ + } +} diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index d2e873a97e..a997e7726e 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -1,7 +1,5 @@ // 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.Runtime.InteropServices; -using System.Runtime.Versioning; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using Xunit; @@ -11,7 +9,7 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests { - public class PlatformCompatabilityAnalyzerTests + public partial class PlatformCompatabilityAnalyzerTests { [Fact] public async Task OsDependentMethodCalledWarns() @@ -66,6 +64,39 @@ public int M3(int option) await VerifyCS.VerifyAnalyzerAsync(source); } + [Fact] + public async Task OsDependentPropertyConditionalCheckNotWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + [MinimumOSPlatform(""Windows10.1.1"")] + public string WindowsStringProperty { get; set; } + [MinimumOSPlatform(""Windows10.1"")] + public int WindowsIntProperty { get; set; } + public void M1() + { + [|WindowsStringProperty|] = ""Hello""; + string s = [|WindowsStringProperty|]; + bool check = s == WindowsStringProperty; + M2([|WindowsStringProperty|]); + M3([|WindowsIntProperty|]); + } + public string M2(string option) + { + return option; + } + public int M3(int option) + { + return option; + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + [Fact] public async Task OsDependentEnumValueCalledWarns() { @@ -78,14 +109,45 @@ public void M1() { PlatformEnum val = [|PlatformEnum.Windows10|]; M2([|PlatformEnum.Windows10|]); - M3([|PlatformEnum.Linux48|]); + M2([|PlatformEnum.Linux48|]); M2(PlatformEnum.NoPlatform); } public PlatformEnum M2(PlatformEnum option) { return option; } - public PlatformEnum M3(PlatformEnum option) +} + +public enum PlatformEnum +{ + [MinimumOSPlatform(""Windows10.0"")] + Windows10, + [MinimumOSPlatform(""Linux4.8"")] + Linux48, + NoPlatform +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task OsDependentEnumConditionalCheckNotWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test2 +{ + public void M1() + { + PlatformEnum val = [|PlatformEnum.Windows10|]; + if (val == PlatformEnum.Windows10) + return; + M2([|PlatformEnum.Windows10|]); + M2([|PlatformEnum.Linux48|]); + M2(PlatformEnum.NoPlatform); + } + public PlatformEnum M2(PlatformEnum option) { return option; } @@ -478,871 +540,6 @@ public void M2() }.RunAsync(); } - [Fact] - public async Task GuardedCalled_SimpleIf_NotWarns() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -public class Test -{ - public void M1() - { - [|M2()|]; - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) - M2(); - } - [MinimumOSPlatform(""Windows10.1.2.3"")] - public void M2() - { - } -} -" + MockPlatformApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedCall_MultipleSimpleIfTests() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -public class Test -{ - public void M1() - { - [|M2()|]; - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) - M2(); - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 10, 1, 2, 3)) - [|M2()|]; - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) - M2(); - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 8, 1, 2, 3)) - [|M2()|]; - } - [MinimumOSPlatform(""Windows10.1.2.3"")] - public void M2() - { - } -} -" + MockPlatformApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedWith_IsOSPlatformOrLater_SimpleIfElseTest() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) - { - M2(); - } - else - { - [|M2()|]; - } - } - - [MinimumOSPlatform(""Windows10.1.2.3"")] - void M2() - { - } -}" + MockPlatformApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedWith_IsOSPlatformEarlierThan_SimpleIfElseTest() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 10)) - { - [|M2()|]; - M3(); - } - else - { - [|M2()|]; - [|M3()|]; - } - } - - [MinimumOSPlatform(""Windows10.1.2.3"")] - void M2() - { - } - - [ObsoletedInOSPlatform(""Windows10.1.2.3"")] - void M3 () - { - } -}" + MockPlatformApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedWith_StringOverload_SimpleIfElseTest() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(""Windows10.1"")) - { - [|M2()|]; - M3(); - } - else - { - [|M2()|]; - [|M3()|]; - } - - if(RuntimeInformationHelper.IsOSPlatformOrLater(""Windows10.1.3"")) - { - [|M3()|]; - M2(); - } - else - { - [|M2()|]; - [|M3()|]; - } - } - - [MinimumOSPlatform(""Windows10.1.2.3"")] - void M2() - { - } - - [ObsoletedInOSPlatform(""Windows10.1.2.3"")] - void M3 () - { - } -}" + MockPlatformApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task OsDependentEnumValue_GuardedWith_SimpleIfElseTest() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -public class Test2 -{ - public void M1() - { - PlatformEnum val = [|PlatformEnum.Windows10|]; - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10)) - { - M2(PlatformEnum.Windows10); - } - else - { - M2([|PlatformEnum.Windows10|]); - } - M2([|PlatformEnum.Linux48|]); - M2(PlatformEnum.NoPlatform); - } - public PlatformEnum M2(PlatformEnum option) - { - return option; - } -} - -public enum PlatformEnum -{ - [MinimumOSPlatform(""Windows10.0"")] - Windows10, - [MinimumOSPlatform(""Linux4.8"")] - Linux48, - NoPlatform -} -" + MockPlatformApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedCall_SimpleIfElseIfElseTest() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - [|M2()|]; - - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) - { - M2(); - } - else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 11)) - { - [|M2()|]; - } - else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) - { - [|M2()|]; - } - else if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12)) - { - M2(); - } - else - { - [|M2()|]; - } - } - - [MinimumOSPlatform(""Windows10.1.2.3"")] - void M2() - { - } -}" + MockPlatformApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedCall_SimpleIfElseTestWithNegation() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -public class Test -{ - public void M1() - { - [|M2()|]; - if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) - [|M2()|]; - else - M2(); - } - [MinimumOSPlatform(""Windows10.1.2.3"")] - public void M2() - { - } -} -" + MockPlatformApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedCall_SimpleIfElseIfElseTestWithNegation() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -public class Test -{ - public void M1() - { - [|M2()|]; - if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) - [|M2()|]; - else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 1, 1)) - M2(); - else - M2(); - } - [MinimumOSPlatform(""Windows10.1.2.3"")] - public void M2() - { - } -} -" + MockPlatformApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedCall_SimpleIfTestWithNegationAndReturn() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -public class Test -{ - public void M1() - { - [|M2()|]; - if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) - return; - M2(); - } - [MinimumOSPlatform(""Windows10.1.2.3"")] - public void M2() - { - } -} -" + MockPlatformApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedCall_SimpleIfTestWithLogicalAnd() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -public class Test -{ - public void M1() - { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && - RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) - { - M2(); - } - - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) && - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12)) - { - M2(); - } - - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) && - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 12)) - { - [|M2()|]; - } - - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && 1 == 1) - { - M2(); - } - - [|M2()|]; - } - [MinimumOSPlatform(""Windows10.1.2.3"")] - public void M2() - { - } -} -" + MockPlatformApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedCall_SimpleIfElseTestWithLogicalAnd() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -public class Test -{ - public void M1() - { - [|M2()|]; - - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && - RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) - { - M2(); - } - else - { - [|M2()|]; - } - - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) && - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 12)) - { - [|M2()|]; - } - else - { - [|M2()|]; - } - } - [MinimumOSPlatform(""Windows10.1.2.3"")] - public void M2() - { - } -} -" + MockPlatformApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedCall_SimpleIfTestWithLogicalOr() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -public class Test -{ - public void M1() - { - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || - RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) - { - [|M2()|]; - } - - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) - { - [|M2()|]; - } - - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 12) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) - { - [|M2()|]; - } - - [|M2()|]; - } - [MinimumOSPlatform(""Windows10.1.2.3"")] - public void M2() - { - } -} -" + MockPlatformApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedCall_SimpleIfElseTestWithLogicalOr() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -public class Test -{ - public void M1() - { - [|M2()|]; - - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || - RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) - { - [|M2()|]; - } - else - { - [|M2()|]; - } - - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) - { - [|M2()|]; - } - else - { - [|M2()|]; - } - - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) - { - [|M2()|]; // Even it is not meaningful check i think it is a bug, it shouldn't warn - } - else - { - [|M2()|]; - } - } - [MinimumOSPlatform(""Windows10.1.2.3"")] - public void M2() - { - } -} -" + MockPlatformApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedCall_SimpleIfElseIfElseTestWithLogicalOr() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -public class Test -{ - public void M1() - { - [|M2()|]; - - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 5, 1)) - { - [|M2()|]; - } - else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 9)) - { - [|M2()|]; - } - else - [|M2()|]; - - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) - { - [|M2()|]; - } - else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) - { - M2(); - } - else - { - [|M2()|]; - } - } - [MinimumOSPlatform(""Windows10.1.2.3"")] - public void M2() - { - } -} -" + MockPlatformApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedCall_SimpleIfElseIfTestWithLogicalOrAnd() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - [|M2()|]; - - if((RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1)) && - (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 2))) - { - [|M2()|]; - } - else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 13) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 3) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 4)) - { - [|M2()|]; - } - else - { - [|M2()|]; - } - } - - [MinimumOSPlatform(""Windows10.1.2.3"")] - void M2() - { - } -}" -+ MockPlatformApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedWith_ControlFlowAndMultipleChecks() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 8)) - { - [|M2()|]; - - if (RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 2, 0)) - { - [|M2()|]; - } - else if (!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2, 1)) - { - [|M2()|]; - } - else - { - M2(); - } - - [|M2()|]; - } - else - { - [|M2()|]; - } - - [|M2()|]; - } - - [MinimumOSPlatform(""Windows10.1.2.3"")] - void M2() - { - } -}" + MockPlatformApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedWith_DebugAssertAnalysisTest() - { - var source = @" -using System.Diagnostics; -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - [|M2()|]; - - Debug.Assert(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)); - - M2(); - } - - [MinimumOSPlatform(""Windows10.1.2.3"")] - void M2() - { - } -}" + MockPlatformApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedWith_ResultSavedInLocal() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - var x1 = RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11); - var x2 = RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1); - - if (x1) - { - M2(); - } - - if (x1 || x2) - { - [|M2()|]; - } - - if (x2) - [|M2()|]; - } - - [MinimumOSPlatform(""Windows10.1.2.3"")] - void M2() - { - } -}" + MockPlatformApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task GuardedWith_VersionSavedInLocal() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - var v11 = 11; - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, v11)) - { - M2(); - } - } - - [MinimumOSPlatform(""Windows10.1.2.3"")] - void M2() - { - } -}" + MockPlatformApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task PlatformSavedInLocal_NotYetSupported() // TODO do we want to support it? - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - var platform = OSPlatform.Windows; - if (RuntimeInformationHelper.IsOSPlatformOrLater(platform, 11)) - { - [|M2()|]; - } - } - - [MinimumOSPlatform(""Windows10.1.2.3"")] - void M2() - { - } -}" + MockPlatformApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - [Fact] - public async Task UnrelatedConditionCheckDoesNotInvalidateState() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -class Test -{ - void M1(bool flag1, bool flag2) - { - [|M2()|]; - - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) - { - M2(); - - if (flag1 || flag2) - { - M2(); - } - else - { - M2(); - } - - M2(); - } - - if (flag1 || flag2) - { - [|M2()|]; - } - else - { - [|M2()|]; - } - } - - [MinimumOSPlatform(""Windows10.1.2.3"")] - void M2() - { - } -}" + MockPlatformApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source); - } - - /* [Fact] TODO: Not working anymore, fix this - public async Task InterproceduralAnalysisTest() - { - var source = @" -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; - - if (IsWindows11OrLater()) - { - M2(); - } - - {|#1:M2()|}; - } - - [MinimumOSPlatform(""Windows10.1.2.3"")] - void M2() - { - } - - bool IsWindows11OrLater() - { - return RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows,10,2,3,4); - } -}" + MockPlatformApiSource; - - var test = new VerifyCS.Test - { - TestState = - { - Sources = { source }, - AdditionalFiles = { (".editorconfig", "dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive") } - } - }; - - test.ExpectedDiagnostics.AddRange(new[] - { - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithLocation(0).WithArguments("M2", "Windows", "10.1.2.3"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithLocation(1).WithArguments("M2", "Windows", "10.1.2.3"), - }); - - await test.RunAsync(); - }*/ - private readonly string MockPlatformApiSource = @" namespace System.Runtime.Versioning { From 5a21e8ee81bb91ff55b8752b000ec6db0754acb8 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 17 Jul 2020 18:23:07 -0700 Subject: [PATCH 07/48] Add/fix tests --- .../PlatformCompatabilityAnalyzer.cs | 13 +++- ...ompatabilityAnalyzerTests.GuardedCalls.cs} | 14 +++-- .../PlatformCompatabilityAnalyzerTests.cs | 59 +++++++++++-------- 3 files changed, 55 insertions(+), 31 deletions(-) rename src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/{PlatformCompatabilityAnalyserTests.GuardedCalls.cs => PlatformCompatabilityAnalyzerTests.GuardedCalls.cs} (97%) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index ce1f7bffed..998b4ad5c9 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -236,12 +236,12 @@ private static void AnalyzeInvocationOperation(IOperation operation, OperationAn } else if (operation is IPropertyReferenceOperation pOperation) { - if (!(pOperation.Parent is IBinaryOperation bo && (bo.OperatorKind == BinaryOperatorKind.Equals || bo.OperatorKind == BinaryOperatorKind.NotEquals))) + if (!IsWithinConditionalOperation(operation)) attributes = FindAllPlatformAttributesApplied(pOperation.Property.GetAttributes(), pOperation.Property.ContainingType); } else if (operation is IFieldReferenceOperation fOperation) { - if (!(fOperation.Parent is IBinaryOperation bo && (bo.OperatorKind == BinaryOperatorKind.Equals || bo.OperatorKind == BinaryOperatorKind.NotEquals))) + if (!IsWithinConditionalOperation(operation)) attributes = FindAllPlatformAttributesApplied(fOperation.Field.GetAttributes(), fOperation.Field.ContainingType); } @@ -265,6 +265,15 @@ private static void AnalyzeInvocationOperation(IOperation operation, OperationAn } } + private static bool IsWithinConditionalOperation(IOperation pOperation) => + 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 IsSuppressedByTfm(List? parsedTfms, PlatformAttrbiuteInfo parsedAttribute) { if (parsedTfms != null) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyserTests.GuardedCalls.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs similarity index 97% rename from src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyserTests.GuardedCalls.cs rename to src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs index 76e3a28db5..ff22dd2ce4 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyserTests.GuardedCalls.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs @@ -1,10 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Text; +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace Microsoft.CodeAnalysis.NetAnalyzers.UnitTests.Microsoft.NetCore.Analyzers.InteropServices +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 PlatformCompatabilityAnalyserTests + public partial class PlatformCompatabilityAnalyzerTests { [Fact] public async Task GuardedCalled_SimpleIf_NotWarns() diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index a997e7726e..de88aad017 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -12,7 +12,7 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests public partial class PlatformCompatabilityAnalyzerTests { [Fact] - public async Task OsDependentMethodCalledWarns() + public async Task OsDependentMethodsCalledWarns() { var source = @" using System.Runtime.Versioning; @@ -21,10 +21,20 @@ public class Test { public void M1() { - [|M2()|]; + [|WindowsOnly()|]; + [|Obsoleted()|]; + [|Removed()|]; } [MinimumOSPlatform(""Windows10.1.1.1"")] - public void M2() + public void WindowsOnly() + { + } + [ObsoletedInOSPlatform(""Linux4.1"")] + public void Obsoleted() + { + } + [RemovedInOSPlatform(""Linux4.1"")] + public void Removed() { } } @@ -42,14 +52,18 @@ public class Test { [MinimumOSPlatform(""Windows10.1.1"")] public string WindowsStringProperty { get; set; } - [MinimumOSPlatform(""Windows10.1"")] - public int WindowsIntProperty { get; set; } + [ObsoletedInOSPlatform(""ios4.1"")] + public int ObsoleteIntProperty { get; set; } + [RemovedInOSPlatform(""Linux4.1"")] + public byte RemovedProperty { get; } public void M1() { [|WindowsStringProperty|] = ""Hello""; string s = [|WindowsStringProperty|]; M2([|WindowsStringProperty|]); - M3([|WindowsIntProperty|]); + [|ObsoleteIntProperty|] = 5; + M3([|ObsoleteIntProperty|]); + M3([|RemovedProperty|]); } public string M2(string option) { @@ -64,31 +78,28 @@ public int M3(int option) await VerifyCS.VerifyAnalyzerAsync(source); } - [Fact] - public async Task OsDependentPropertyConditionalCheckNotWarns() + [Theory] + [InlineData("MinimumOSPlatform", "string StringProperty", " == StringProperty", @"StringProperty|] = ""Hello""", "StringProperty")] + [InlineData("ObsoletedInOSPlatform", "int IntProperty", " > IntProperty", "IntProperty|] = 5", "IntProperty")] + [InlineData("RemovedInOSPlatform", "int RemovedProperty", " <= RemovedProperty", "RemovedProperty|] = 3", "RemovedProperty")] + public async Task OsDependentPropertyConditionalCheckNotWarns(string attribute, string property, string condition, string setter, string getter) { var source = @" using System.Runtime.Versioning; public class Test { - [MinimumOSPlatform(""Windows10.1.1"")] - public string WindowsStringProperty { get; set; } - [MinimumOSPlatform(""Windows10.1"")] - public int WindowsIntProperty { get; set; } + [" + attribute + @"(""Windows10.1.1"")] + public " + property + @" { get; set; } + public void M1() { - [|WindowsStringProperty|] = ""Hello""; - string s = [|WindowsStringProperty|]; - bool check = s == WindowsStringProperty; - M2([|WindowsStringProperty|]); - M3([|WindowsIntProperty|]); + [|" + setter + @"; + var s = [|" + getter + @"|]; + bool check = s " + condition + @"; + M2([|" + getter + @"|]); } - public string M2(string option) - { - return option; - } - public int M3(int option) + public object M2(object option) { return option; } @@ -221,7 +232,7 @@ public void M2() }*/ [Fact] - public async Task OsDependentMethodCalledFromIntanceWarns() + public async Task OsDependentMethodCalledFromInstanceWarns() { var source = @" using System.Runtime.Versioning; @@ -246,7 +257,7 @@ public void M2() } [Fact] - public async Task OsDependentMethodCalledFromOtherNsIntanceWarns() + public async Task OsDependentMethodCalledFromOtherNsInstanceWarns() { var source = @" using System.Runtime.Versioning; From 7e7e7344c2c5169f3db1941e21ac37a7ca250fbe Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 20 Jul 2020 12:46:53 -0700 Subject: [PATCH 08/48] Add more tests --- .../PlatformCompatabilityAnalyzer.Data.cs | 2 +- .../MicrosoftNetCoreAnalyzersResources.resx | 3 - .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 5 -- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 5 -- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 5 -- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 5 -- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 5 -- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 5 -- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 5 -- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 5 -- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 5 -- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 5 -- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 5 -- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 5 -- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 5 -- .../PlatformCompatabilityAnalyzerTests.cs | 55 ++++++++++++++++++- 16 files changed, 54 insertions(+), 71 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index d08473bc3b..b325d17b95 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -47,7 +47,7 @@ public static bool TryParseAttributeData(AttributeData osAttibute, out PlatformA parsedAttribute.AttributeType = PlatformAttrbiteType.None; break; } - if (TryParsePlatformString(osAttibute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version)) + if (!osAttibute.ConstructorArguments[0].IsNull && TryParsePlatformString(osAttibute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version)) { parsedAttribute.OsPlatformName = platformName; parsedAttribute.Version = version; diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index daaef827de..78852e0ffc 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1362,9 +1362,6 @@ Call of platform dependent API - - TODO Platform checks:'{0}' - Using platform dependendent API on a component makes the code no longer work across all platforms. 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 36145ec6c2..df0e1ae2fb 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1707,11 +1707,6 @@ Zkontrolujte dotazy SQL pro chyby zabezpečení - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie Nastavit HttpOnly na hodnotu true pro HttpCookie 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 31889b39a9..cb7d3ee308 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1707,11 +1707,6 @@ SQL-Abfragen auf Sicherheitsrisiken überprüfen - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie HttpOnly für HttpCookie auf TRUE festlegen 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 7e0c119e10..7c6ae70bb0 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1707,11 +1707,6 @@ Revisar consultas SQL para comprobar si tienen vulnerabilidades de seguridad - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie Establecer HttpOnly en true para HttpCookie 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 ba4e67601f..167e4cc454 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1707,11 +1707,6 @@ Vérifier si les requêtes SQL présentent des failles de sécurité - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie Affectez la valeur true à HttpOnly pour HttpCookie 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 ca29517047..c54acb4fee 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1707,11 +1707,6 @@ Controllare l'eventuale vulnerabilità di sicurezza delle query SQL - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie Impostare HttpOnly su true per HttpCookie 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 893211be49..3274fa20c8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1707,11 +1707,6 @@ SQL クエリのセキュリティ脆弱性を確認 - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie HttpCookie で HttpOnly を true に設定する 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 4d0aa9ca73..1013c791ae 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1707,11 +1707,6 @@ 보안상 취약한 부분이 있는지 SQL 쿼리를 검토하십시오. - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie HttpCookie에 대해 HttpOnly를 true로 설정 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 6cfc88190d..6ff433f82b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1708,11 +1708,6 @@ Sprawdź zapytania SQL pod kątem luk w zabezpieczeniach - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie Ustaw element HttpOnly na wartość true dla elementu HttpCookie 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 c3d3befa57..3ad370d064 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 @@ -1707,11 +1707,6 @@ Revisar as consultas SQL em busca de vulnerabilidades de segurança - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie Definir HttpOnly como true para HttpCookie 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 02201d665d..42adeeb70e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1707,11 +1707,6 @@ Проверка запросов SQL на уязвимости безопасности - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie Установите для параметра HttpOnly объекта HttpCookie значение true 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 e84823522a..0529acf847 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1707,11 +1707,6 @@ SQL sorgularını güvenlik açıkları için inceleyin - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie HttpCookie için HttpOnly'yi true olarak ayarlayın 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 d10335e368..f9eb29ddd6 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 @@ -1707,11 +1707,6 @@ 检查 SQL 查询是否存在安全漏洞 - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie 将 HttpCookie 的 HttpOnly 设置为 true 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 ab97ec8bf7..ad8e80bb54 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 @@ -1707,11 +1707,6 @@ 必須檢閱 SQL 查詢中是否有安全性弱點 - - TODO Platform checks:'{0}' - TODO Platform checks:'{0}' - - Set HttpOnly to true for HttpCookie 針對 HttpCookie 將 HttpOnly 設為 true diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index de88aad017..fdcfda46d1 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -42,6 +42,57 @@ public void Removed() await VerifyCS.VerifyAnalyzerAsync(source); } + [Fact] + public async Task WrongPlatformStringsShouldHandledGracefully() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + public void M1() + { + Windows10(); + Windows1_2_3_4_5(); + [|ObsoletedLinuxDash4_1()|]; + [|ObsoletedLinuxStar4_1()|]; + [|RemovedLinu4_1()|]; + ObsoletedWithNullString(); + RemovedWithEmptyString(); + } + [MinimumOSPlatform(""Windows10"")] + public void Windows10() + { + } + [MinimumOSPlatform(""Windows1.2.3.4.5"")] + public void Windows1_2_3_4_5() + { + } + [ObsoletedInOSPlatform(""Linux-4.1"")] + public void ObsoletedLinuxDash4_1() + { + } + [ObsoletedInOSPlatform(""Linux*4.1"")] + public void ObsoletedLinuxStar4_1() + { + } + [ObsoletedInOSPlatform(null)] + public void ObsoletedWithNullString() + { + } + [RemovedInOSPlatform(""Linu4.1"")] + public void RemovedLinu4_1() + { + } + [RemovedInOSPlatform("""")] + public void RemovedWithEmptyString() + { + } +} +" + MockPlatformApiSource; + await VerifyCS.VerifyAnalyzerAsync(source); + } + [Fact] public async Task OsDependentPropertyCalledWarns() { @@ -131,9 +182,9 @@ public PlatformEnum M2(PlatformEnum option) public enum PlatformEnum { - [MinimumOSPlatform(""Windows10.0"")] + [MinimumOSPlatform(""windows10.0"")] Windows10, - [MinimumOSPlatform(""Linux4.8"")] + [MinimumOSPlatform(""linux4.8"")] Linux48, NoPlatform } From e80c51aa3bbc1875f592b7debc3e724c4b9e5cb1 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 21 Jul 2020 09:34:41 -0700 Subject: [PATCH 09/48] Osx equals mac and vise versa, add more tests and refactoring --- .../PlatformCompatabilityAnalyzer.Data.cs | 2 +- .../PlatformCompatabilityAnalyzer.cs | 26 ++- ...CompatabilityAnalyzerTests.GuardedCalls.cs | 92 +++++++--- .../PlatformCompatabilityAnalyzerTests.cs | 158 +++++++----------- 4 files changed, 151 insertions(+), 127 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index b325d17b95..b9d6b03500 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -73,7 +73,7 @@ public override bool Equals(object obj) public static bool operator !=(PlatformAttrbiuteInfo left, PlatformAttrbiuteInfo right) => !(left == right); public bool Equals(PlatformAttrbiuteInfo other) => - AttributeType == other.AttributeType && OsPlatformName == other.OsPlatformName && Version.Equals(other.Version); + AttributeType == other.AttributeType && IsOSPlatformsEqual(OsPlatformName, other.OsPlatformName) && Version.Equals(other.Version); internal static bool TryParseTfmString(string osString, out PlatformAttrbiuteInfo parsedTfm) { diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 998b4ad5c9..3c93c79ee8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -71,15 +71,18 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { - var typeName = WellKnownTypeNames.SystemRuntimeInteropServicesRuntimeInformation + "Helper"; + var typeName = WellKnownTypeNames.SystemRuntimeInteropServicesRuntimeInformation; - if (!context.Compilation.TryGetOrCreateTypeByMetadataName(typeName, out var runtimeInformationType) || - !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesOSPlatform, out var osPlatformType)) + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(typeName + "Helper", out var runtimeInformationType)) + { + runtimeInformationType = context.Compilation.GetOrCreateTypeByMetadataName(typeName); + } + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesOSPlatform, out var osPlatformType)) { return; } - var guardMethods = GetRuntimePlatformGuardMethods(runtimeInformationType); + var guardMethods = GetRuntimePlatformGuardMethods(runtimeInformationType!); context.RegisterOperationBlockStartAction(context => AnalyzerOperationBlock(context, guardMethods, osPlatformType)); }); @@ -168,7 +171,7 @@ private static void ReportDiangosticsIfNotGuarded(IOperation operation, Immutabl { if (!info.Negated) { - if (attribute.OsPlatformName.Equals(info.PlatformPropertyName, StringComparison.InvariantCultureIgnoreCase)) + if (IsOSPlatformsEqual(attribute.OsPlatformName, info.PlatformPropertyName)) { if (info.InvokedPlatformCheckMethodName.Equals(s_platformCheckMethods[0], StringComparison.InvariantCulture)) { @@ -280,7 +283,7 @@ private static bool IsSuppressedByTfm(List? parsedTfms, P { foreach (PlatformAttrbiuteInfo tfm in parsedTfms) { - if (tfm.OsPlatformName.Equals(parsedAttribute.OsPlatformName, StringComparison.InvariantCultureIgnoreCase)) + if (IsOSPlatformsEqual(parsedAttribute.OsPlatformName, tfm.OsPlatformName)) { return AttributeVersionsMatch(parsedAttribute, tfm); } @@ -290,6 +293,13 @@ private static bool IsSuppressedByTfm(List? parsedTfms, P return false; } + private static bool IsOSPlatformsEqual(string firstOs, string secondOs) + { + return firstOs.Equals(secondOs, StringComparison.OrdinalIgnoreCase) + || (firstOs.Equals("OSX", StringComparison.OrdinalIgnoreCase) && secondOs.Equals("MACOS", StringComparison.OrdinalIgnoreCase)) + || (secondOs.Equals("OSX", StringComparison.OrdinalIgnoreCase) && firstOs.Equals("MACOS", StringComparison.OrdinalIgnoreCase)); + } + private static DiagnosticDescriptor SwitchRule(PlatformAttrbiteType attributeType) { if (attributeType == PlatformAttrbiteType.MinimumOSPlatformAttribute) @@ -384,11 +394,11 @@ private static bool IsSuppressedByAttribute(PlatformAttrbiuteInfo diagnosingAttr { foreach (AttributeData attribute in attributes) { - if (diagnosingAttribute.AttributeType.ToString().Equals(attribute.AttributeClass.Name, StringComparison.InvariantCulture)) + if (diagnosingAttribute.AttributeType.ToString() == attribute.AttributeClass.Name) { if (PlatformAttrbiuteInfo.TryParseAttributeData(attribute, out PlatformAttrbiuteInfo parsedAttribute)) { - if (diagnosingAttribute.OsPlatformName == parsedAttribute.OsPlatformName && AttributeVersionsMatch(diagnosingAttribute, parsedAttribute)) + if (IsOSPlatformsEqual(diagnosingAttribute.OsPlatformName, parsedAttribute.OsPlatformName) && AttributeVersionsMatch(diagnosingAttribute, parsedAttribute)) { return true; } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs index ff22dd2ce4..d86218cf5d 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs @@ -30,7 +30,7 @@ public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -60,7 +60,7 @@ public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -89,7 +89,7 @@ void M1() void M2() { } -}" + MockPlatformApiSource; +}" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -126,7 +126,7 @@ void M2() void M3 () { } -}" + MockPlatformApiSource; +}" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -174,7 +174,7 @@ void M2() void M3 () { } -}" + MockPlatformApiSource; +}" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -216,7 +216,7 @@ public enum PlatformEnum Linux48, NoPlatform } -" + MockPlatformApiSource; +" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -259,7 +259,7 @@ void M1() void M2() { } -}" + MockPlatformApiSource; +}" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -286,7 +286,7 @@ public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -314,7 +314,7 @@ public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -339,7 +339,7 @@ public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -384,7 +384,7 @@ public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -426,7 +426,7 @@ public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -466,7 +466,7 @@ public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -518,7 +518,7 @@ public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -566,7 +566,7 @@ public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -607,7 +607,7 @@ void M2() { } }" -+ MockPlatformApiSource; ++ MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -653,7 +653,7 @@ void M1() void M2() { } -}" + MockPlatformApiSource; +}" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -681,7 +681,7 @@ void M1() void M2() { } -}" + MockPlatformApiSource; +}" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -718,7 +718,7 @@ void M1() void M2() { } -}" + MockPlatformApiSource; +}" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -745,7 +745,7 @@ void M1() void M2() { } -}" + MockPlatformApiSource; +}" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -772,7 +772,7 @@ void M1() void M2() { } -}" + MockPlatformApiSource; +}" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -820,12 +820,12 @@ void M1(bool flag1, bool flag2) void M2() { } -}" + MockPlatformApiSource; +}" + MockAttributesSource + MockRuntimeApiSource; await VerifyCS.VerifyAnalyzerAsync(source); } - /* [Fact] TODO: Not working anymore, fix this + /*[Fact] //TODO: Not working anymore, fix this public async Task InterproceduralAnalysisTest() { var source = @" @@ -855,7 +855,7 @@ bool IsWindows11OrLater() { return RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows,10,2,3,4); } -}" + MockPlatformApiSource; +}" + MockAttributesSource + MockRuntimeApiSource; var test = new VerifyCS.Test { @@ -874,5 +874,49 @@ bool IsWindows11OrLater() await test.RunAsync(); }*/ + + private readonly string MockRuntimeApiSource = @" +namespace System.Runtime.InteropServices +{ + public static class RuntimeInformationHelper + { +#pragma warning disable CA1801, IDE0060 // Review unused parameters + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major) + { + return true; + } + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor) + { + return true; + } + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build) + { + return true; + } + public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build, int revision) + { + return true; + } + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major) + { + return false; + } + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor) + { + return false; + } + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build) + { + return false; + } + public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build, int revision) + { + return false; + } + public static bool IsOSPlatformOrLater(string platformName) => true; + public static bool IsOSPlatformEarlierThan(string platformName) => true; +#pragma warning restore CA1801, IDE0060 // Review unused parameters + } +}"; } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index fdcfda46d1..d478b8b6a2 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -38,7 +38,7 @@ public void Removed() { } } -" + MockPlatformApiSource; +" + MockAttributesSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -89,7 +89,7 @@ public void RemovedWithEmptyString() { } } -" + MockPlatformApiSource; +" + MockAttributesSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -125,7 +125,7 @@ public int M3(int option) return option; } } -" + MockPlatformApiSource; +" + MockAttributesSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -155,7 +155,7 @@ public object M2(object option) return option; } } -" + MockPlatformApiSource; +" + MockAttributesSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -188,7 +188,7 @@ public enum PlatformEnum Linux48, NoPlatform } -" + MockPlatformApiSource; +" + MockAttributesSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -223,7 +223,7 @@ public enum PlatformEnum Linux48, NoPlatform } -" + MockPlatformApiSource; +" + MockAttributesSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -257,11 +257,11 @@ public int M3(int option) return option; } } -" + MockPlatformApiSource; +" + MockAttributesSource; await VerifyCS.VerifyAnalyzerAsync(source); } - /*[Fact] TODO: Add the attribute assembly level and test + /*[Fact] TODO: test with assembly level attribute public async Task MethodWithTargetPlatrofrmAttributeDoesNotWarn() { var source = @" @@ -303,7 +303,7 @@ public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -333,7 +333,7 @@ public void M2() } } } -" + MockPlatformApiSource; +" + MockAttributesSource; await VerifyCS.VerifyAnalyzerAsync(source); } @@ -359,11 +359,11 @@ public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource; await VerifyCS.VerifyAnalyzerAsync(source); } - /* [Fact] TODO find out how to pass 2 sources or wait until assembly level APIs merged + /*[Fact] TODO wait until assembly level APIs merged public async Task MethodOfOsDependentAssemblyCalledWithoutSuppressionWarns() { var source = @" @@ -393,15 +393,20 @@ await VerifyCS.VerifyAnalyzerAsync(source, }*/ [Theory] - [InlineData("10.1.2.3", "10.1.2.3", false)] - [InlineData("10.1.2.3", "10.1.3.3", false)] - [InlineData("10.1.2.3", "10.1.3.1", false)] - [InlineData("10.1.2.3", "11.1.2.3", false)] - [InlineData("10.1.2.3", "10.2.2.0", false)] - [InlineData("10.1.2.3", "10.1.1.3", true)] - [InlineData("10.1.2.3", "10.1.1.4", true)] - [InlineData("10.1.2.3", "10.0.1.9", true)] - [InlineData("10.1.2.3", "8.2.3.3", true)] + [InlineData("Windows10.1.2.3", "Windows10.1.2.3", false)] + [InlineData("windows10.1.2.3", "Windows10.1.2.3", false)] + [InlineData("WINDOWS10.1.2.3", "Windows10.1.3.3", false)] + [InlineData("Windows10.1.2.3", "WindowS10.1.3.1", false)] + [InlineData("Windows10.1.2.3", "Windows11.1.2.3", false)] + [InlineData("Windows10.1.2.3", "windows11.1.2.3", false)] + [InlineData("Windows10.1.2.3", "Windows10.2.2.0", false)] + [InlineData("osx10.1.2.3", "macos10.2.2.0", false)] + [InlineData("OSX10.1.2.3", "osx10.2.2.0", false)] + [InlineData("Windows10.1.2.3", "osx10.1.1.3", true)] + [InlineData("Windows10.1.2.3", "Windows10.1.1.3", true)] + [InlineData("Windows10.1.2.3", "Windows10.1.1.4", true)] + [InlineData("Windows10.1.2.3", "Windows10.0.1.9", true)] + [InlineData("Windows10.1.2.3", "Windows8.2.3.3", true)] public async Task MethodOfOsDependentClassSuppressedWithMinimumOsAttribute(string dependentVersion, string suppressingVersion, bool warn) { var source = @" @@ -409,7 +414,7 @@ public async Task MethodOfOsDependentClassSuppressedWithMinimumOsAttribute(strin public class Test { - [MinimumOSPlatform(""Windows" + suppressingVersion + @""")] + [MinimumOSPlatform(""" + suppressingVersion + @""")] public void M1() { OsDependentClass odc = new OsDependentClass(); @@ -417,14 +422,14 @@ public void M1() } } -[MinimumOSPlatform(""Windows" + dependentVersion + @""")] +[MinimumOSPlatform(""" + dependentVersion + @""")] public class OsDependentClass { public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource; if (warn) await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); @@ -433,15 +438,18 @@ public void M2() } [Theory] - [InlineData("10.1.2.3", "10.1.2.3", true)] - [InlineData("10.1.2.3", "10.1.3.3", true)] - [InlineData("10.1.2.3", "10.1.3.1", true)] - [InlineData("10.1.2.3", "11.1.2.3", true)] - [InlineData("10.1.2.3", "10.2.2.0", true)] - [InlineData("10.1.2.3", "10.1.1.3", false)] - [InlineData("10.1.2.3", "10.1.1.4", false)] - [InlineData("10.1.2.3", "10.1.0.1", false)] - [InlineData("10.1.2.3", "8.2.3.4", false)] + [InlineData("Windows10.1.2.3", "Windows10.1.2.3", true)] + [InlineData("Windows10.1.2.3", "Windows10.1.3.3", true)] + [InlineData("Windows10.1.2.3", "Windows10.1.3.1", true)] + [InlineData("Windows10.1.2.3", "Windows11.1.2.3", true)] + [InlineData("Windows10.1.2.3", "Windows10.2.2.0", true)] + [InlineData("Windows10.1.2.3", "Windows10.1.1.3", false)] + [InlineData("Windows10.1.2.3", "WINDOWS10.1.1.3", false)] + [InlineData("Windows10.1.2.3", "Windows10.1.1.4", false)] + [InlineData("osx10.1.2.3", "MacOs10.1.1.4", false)] + [InlineData("macOs10.1.2.3", "Osx10.1.1.4", false)] + [InlineData("windows10.1.2.3", "Windows10.1.0.1", false)] + [InlineData("Windows10.1.2.3", "Windows8.2.3.4", false)] public async Task MethodOfOsDependentClassSuppressedWithObsoleteAttribute(string dependentVersion, string suppressingVersion, bool warn) { var source = @" @@ -449,7 +457,7 @@ public async Task MethodOfOsDependentClassSuppressedWithObsoleteAttribute(string public class Test { - [ObsoletedInOSPlatform(""Windows" + suppressingVersion + @""")] + [ObsoletedInOSPlatform(""" + suppressingVersion + @""")] public void M1() { OsDependentClass odc = new OsDependentClass(); @@ -457,14 +465,14 @@ public void M1() } } -[ObsoletedInOSPlatform(""Windows" + dependentVersion + @""")] +[ObsoletedInOSPlatform(""" + dependentVersion + @""")] public class OsDependentClass { public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource; if (warn) await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); @@ -473,15 +481,19 @@ public void M2() } [Theory] - [InlineData("10.1.2.3", "10.1.2.3", true)] - [InlineData("10.1.2.3", "10.1.3.3", true)] - [InlineData("10.1.2.3", "10.1.3.1", true)] - [InlineData("10.1.2.3", "11.1.2.3", true)] - [InlineData("10.1.2.3", "10.2.2.0", true)] - [InlineData("10.1.2.3", "10.1.1.3", false)] - [InlineData("10.1.2.3", "10.1.1.4", false)] - [InlineData("10.1.2.3", "10.1.0.1", false)] - [InlineData("10.1.2.3", "8.2.3.4", false)] + [InlineData("Windows10.1.2.3", "Windows10.1.2.3", true)] + [InlineData("Windows10.1.2.3", "Windows10.1.3.3", true)] + [InlineData("Windows10.1.2.3", "Windows10.1.3.1", true)] + [InlineData("Windows10.1.2.3", "Windows11.1.2.3", true)] + [InlineData("Windows10.1.2.3", "Windows10.2.2.0", true)] + [InlineData("Windows10.1.2.3", "Windows10.1.1.3", false)] + [InlineData("Windows10.1.2.3", "Windows10.1.1.4", false)] + [InlineData("Macos10.1.2.3", "macos10.1.1.4", false)] + [InlineData("OSX10.1.2.3", "Macos10.1.1.4", false)] + [InlineData("Windows10.1.2.3", "Macos10.1.1.4", true)] + [InlineData("Windows10.1.2.3", "WINDOWS10.1.1.4", false)] + [InlineData("Windows10.1.2.3", "Windows10.1.0.1", false)] + [InlineData("windows10.1.2.3", "Windows8.2.3.4", false)] public async Task MethodOfOsDependentClassSuppressedWithRemovedAttribute(string dependentVersion, string suppressingVersion, bool warn) { var source = @" @@ -489,7 +501,7 @@ public async Task MethodOfOsDependentClassSuppressedWithRemovedAttribute(string public class Test { - [RemovedInOSPlatform(""Windows" + suppressingVersion + @""")] + [RemovedInOSPlatform(""" + suppressingVersion + @""")] public void M1() { OsDependentClass odc = new OsDependentClass(); @@ -497,14 +509,14 @@ public void M1() } } -[RemovedInOSPlatform(""Windows" + dependentVersion + @""")] +[RemovedInOSPlatform(""" + dependentVersion + @""")] public class OsDependentClass { public void M2() { } } -" + MockPlatformApiSource; +" + MockAttributesSource; if (warn) await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RemovedRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); @@ -549,7 +561,7 @@ public void M2() {{ }} }} -" + MockPlatformApiSource +" + MockAttributesSource }, AdditionalFiles = { (".editorconfig", editorConfigText) } }, @@ -594,7 +606,7 @@ public void M2() {{ }} }} -" + MockPlatformApiSource +" + MockAttributesSource }, AdditionalFiles = { (".editorconfig", editorConfigText) } }, @@ -602,7 +614,7 @@ public void M2() }.RunAsync(); } - private readonly string MockPlatformApiSource = @" + private readonly string MockAttributesSource = @" namespace System.Runtime.Versioning { public abstract class OSPlatformAttribute : Attribute @@ -677,48 +689,6 @@ public ObsoletedInOSPlatformAttribute(string platformName, string message) : bas public string Url { get; set; } } } - -namespace System.Runtime.InteropServices -{ - public static class RuntimeInformationHelper - { -#pragma warning disable CA1801, IDE0060 // Review unused parameters - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major) - { - return true; - } - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor) - { - return true; - } - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build) - { - return true; - } - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build, int revision) - { - return true; - } - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major) - { - return false; - } - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor) - { - return false; - } - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build) - { - return false; - } - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build, int revision) - { - return false; - } - public static bool IsOSPlatformOrLater(string platformName) => true; - public static bool IsOSPlatformEarlierThan(string platformName) => true; -#pragma warning restore CA1801, IDE0060 // Review unused parameters - } -}"; +"; } } From 8c2f91041f1bd348e5393b66a1a90162a8d9b36f Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 21 Jul 2020 15:00:41 -0700 Subject: [PATCH 10/48] Apply feedback, more refactoring --- .../PlatformCompatabilityAnalyzer.Data.cs | 38 +-- .../PlatformCompatabilityAnalyzer.cs | 58 ++--- ...CompatabilityAnalyzerTests.GuardedCalls.cs | 47 ++-- .../PlatformCompatabilityAnalyzerTests.cs | 244 ++++++++++-------- src/Test.Utilities/CSharpCodeFixVerifier`2.cs | 1 - 5 files changed, 204 insertions(+), 184 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index b9d6b03500..b55e6c93db 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -19,35 +19,35 @@ namespace Microsoft.NetCore.Analyzers.InteropServices public partial class PlatformCompatabilityAnalyzer { - private enum PlatformAttrbiteType + private enum PlatformAttributeType { None, MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute, TargetPlatformAttribute } - private struct PlatformAttrbiuteInfo : IEquatable + private struct PlatformAttributeInfo : IEquatable { - public PlatformAttrbiteType AttributeType { get; set; } + public PlatformAttributeType AttributeType { get; set; } public string OsPlatformName { get; set; } public Version Version { get; set; } - public static bool TryParseAttributeData(AttributeData osAttibute, out PlatformAttrbiuteInfo parsedAttribute) + public static bool TryParseAttributeData(AttributeData osAttribute, out PlatformAttributeInfo parsedAttribute) { - parsedAttribute = new PlatformAttrbiuteInfo(); - switch (osAttibute.AttributeClass.Name) + parsedAttribute = new PlatformAttributeInfo(); + switch (osAttribute.AttributeClass.Name) { case MinimumOSPlatformAttribute: - parsedAttribute.AttributeType = PlatformAttrbiteType.MinimumOSPlatformAttribute; break; + parsedAttribute.AttributeType = PlatformAttributeType.MinimumOSPlatformAttribute; break; case ObsoletedInOSPlatformAttribute: - parsedAttribute.AttributeType = PlatformAttrbiteType.ObsoletedInOSPlatformAttribute; break; + parsedAttribute.AttributeType = PlatformAttributeType.ObsoletedInOSPlatformAttribute; break; case RemovedInOSPlatformAttribute: - parsedAttribute.AttributeType = PlatformAttrbiteType.RemovedInOSPlatformAttribute; break; + parsedAttribute.AttributeType = PlatformAttributeType.RemovedInOSPlatformAttribute; break; case TargetPlatformAttribute: - parsedAttribute.AttributeType = PlatformAttrbiteType.TargetPlatformAttribute; break; + parsedAttribute.AttributeType = PlatformAttributeType.TargetPlatformAttribute; break; default: - parsedAttribute.AttributeType = PlatformAttrbiteType.None; break; + parsedAttribute.AttributeType = PlatformAttributeType.None; break; } - if (!osAttibute.ConstructorArguments[0].IsNull && TryParsePlatformString(osAttibute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version)) + if (!osAttribute.ConstructorArguments[0].IsNull && TryParsePlatformString(osAttribute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version)) { parsedAttribute.OsPlatformName = platformName; parsedAttribute.Version = version; @@ -59,7 +59,7 @@ public static bool TryParseAttributeData(AttributeData osAttibute, out PlatformA public override bool Equals(object obj) { - if (obj is PlatformAttrbiuteInfo info) + if (obj is PlatformAttributeInfo info) { return Equals(info); } @@ -68,17 +68,17 @@ public override bool Equals(object obj) public override int GetHashCode() => HashUtilities.Combine(AttributeType.GetHashCode(), OsPlatformName.GetHashCode(), Version.GetHashCode()); - public static bool operator ==(PlatformAttrbiuteInfo left, PlatformAttrbiuteInfo right) => left.Equals(right); + public static bool operator ==(PlatformAttributeInfo left, PlatformAttributeInfo right) => left.Equals(right); - public static bool operator !=(PlatformAttrbiuteInfo left, PlatformAttrbiuteInfo right) => !(left == right); + public static bool operator !=(PlatformAttributeInfo left, PlatformAttributeInfo right) => !(left == right); - public bool Equals(PlatformAttrbiuteInfo other) => + public bool Equals(PlatformAttributeInfo other) => AttributeType == other.AttributeType && IsOSPlatformsEqual(OsPlatformName, other.OsPlatformName) && Version.Equals(other.Version); - internal static bool TryParseTfmString(string osString, out PlatformAttrbiuteInfo parsedTfm) + internal static bool TryParseTfmString(string osString, out PlatformAttributeInfo parsedTfm) { - parsedTfm = new PlatformAttrbiuteInfo(); - parsedTfm.AttributeType = PlatformAttrbiteType.None; + parsedTfm = new PlatformAttributeInfo(); + parsedTfm.AttributeType = PlatformAttributeType.None; if (TryParsePlatformString(osString, out string platformName, out Version? version)) { diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 3c93c79ee8..ceaeb60328 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -21,7 +21,7 @@ namespace Microsoft.NetCore.Analyzers.InteropServices public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA1417"; - private static readonly ImmutableArray s_platformCheckMethods = ImmutableArray.Create("IsOSPlatformOrLater", "IsOSPlatformEarlierThan"); + private static readonly ImmutableArray s_platformCheckMethodNames = ImmutableArray.Create("IsOSPlatformOrLater", "IsOSPlatformEarlierThan"); private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute); private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableAddedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckAddedMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); @@ -90,14 +90,14 @@ public override void Initialize(AnalysisContext context) static ImmutableArray GetRuntimePlatformGuardMethods(INamedTypeSymbol runtimeInformationType) { return runtimeInformationType.GetMembers().OfType().Where(m => - s_platformCheckMethods.Contains(m.Name) && !m.Parameters.IsEmpty).ToImmutableArray(); + s_platformCheckMethodNames.Contains(m.Name) && !m.Parameters.IsEmpty).ToImmutableArray(); } } private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, ImmutableArray guardMethods, INamedTypeSymbol osPlatformType) { var parsedTfms = ParseTfm(context.Options, context.OwningSymbol, context.Compilation, context.CancellationToken); - var platformSpecificOperations = PooledDictionary>.GetInstance(); + var platformSpecificOperations = PooledDictionary>.GetInstance(); context.RegisterOperationAction(context => { @@ -155,9 +155,9 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, }); } - private static void ReportDiangosticsIfNotGuarded(IOperation operation, ImmutableArray attributes, GlobalFlowStateAnalysisValueSet value, OperationBlockAnalysisContext context) + private static void ReportDiangosticsIfNotGuarded(IOperation operation, ImmutableArray attributes, GlobalFlowStateAnalysisValueSet value, OperationBlockAnalysisContext context) { - PlatformAttrbiuteInfo attribute = attributes.FirstOrDefault(); + PlatformAttributeInfo attribute = attributes.FirstOrDefault(); if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Empty || value.Kind == GlobalFlowStateAnalysisValueSetKind.Unset) { ReportDiagnostics(operation, attribute, context); @@ -173,16 +173,16 @@ private static void ReportDiangosticsIfNotGuarded(IOperation operation, Immutabl { if (IsOSPlatformsEqual(attribute.OsPlatformName, info.PlatformPropertyName)) { - if (info.InvokedPlatformCheckMethodName.Equals(s_platformCheckMethods[0], StringComparison.InvariantCulture)) + if (info.InvokedPlatformCheckMethodName.Equals(s_platformCheckMethodNames[0], StringComparison.InvariantCulture)) { - if (attribute.AttributeType == PlatformAttrbiteType.MinimumOSPlatformAttribute && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) + if (attribute.AttributeType == PlatformAttributeType.MinimumOSPlatformAttribute && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) { guarded = true; } } else { - if ((attribute.AttributeType == PlatformAttrbiteType.ObsoletedInOSPlatformAttribute || attribute.AttributeType == PlatformAttrbiteType.RemovedInOSPlatformAttribute) + if ((attribute.AttributeType == PlatformAttributeType.ObsoletedInOSPlatformAttribute || attribute.AttributeType == PlatformAttributeType.RemovedInOSPlatformAttribute) && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) { guarded = true; @@ -199,7 +199,7 @@ private static void ReportDiangosticsIfNotGuarded(IOperation operation, Immutabl } } - private static void ReportDiagnosticsForAll(PooledDictionary> platformSpecificOperations, OperationBlockAnalysisContext context) + private static void ReportDiagnosticsForAll(PooledDictionary> platformSpecificOperations, OperationBlockAnalysisContext context) { foreach (var platformSpecificOperation in platformSpecificOperations) { @@ -207,7 +207,7 @@ private static void ReportDiagnosticsForAll(PooledDictionary? parsedTfms, ref PooledDictionary> platformSpecificOperations) + List? parsedTfms, ref PooledDictionary> platformSpecificOperations) { - using var builder = ArrayBuilder.GetInstance(); + using var builder = ArrayBuilder.GetInstance(); AttributeData[]? attributes = null; if (operation is IInvocationOperation iOperation) @@ -252,7 +252,7 @@ private static void AnalyzeInvocationOperation(IOperation operation, OperationAn { foreach (AttributeData attribute in attributes) { - if (PlatformAttrbiuteInfo.TryParseAttributeData(attribute, out PlatformAttrbiuteInfo parsedAttribute)) + if (PlatformAttributeInfo.TryParseAttributeData(attribute, out PlatformAttributeInfo parsedAttribute)) { if (!(IsSuppressedByTfm(parsedTfms, parsedAttribute) || IsSuppressedByAttribute(parsedAttribute, context.ContainingSymbol))) { @@ -277,11 +277,11 @@ pOperation.Parent is IBinaryOperation bo && bo.OperatorKind == BinaryOperatorKind.GreaterThanOrEqual || bo.OperatorKind == BinaryOperatorKind.LessThanOrEqual); - private static bool IsSuppressedByTfm(List? parsedTfms, PlatformAttrbiuteInfo parsedAttribute) + private static bool IsSuppressedByTfm(List? parsedTfms, PlatformAttributeInfo parsedAttribute) { if (parsedTfms != null) { - foreach (PlatformAttrbiuteInfo tfm in parsedTfms) + foreach (PlatformAttributeInfo tfm in parsedTfms) { if (IsOSPlatformsEqual(parsedAttribute.OsPlatformName, tfm.OsPlatformName)) { @@ -300,21 +300,21 @@ private static bool IsOSPlatformsEqual(string firstOs, string secondOs) || (secondOs.Equals("OSX", StringComparison.OrdinalIgnoreCase) && firstOs.Equals("MACOS", StringComparison.OrdinalIgnoreCase)); } - private static DiagnosticDescriptor SwitchRule(PlatformAttrbiteType attributeType) + private static DiagnosticDescriptor SwitchRule(PlatformAttributeType attributeType) { - if (attributeType == PlatformAttrbiteType.MinimumOSPlatformAttribute) + if (attributeType == PlatformAttributeType.MinimumOSPlatformAttribute) return MinimumOsRule; - if (attributeType == PlatformAttrbiteType.ObsoletedInOSPlatformAttribute) + if (attributeType == PlatformAttributeType.ObsoletedInOSPlatformAttribute) return ObsoleteRule; return RemovedRule; } - private static List? ParseTfm(AnalyzerOptions options, ISymbol containingSymbol, Compilation compilation, CancellationToken cancellationToken) + private static List? ParseTfm(AnalyzerOptions options, ISymbol containingSymbol, Compilation compilation, CancellationToken cancellationToken) { string? tfmString = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetFramework, MinimumOsRule, containingSymbol, compilation, cancellationToken); if (tfmString != null) { - using var builder = ArrayBuilder.GetInstance(); + using var builder = ArrayBuilder.GetInstance(); var tfms = tfmString.Split(SeparatorSemicolon); @@ -332,7 +332,7 @@ private static DiagnosticDescriptor SwitchRule(PlatformAttrbiteType attributeTyp else { Debug.Assert(tokens.Length == 2); - if (PlatformAttrbiuteInfo.TryParseTfmString(tokens[1], out PlatformAttrbiuteInfo parsedTfm)) + if (PlatformAttributeInfo.TryParseTfmString(tokens[1], out PlatformAttributeInfo parsedTfm)) { var tpmv = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetPlatformMinVersion, MinimumOsRule, containingSymbol, compilation, cancellationToken); if (tpmv != null) @@ -351,9 +351,9 @@ private static DiagnosticDescriptor SwitchRule(PlatformAttrbiteType attributeTyp return null; } - private static PlatformAttrbiuteInfo CreateWindowsOnlyPlatformInfo() + private static PlatformAttributeInfo CreateWindowsOnlyPlatformInfo() { - var platformInfo = new PlatformAttrbiuteInfo(); + var platformInfo = new PlatformAttributeInfo(); platformInfo.Version = new Version(7, 0); platformInfo.OsPlatformName = Windows; return platformInfo; @@ -385,7 +385,7 @@ private static AttributeData[] FindAllPlatformAttributesApplied(ImmutableArray; namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests { @@ -31,7 +28,7 @@ public void M2() } } " + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -61,7 +58,7 @@ public void M2() } } " + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -91,7 +88,7 @@ void M2() } }" + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -128,7 +125,7 @@ void M3 () } }" + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -176,7 +173,7 @@ void M3 () } }" + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -217,7 +214,7 @@ public enum PlatformEnum NoPlatform } " + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -261,7 +258,7 @@ void M2() } }" + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -287,7 +284,7 @@ public void M2() } } " + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -315,7 +312,7 @@ public void M2() } } " + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -340,7 +337,7 @@ public void M2() } } " + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -385,7 +382,7 @@ public void M2() } } " + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -427,7 +424,7 @@ public void M2() } } " + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -467,7 +464,7 @@ public void M2() } } " + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -519,7 +516,7 @@ public void M2() } } " + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -567,7 +564,7 @@ public void M2() } } " + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -608,7 +605,7 @@ void M2() } }" + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -655,7 +652,7 @@ void M2() } }" + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -683,7 +680,7 @@ void M2() } }" + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -720,7 +717,7 @@ void M2() } }" + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -747,7 +744,7 @@ void M2() } }" + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -774,7 +771,7 @@ void M2() } }" + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -822,7 +819,7 @@ void M2() } }" + MockAttributesSource + MockRuntimeApiSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } /*[Fact] //TODO: Not working anymore, fix this diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index d478b8b6a2..cede6d5012 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -1,5 +1,6 @@ // 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 Microsoft.CodeAnalysis.Testing; using Xunit; @@ -39,7 +40,7 @@ public void Removed() } } " + MockAttributesSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -90,7 +91,7 @@ public void RemovedWithEmptyString() } } " + MockAttributesSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -126,13 +127,11 @@ public int M3(int option) } } " + MockAttributesSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Theory] - [InlineData("MinimumOSPlatform", "string StringProperty", " == StringProperty", @"StringProperty|] = ""Hello""", "StringProperty")] - [InlineData("ObsoletedInOSPlatform", "int IntProperty", " > IntProperty", "IntProperty|] = 5", "IntProperty")] - [InlineData("RemovedInOSPlatform", "int RemovedProperty", " <= RemovedProperty", "RemovedProperty|] = 3", "RemovedProperty")] + [MemberData(nameof(Create_AtrrbiuteProperty_WithCondtions))] public async Task OsDependentPropertyConditionalCheckNotWarns(string attribute, string property, string condition, string setter, string getter) { var source = @" @@ -156,7 +155,14 @@ public object M2(object option) } } " + MockAttributesSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); + } + + public static IEnumerable Create_AtrrbiuteProperty_WithCondtions() + { + yield return new object[] { "MinimumOSPlatform", "string StringProperty", " == StringProperty", @"StringProperty|] = ""Hello""", "StringProperty" }; + yield return new object[] { "ObsoletedInOSPlatform", "int IntProperty", " > IntProperty", "IntProperty|] = 5", "IntProperty" }; + yield return new object[] { "RemovedInOSPlatform", "int RemovedProperty", " <= RemovedProperty", "RemovedProperty|] = 3", "RemovedProperty" }; } [Fact] @@ -189,7 +195,7 @@ public enum PlatformEnum NoPlatform } " + MockAttributesSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -224,7 +230,7 @@ public enum PlatformEnum NoPlatform } " + MockAttributesSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -258,7 +264,7 @@ public int M3(int option) } } " + MockAttributesSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } /*[Fact] TODO: test with assembly level attribute @@ -304,7 +310,7 @@ public void M2() } } " + MockAttributesSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -335,7 +341,7 @@ public void M2() } " + MockAttributesSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -360,7 +366,7 @@ public void M2() } } " + MockAttributesSource; - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } /*[Fact] TODO wait until assembly level APIs merged @@ -392,21 +398,27 @@ await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer2.Rule).WithSpan(10, 21, 10, 29).WithArguments("M2", "Windows", "10.1.2.3")); }*/ + public static IEnumerable MinimumOsAttributeTestData() + { + yield return new object[] { "Windows10.1.2.3", "Windows10.1.2.3", false }; + yield return new object[] { "Windows10.1.2.3", "Windows10.1.3.3", false }; + yield return new object[] { "WINDOWS10.1.2.3", "Windows10.1.3.1", false }; + yield return new object[] { "Windows10.1.2.3", "Windows11.1.2.3", false }; + yield return new object[] { "Windows10.1.2.3", "windows10.2.2.0", false }; + yield return new object[] { "Windows10.1.2.3", "Windows10.1.1.3", true }; + yield return new object[] { "Windows10.1.2.3", "WINDOWS11.1.1.3", false }; + yield return new object[] { "Windows10.1.2.3", "Windows10.1.1.4", true }; + yield return new object[] { "osx10.1.2.3", "macos10.2.2.0", false }; + yield return new object[] { "macOs10.1.2.3", "Osx11.1.1.0", false }; + yield return new object[] { "MACOS10.1.2.3", "osx10.2.2.0", false }; + yield return new object[] { "Windows10.1.2.3", "Osx11.1.1.4", true }; + yield return new object[] { "Windows10.1.2.3", "Windows10.0.1.9", true }; + yield return new object[] { "Windows10.1.2.3", "Windows10.1.1.4", true }; + yield return new object[] { "Windows10.1.2.3", "Windows8.2.3.3", true }; + } + [Theory] - [InlineData("Windows10.1.2.3", "Windows10.1.2.3", false)] - [InlineData("windows10.1.2.3", "Windows10.1.2.3", false)] - [InlineData("WINDOWS10.1.2.3", "Windows10.1.3.3", false)] - [InlineData("Windows10.1.2.3", "WindowS10.1.3.1", false)] - [InlineData("Windows10.1.2.3", "Windows11.1.2.3", false)] - [InlineData("Windows10.1.2.3", "windows11.1.2.3", false)] - [InlineData("Windows10.1.2.3", "Windows10.2.2.0", false)] - [InlineData("osx10.1.2.3", "macos10.2.2.0", false)] - [InlineData("OSX10.1.2.3", "osx10.2.2.0", false)] - [InlineData("Windows10.1.2.3", "osx10.1.1.3", true)] - [InlineData("Windows10.1.2.3", "Windows10.1.1.3", true)] - [InlineData("Windows10.1.2.3", "Windows10.1.1.4", true)] - [InlineData("Windows10.1.2.3", "Windows10.0.1.9", true)] - [InlineData("Windows10.1.2.3", "Windows8.2.3.3", true)] + [MemberData(nameof(MinimumOsAttributeTestData))] public async Task MethodOfOsDependentClassSuppressedWithMinimumOsAttribute(string dependentVersion, string suppressingVersion, bool warn) { var source = @" @@ -432,24 +444,13 @@ public void M2() " + MockAttributesSource; if (warn) - await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); else - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Theory] - [InlineData("Windows10.1.2.3", "Windows10.1.2.3", true)] - [InlineData("Windows10.1.2.3", "Windows10.1.3.3", true)] - [InlineData("Windows10.1.2.3", "Windows10.1.3.1", true)] - [InlineData("Windows10.1.2.3", "Windows11.1.2.3", true)] - [InlineData("Windows10.1.2.3", "Windows10.2.2.0", true)] - [InlineData("Windows10.1.2.3", "Windows10.1.1.3", false)] - [InlineData("Windows10.1.2.3", "WINDOWS10.1.1.3", false)] - [InlineData("Windows10.1.2.3", "Windows10.1.1.4", false)] - [InlineData("osx10.1.2.3", "MacOs10.1.1.4", false)] - [InlineData("macOs10.1.2.3", "Osx10.1.1.4", false)] - [InlineData("windows10.1.2.3", "Windows10.1.0.1", false)] - [InlineData("Windows10.1.2.3", "Windows8.2.3.4", false)] + [MemberData(nameof(ObsoletedRemovedAttributeTestData))] public async Task MethodOfOsDependentClassSuppressedWithObsoleteAttribute(string dependentVersion, string suppressingVersion, bool warn) { var source = @" @@ -475,25 +476,30 @@ public void M2() " + MockAttributesSource; if (warn) - await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); else - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); + } + + public static IEnumerable ObsoletedRemovedAttributeTestData() + { + yield return new object[] { "Windows10.1.2.3", "Windows10.1.2.3", true }; + yield return new object[] { "Windows10.1.2.3", "Windows10.1.3.3", true }; + yield return new object[] { "Windows10.1.2.3", "Windows10.1.3.1", true }; + yield return new object[] { "Windows10.1.2.3", "Windows11.1.2.3", true }; + yield return new object[] { "Windows10.1.2.3", "Windows10.2.2.0", true }; + yield return new object[] { "Windows10.1.2.3", "Windows10.1.1.3", false }; + yield return new object[] { "Windows10.1.2.3", "WINDOWS10.1.1.3", false }; + yield return new object[] { "Windows10.1.2.3", "Windows10.1.1.4", false }; + yield return new object[] { "osx10.1.2.3", "MacOs10.1.1.4", false }; + yield return new object[] { "macOs10.1.2.3", "Osx10.1.1.4", false }; + yield return new object[] { "Windows10.1.2.3", "Osx10.1.1.4", true }; + yield return new object[] { "windows10.1.2.3", "Windows10.1.0.1", false }; + yield return new object[] { "Windows10.1.2.3", "Windows8.2.3.4", false }; } [Theory] - [InlineData("Windows10.1.2.3", "Windows10.1.2.3", true)] - [InlineData("Windows10.1.2.3", "Windows10.1.3.3", true)] - [InlineData("Windows10.1.2.3", "Windows10.1.3.1", true)] - [InlineData("Windows10.1.2.3", "Windows11.1.2.3", true)] - [InlineData("Windows10.1.2.3", "Windows10.2.2.0", true)] - [InlineData("Windows10.1.2.3", "Windows10.1.1.3", false)] - [InlineData("Windows10.1.2.3", "Windows10.1.1.4", false)] - [InlineData("Macos10.1.2.3", "macos10.1.1.4", false)] - [InlineData("OSX10.1.2.3", "Macos10.1.1.4", false)] - [InlineData("Windows10.1.2.3", "Macos10.1.1.4", true)] - [InlineData("Windows10.1.2.3", "WINDOWS10.1.1.4", false)] - [InlineData("Windows10.1.2.3", "Windows10.1.0.1", false)] - [InlineData("windows10.1.2.3", "Windows8.2.3.4", false)] + [MemberData(nameof(ObsoletedRemovedAttributeTestData))] public async Task MethodOfOsDependentClassSuppressedWithRemovedAttribute(string dependentVersion, string suppressingVersion, bool warn) { var source = @" @@ -519,35 +525,17 @@ public void M2() " + MockAttributesSource; if (warn) - await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RemovedRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RemovedRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); else - await VerifyCS.VerifyAnalyzerAsync(source); + await VerifyAnalyzerAsyncCs(source); } [Theory] - [InlineData("", true)] - [InlineData("build_property.TargetFramework=net5.0", true)] - [InlineData("build_property.TargetFramework=net472", true)] - [InlineData("build_property.TargetFramework=net5.0-linux", true)] - [InlineData("build_property.TargetFramework=net5.0-windows", true)] - [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.1", false)] - [InlineData("build_property.TargetFramework=net5.0-windows10.2", false)] - [InlineData("build_property.TargetFramework=net5.0-windows11.0", false)] - [InlineData("build_property.TargetFramework=net5.0-windows10.0", true)] - [InlineData("build_property.TargetFramework=net5.0-windows11.0\nbuild_property.TargetPlatformMinVersion=10.0;", true)] - [InlineData("build_property.TargetFramework=net5.0-windows10.2\nbuild_property.TargetPlatformMinVersion=10.1;", true)] - [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.2\nbuild_property.TargetPlatformMinVersion=10.0.0.1;", true)] - [InlineData("build_property.TargetFramework=net5.0-windows10.2.1\nbuild_property.TargetPlatformMinVersion=9.1.1.1;", true)] + [MemberData(nameof(MinimumOsAttributeTfmTestData))] public async Task TfmAndTargetPlatformMinVersionWithMinimumOsAttribute(string editorConfigText, bool expectDiagnostic) { var invocation = expectDiagnostic ? @"[|M2()|]" : "M2()"; - await new VerifyCS.Test - { - TestState = - { - Sources = - { - $@" + var source =$@" using System.Runtime.Versioning; public class Test @@ -561,38 +549,50 @@ public void M2() {{ }} }} -" + MockAttributesSource - }, - AdditionalFiles = { (".editorconfig", editorConfigText) } - }, - MarkupOptions = MarkupOptions.UseFirstDescriptor - }.RunAsync(); +" + MockAttributesSource; + await VerifyAnalyzerAsyncCs(source, editorConfigText); + } + + public static IEnumerable MinimumOsAttributeTfmTestData() + { + yield return new object[] { "", true }; + yield return new object[] { "build_property.TargetFramework=net5.0", true }; + yield return new object[] { "build_property.TargetFramework=net472", true }; + yield return new object[] { "build_property.TargetFramework=net5.0-linux", true }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows", true }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows10.1.1.1", false }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows10.2", false }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows11.0", false }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows10.0", true }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows11.0\nbuild_property.TargetPlatformMinVersion=10.0;", true }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows10.2\nbuild_property.TargetPlatformMinVersion=10.1;", true }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows10.1.1.2\nbuild_property.TargetPlatformMinVersion=10.0.0.1;", true }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows10.2.1\nbuild_property.TargetPlatformMinVersion=9.1.1.1;", true }; + } + + public static IEnumerable ObsoletedRemovedTfmTestData() + { + yield return new object[] { "", true }; + yield return new object[] { "build_property.TargetFramework=net5.0", true }; + yield return new object[] { "build_property.TargetFramework=net472", false }; // because no version part it is 0.0.0.0 + yield return new object[] { "build_property.TargetFramework=net5.0-linux", true }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows", true }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows10.1.1.1", true }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows10.2", true }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows11.0", true }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows10.0", false }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows11.0\nbuild_property.TargetPlatformMinVersion=10.0;", false }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows10.2\nbuild_property.TargetPlatformMinVersion=10.1;", false }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows10.1.1.2\nbuild_property.TargetPlatformMinVersion=10.0.0.1;", false }; + yield return new object[] { "build_property.TargetFramework=net5.0-windows10.2.1\nbuild_property.TargetPlatformMinVersion=9.1.1.1;", false }; } [Theory] - [InlineData("", true)] - [InlineData("build_property.TargetFramework=net5.0", true)] - [InlineData("build_property.TargetFramework=net472", false)] // TODO: because no version part it is 0.0.0.0 - [InlineData("build_property.TargetFramework=net5.0-linux", true)] - [InlineData("build_property.TargetFramework=net5.0-windows", true)] - [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.1", true)] - [InlineData("build_property.TargetFramework=net5.0-windows10.2", true)] - [InlineData("build_property.TargetFramework=net5.0-windows11.0", true)] - [InlineData("build_property.TargetFramework=net5.0-windows10.0", false)] - [InlineData("build_property.TargetFramework=net5.0-windows11.0\nbuild_property.TargetPlatformMinVersion=10.0;", false)] - [InlineData("build_property.TargetFramework=net5.0-windows10.2\nbuild_property.TargetPlatformMinVersion=10.1;", false)] - [InlineData("build_property.TargetFramework=net5.0-windows10.1.1.2\nbuild_property.TargetPlatformMinVersion=10.0.0.1;", false)] - [InlineData("build_property.TargetFramework=net5.0-windows10.2.1\nbuild_property.TargetPlatformMinVersion=9.1.1.1;", false)] + [MemberData(nameof(ObsoletedRemovedTfmTestData))] public async Task TfmAndTargetPlatformMinVersionWithObsoleteAttribute(string editorConfigText, bool expectDiagnostic) { var invocation = expectDiagnostic ? @"[|M2()|]" : "M2()"; - await new VerifyCS.Test - { - TestState = - { - Sources = - { -$@" + var source =$@" using System.Runtime.Versioning; public class Test @@ -606,12 +606,36 @@ public void M2() {{ }} }} -" + MockAttributesSource - }, - AdditionalFiles = { (".editorconfig", editorConfigText) } - }, - MarkupOptions = MarkupOptions.UseFirstDescriptor - }.RunAsync(); +" + MockAttributesSource; + + await VerifyAnalyzerAsyncCs(source, editorConfigText); + } + + private static VerifyCS.Test PopulateTest(string sourceCode) + { + return new VerifyCS.Test + { + TestCode = sourceCode, + ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp50, + MarkupOptions = MarkupOptions.UseFirstDescriptor, + TestState = { } + }; + } + + private static async Task VerifyAnalyzerAsyncCs(string sourceCode) => await PopulateTest(sourceCode).RunAsync(); + + private static async Task VerifyAnalyzerAsyncCs(string sourceCode, DiagnosticResult expectedDiagnostic) + { + var test = PopulateTest(sourceCode); + test.ExpectedDiagnostics.Add(expectedDiagnostic); + await test.RunAsync(); + } + + private static async Task VerifyAnalyzerAsyncCs(string sourceCode, string additionalFiles) + { + var test = PopulateTest(sourceCode); + test.TestState.AdditionalFiles.Add((".editorconfig", additionalFiles)); + await test.RunAsync(); } private readonly string MockAttributesSource = @" diff --git a/src/Test.Utilities/CSharpCodeFixVerifier`2.cs b/src/Test.Utilities/CSharpCodeFixVerifier`2.cs index 345bc3bacf..466038c5cb 100644 --- a/src/Test.Utilities/CSharpCodeFixVerifier`2.cs +++ b/src/Test.Utilities/CSharpCodeFixVerifier`2.cs @@ -28,7 +28,6 @@ public static async Task VerifyAnalyzerAsync(string source, params DiagnosticRes var test = new Test { TestCode = source, - MarkupOptions = MarkupOptions.UseFirstDescriptor }; test.ExpectedDiagnostics.AddRange(expected); From 84a2fd63bfdf052d867d8830d423962d2b10b955 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 21 Jul 2020 16:37:51 -0700 Subject: [PATCH 11/48] Local function, lambda, even, delegate support and tests --- .../Core/AnalyzerReleases.Unshipped.md | 3 +- .../PlatformCompatabilityAnalyzer.Data.cs | 69 +- ...mCompatabilityAnalyzer.OperationVisitor.cs | 16 +- .../PlatformCompatabilityAnalyzer.cs | 219 +++-- ...ePlatformCheckAnalyzer.OperationVisitor.cs | 48 - ...formCheckAnalyzer.RuntimeOSPlatformInfo.cs | 189 ---- .../RuntimePlatformCheckAnalyzer.cs | 170 ---- ...CompatabilityAnalyzerTests.GuardedCalls.cs | 566 ++++++++++-- .../PlatformCompatabilityAnalyzerTests.cs | 551 +++++++++--- .../RuntimePlatformCheckAnalyzerTests.cs | 820 ------------------ .../Extensions/IOperationExtensions.cs | 21 + .../DataFlow/DataFlowAnalysisResult.cs | 13 + 12 files changed, 1136 insertions(+), 1549 deletions(-) delete mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.OperationVisitor.cs delete mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.RuntimeOSPlatformInfo.cs delete mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzer.cs delete mode 100644 src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzerTests.cs diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index 9e6f33e3b6..16c42edc43 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -9,9 +9,8 @@ CA1046 | Design | Disabled | DoNotOverloadOperatorEqualsOnReferenceTypes, [Docum CA1047 | Design | Info | DoNotDeclareProtectedMembersInSealedTypes, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1047) CA1069 | Design | Info | EnumShouldNotHaveDuplicatedValues, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1069) CA1070 | Design | Info | DoNotDeclareEventFieldsAsVirtual, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1070) -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) -CA1418 | Interoperability | Warning | PlatformCompatabilityAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1418) CA1700 | Naming | Disabled | DoNotNameEnumValuesReserved, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1700) CA1713 | Naming | Disabled | EventsShouldNotHaveBeforeOrAfterPrefix, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1713) CA1805 | Performance | Info | DoNotInitializeUnnecessarilyAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1805) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index b55e6c93db..e60863bc9e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -17,7 +17,7 @@ namespace Microsoft.NetCore.Analyzers.InteropServices { using ValueContentAnalysisResult = DataFlowAnalysisResult; - public partial class PlatformCompatabilityAnalyzer + public sealed partial class PlatformCompatabilityAnalyzer { private enum PlatformAttributeType { @@ -27,29 +27,35 @@ private enum PlatformAttributeType private struct PlatformAttributeInfo : IEquatable { public PlatformAttributeType AttributeType { get; set; } - public string OsPlatformName { get; set; } + public string PlatformName { get; set; } public Version Version { get; set; } - public static bool TryParseAttributeData(AttributeData osAttribute, out PlatformAttributeInfo parsedAttribute) + public static bool TryParsePlatformAttributeInfo(AttributeData osAttribute, out PlatformAttributeInfo parsedAttribute) { parsedAttribute = new PlatformAttributeInfo(); switch (osAttribute.AttributeClass.Name) { case MinimumOSPlatformAttribute: - parsedAttribute.AttributeType = PlatformAttributeType.MinimumOSPlatformAttribute; break; + parsedAttribute.AttributeType = PlatformAttributeType.MinimumOSPlatformAttribute; + break; case ObsoletedInOSPlatformAttribute: - parsedAttribute.AttributeType = PlatformAttributeType.ObsoletedInOSPlatformAttribute; break; + parsedAttribute.AttributeType = PlatformAttributeType.ObsoletedInOSPlatformAttribute; + break; case RemovedInOSPlatformAttribute: - parsedAttribute.AttributeType = PlatformAttributeType.RemovedInOSPlatformAttribute; break; + parsedAttribute.AttributeType = PlatformAttributeType.RemovedInOSPlatformAttribute; + break; case TargetPlatformAttribute: - parsedAttribute.AttributeType = PlatformAttributeType.TargetPlatformAttribute; break; + parsedAttribute.AttributeType = PlatformAttributeType.TargetPlatformAttribute; + break; default: - parsedAttribute.AttributeType = PlatformAttributeType.None; break; + parsedAttribute.AttributeType = PlatformAttributeType.None; + break; } - if (!osAttribute.ConstructorArguments[0].IsNull && TryParsePlatformString(osAttribute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version)) + if (osAttribute.ConstructorArguments.Length == 1 && osAttribute.ConstructorArguments[0].Type.SpecialType == SpecialType.System_String && + !osAttribute.ConstructorArguments[0].IsNull && TryParsePlatformNameAndVersion(osAttribute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version)) { - parsedAttribute.OsPlatformName = platformName; + parsedAttribute.PlatformName = platformName; parsedAttribute.Version = version; return true; } @@ -66,30 +72,17 @@ public override bool Equals(object obj) return false; } - public override int GetHashCode() => HashUtilities.Combine(AttributeType.GetHashCode(), OsPlatformName.GetHashCode(), Version.GetHashCode()); + public override int GetHashCode() => HashUtilities.Combine(AttributeType.GetHashCode(), PlatformName.GetHashCode(), Version.GetHashCode()); public static bool operator ==(PlatformAttributeInfo left, PlatformAttributeInfo right) => left.Equals(right); public static bool operator !=(PlatformAttributeInfo left, PlatformAttributeInfo right) => !(left == right); public bool Equals(PlatformAttributeInfo other) => - AttributeType == other.AttributeType && IsOSPlatformsEqual(OsPlatformName, other.OsPlatformName) && Version.Equals(other.Version); - - internal static bool TryParseTfmString(string osString, out PlatformAttributeInfo parsedTfm) - { - parsedTfm = new PlatformAttributeInfo(); - parsedTfm.AttributeType = PlatformAttributeType.None; - - if (TryParsePlatformString(osString, out string platformName, out Version? version)) - { - parsedTfm.OsPlatformName = platformName; - parsedTfm.Version = version; - } - return parsedTfm.Version != null; - } + AttributeType == other.AttributeType && IsOSPlatformsEqual(PlatformName, other.PlatformName) && Version.Equals(other.Version); } - private static bool TryParsePlatformString(string osString, out string osPlatformName, [NotNullWhen(true)] out Version? version) + private static bool TryParsePlatformNameAndVersion(string osString, out string osPlatformName, [NotNullWhen(true)] out Version? version) { version = null; osPlatformName = string.Empty; @@ -113,9 +106,9 @@ private static bool TryParsePlatformString(string osString, out string osPlatfor return false; } - private struct RuntimeMethodInfo : IAbstractAnalysisValue, IEquatable + private struct RuntimeMethodValue : IAbstractAnalysisValue, IEquatable { - private RuntimeMethodInfo(string invokedPlatformCheckMethodName, string platformPropertyName, Version version, bool negated) + private RuntimeMethodValue(string invokedPlatformCheckMethodName, string platformPropertyName, Version version, bool negated) { InvokedPlatformCheckMethodName = invokedPlatformCheckMethodName ?? throw new ArgumentNullException(nameof(invokedPlatformCheckMethodName)); PlatformPropertyName = platformPropertyName ?? throw new ArgumentNullException(nameof(platformPropertyName)); @@ -129,22 +122,22 @@ private RuntimeMethodInfo(string invokedPlatformCheckMethodName, string platform public bool Negated { get; } public IAbstractAnalysisValue GetNegatedValue() - => new RuntimeMethodInfo(InvokedPlatformCheckMethodName, PlatformPropertyName, Version, !Negated); + => new RuntimeMethodValue(InvokedPlatformCheckMethodName, PlatformPropertyName, Version, !Negated); public static bool TryDecode( IMethodSymbol invokedPlatformCheckMethod, ImmutableArray arguments, ValueContentAnalysisResult? valueContentAnalysisResult, INamedTypeSymbol osPlatformType, - [NotNullWhen(returnValue: true)] out RuntimeMethodInfo? info) + [NotNullWhen(returnValue: true)] out RuntimeMethodValue? info) { Debug.Assert(!arguments.IsEmpty); if (arguments[0].Value is ILiteralOperation literal && literal.Type.SpecialType == SpecialType.System_String) { - if (literal.ConstantValue.HasValue && TryParsePlatformString(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) + if (literal.ConstantValue.HasValue && TryParsePlatformNameAndVersion(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) { - info = new RuntimeMethodInfo(invokedPlatformCheckMethod.Name, platformName, version, negated: false); + info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); return true; } } @@ -157,7 +150,7 @@ public static bool TryDecode( return false; } - info = new RuntimeMethodInfo(invokedPlatformCheckMethod.Name, osPlatformName, osVersion, negated: false); + info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, osPlatformName, osVersion, negated: false); return true; } @@ -243,27 +236,27 @@ public override string ToString() return result; } - public bool Equals(RuntimeMethodInfo other) + public bool Equals(RuntimeMethodValue 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 RuntimeMethodInfo otherInfo && Equals(otherInfo); + => obj is RuntimeMethodValue otherInfo && Equals(otherInfo); public override int GetHashCode() => HashUtilities.Combine(InvokedPlatformCheckMethodName.GetHashCode(), PlatformPropertyName.GetHashCode(), Version.GetHashCode(), Negated.GetHashCode()); bool IEquatable.Equals(IAbstractAnalysisValue other) - => other is RuntimeMethodInfo otherInfo && Equals(otherInfo); + => other is RuntimeMethodValue otherInfo && Equals(otherInfo); - public static bool operator ==(RuntimeMethodInfo left, RuntimeMethodInfo right) + public static bool operator ==(RuntimeMethodValue left, RuntimeMethodValue right) { return left.Equals(right); } - public static bool operator !=(RuntimeMethodInfo left, RuntimeMethodInfo right) + public static bool operator !=(RuntimeMethodValue left, RuntimeMethodValue right) { return !(left == right); } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs index 7c0929d041..79546e6e2a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs @@ -7,7 +7,7 @@ namespace Microsoft.NetCore.Analyzers.InteropServices { - public partial class PlatformCompatabilityAnalyzer + public sealed partial class PlatformCompatabilityAnalyzer { private sealed class OperationVisitor : GlobalFlowStateDataFlowOperationVisitor { @@ -36,7 +36,7 @@ public override GlobalFlowStateAnalysisValueSet VisitInvocation_NonLambdaOrDeleg if (_platformCheckMethods.Contains(method.OriginalDefinition) && !visitedArguments.IsEmpty) { - return RuntimeMethodInfo.TryDecode(method, visitedArguments, DataFlowAnalysisContext.ValueContentAnalysisResultOpt, _osPlatformType, out var platformInfo) ? + return RuntimeMethodValue.TryDecode(method, visitedArguments, DataFlowAnalysisContext.ValueContentAnalysisResultOpt, _osPlatformType, out var platformInfo) ? new GlobalFlowStateAnalysisValueSet(platformInfo) : GlobalFlowStateAnalysisValueSet.Unknown; } @@ -55,6 +55,18 @@ public override GlobalFlowStateAnalysisValueSet VisitFieldReference(IFieldRefere var value = base.VisitFieldReference(operation, argument); return GetValueOrDefault(value); } + + public override GlobalFlowStateAnalysisValueSet VisitObjectCreation(IObjectCreationOperation operation, object? argument) + { + var value = base.VisitObjectCreation(operation, argument); + return GetValueOrDefault(value); + } + + public override GlobalFlowStateAnalysisValueSet VisitEventReference(IEventReferenceOperation operation, object? argument) + { + var value = base.VisitEventReference(operation, argument); + return GetValueOrDefault(value); + } } } } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index ceaeb60328..fa935d7369 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -1,12 +1,9 @@ // 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.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using System.Text.RegularExpressions; -using System.Threading; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Analyzer.Utilities.PooledObjects; @@ -17,25 +14,26 @@ namespace Microsoft.NetCore.Analyzers.InteropServices { - [DiagnosticAnalyzer(LanguageNames.CSharp)] + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer { - internal const string RuleId = "CA1417"; + internal const string RuleId = "CA1416"; private static readonly ImmutableArray s_platformCheckMethodNames = ImmutableArray.Create("IsOSPlatformOrLater", "IsOSPlatformEarlierThan"); private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute); + private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableAddedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckAddedMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableObsoleteMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckObsoleteMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableRemovedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckRemovedMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckDescription), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); - private const char SeparatorDash = '-'; - private const char SeparatorSemicolon = ';'; + private const string MinimumOSPlatformAttribute = nameof(MinimumOSPlatformAttribute); private const string ObsoletedInOSPlatformAttribute = nameof(ObsoletedInOSPlatformAttribute); private const string RemovedInOSPlatformAttribute = nameof(RemovedInOSPlatformAttribute); private const string TargetPlatformAttribute = nameof(TargetPlatformAttribute); private const string Windows = nameof(Windows); - private static readonly Regex s_neutralTfmRegex = new Regex(@"^net([5-9]|standard\d|coreapp\d)\.\d$", RegexOptions.IgnoreCase); + private const string OSX = nameof(OSX); + private const string macOS = nameof(macOS); internal static DiagnosticDescriptor MinimumOsRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, @@ -96,14 +94,14 @@ static ImmutableArray GetRuntimePlatformGuardMethods(INamedTypeSy private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, ImmutableArray guardMethods, INamedTypeSymbol osPlatformType) { - var parsedTfms = ParseTfm(context.Options, context.OwningSymbol, context.Compilation, context.CancellationToken); var platformSpecificOperations = PooledDictionary>.GetInstance(); context.RegisterOperationAction(context => { - AnalyzeInvocationOperation(context.Operation, context, parsedTfms, ref platformSpecificOperations); + AnalyzeInvocationOperation(context.Operation, context, ref platformSpecificOperations); } - , OperationKind.Invocation, OperationKind.PropertyReference, OperationKind.FieldReference); + , OperationKind.DelegateCreation, OperationKind.EventReference, OperationKind.FieldReference, + OperationKind.Invocation, OperationKind.ObjectCreation, OperationKind.PropertyReference); context.RegisterOperationBlockEndAction(context => { @@ -135,13 +133,35 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, foreach (var platformSpecificOperation in platformSpecificOperations) { var value = analysisResult[platformSpecificOperation.Key.Kind, platformSpecificOperation.Key.Syntax]; + PlatformAttributeInfo attribute = platformSpecificOperation.Value.FirstOrDefault(); if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown) { - continue; + if (platformSpecificOperation.Key.TryGetContainingLocalOrLambdaFunctionSymbol(out var containingSymbol)) + { + var localResult = analysisResult.TryGetInterproceduralResultByDefinition(containingSymbol); + if (localResult != null) + { + var localValue = localResult[platformSpecificOperation.Key.Kind, platformSpecificOperation.Key.Syntax]; + if (localValue.Kind == GlobalFlowStateAnalysisValueSetKind.Known) + { + if (IsKnownValueGuarded(attribute, localValue)) + { + continue; + } + } + } + } + } + else if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Known) + { + if (IsKnownValueGuarded(attribute, value)) + { + continue; + } } - ReportDiangosticsIfNotGuarded(platformSpecificOperation.Key, platformSpecificOperation.Value, value, context); + ReportDiagnostics(platformSpecificOperation.Key, attribute, context); } } finally @@ -155,48 +175,38 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, }); } - private static void ReportDiangosticsIfNotGuarded(IOperation operation, ImmutableArray attributes, GlobalFlowStateAnalysisValueSet value, OperationBlockAnalysisContext context) + private static bool IsKnownValueGuarded(PlatformAttributeInfo attribute, GlobalFlowStateAnalysisValueSet value) { - PlatformAttributeInfo attribute = attributes.FirstOrDefault(); - if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Empty || value.Kind == GlobalFlowStateAnalysisValueSetKind.Unset) - { - ReportDiagnostics(operation, attribute, context); - } - else + foreach (var analysisValue in value.AnalysisValues) { - bool guarded = false; - foreach (var analysisValue in value.AnalysisValues) + if (analysisValue is RuntimeMethodValue info) { - if (analysisValue is RuntimeMethodInfo info) + if (!info.Negated) { - if (!info.Negated) + if (IsOSPlatformsEqual(attribute.PlatformName, info.PlatformPropertyName)) { - if (IsOSPlatformsEqual(attribute.OsPlatformName, info.PlatformPropertyName)) + if (info.InvokedPlatformCheckMethodName == s_platformCheckMethodNames[0]) { - if (info.InvokedPlatformCheckMethodName.Equals(s_platformCheckMethodNames[0], StringComparison.InvariantCulture)) + if (attribute.AttributeType == PlatformAttributeType.MinimumOSPlatformAttribute && + AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) { - if (attribute.AttributeType == PlatformAttributeType.MinimumOSPlatformAttribute && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) - { - guarded = true; - } + return true; } - else + } + else + { + if ((attribute.AttributeType == PlatformAttributeType.ObsoletedInOSPlatformAttribute || + attribute.AttributeType == PlatformAttributeType.RemovedInOSPlatformAttribute) && + AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) { - if ((attribute.AttributeType == PlatformAttributeType.ObsoletedInOSPlatformAttribute || attribute.AttributeType == PlatformAttributeType.RemovedInOSPlatformAttribute) - && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) - { - guarded = true; - } + return true; } } } } } - if (!guarded) - { - ReportDiagnostics(operation, attribute, context); - } } + return false; } private static void ReportDiagnosticsForAll(PooledDictionary> platformSpecificOperations, OperationBlockAnalysisContext context) @@ -215,6 +225,10 @@ private static void ReportDiagnostics(IOperation operation, PlatformAttributeInf { operationName = iOperation.TargetMethod.Name; } + else if (operation is IObjectCreationOperation cOperation) + { + operationName = cOperation.Constructor.Name; + } else if (operation is IPropertyReferenceOperation pOperation) { operationName = pOperation.Property.Name; @@ -223,12 +237,22 @@ private static void ReportDiagnostics(IOperation operation, PlatformAttributeInf { operationName = fOperation.Field.Name; } + if (operation is IDelegateCreationOperation dOperation) + { + if (dOperation.Target is IMethodReferenceOperation mrOperation) + operationName = mrOperation.Method.Name; + } + else if (operation is IEventReferenceOperation eOperation) + { + operationName = eOperation.Event.Name; + } - context.ReportDiagnostic(operation.CreateDiagnostic(SwitchRule(attribute.AttributeType), operationName, attribute.OsPlatformName, attribute.Version.ToString())); + context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(attribute.AttributeType), + operationName, attribute.PlatformName, attribute.Version.ToString())); } private static void AnalyzeInvocationOperation(IOperation operation, OperationAnalysisContext context, - List? parsedTfms, ref PooledDictionary> platformSpecificOperations) + ref PooledDictionary> platformSpecificOperations) { using var builder = ArrayBuilder.GetInstance(); AttributeData[]? attributes = null; @@ -237,24 +261,36 @@ private static void AnalyzeInvocationOperation(IOperation operation, OperationAn { attributes = FindAllPlatformAttributesApplied(iOperation.TargetMethod.GetAttributes(), iOperation.TargetMethod.ContainingType); } + else if (operation is IObjectCreationOperation cOperation) + { + attributes = FindAllPlatformAttributesApplied(cOperation.Constructor.GetAttributes(), cOperation.Constructor.ContainingType); + } else if (operation is IPropertyReferenceOperation pOperation) { - if (!IsWithinConditionalOperation(operation)) - attributes = FindAllPlatformAttributesApplied(pOperation.Property.GetAttributes(), pOperation.Property.ContainingType); + attributes = FindAllPlatformAttributesApplied(pOperation.Property.GetAttributes(), pOperation.Property.ContainingType); } else if (operation is IFieldReferenceOperation fOperation) { if (!IsWithinConditionalOperation(operation)) attributes = FindAllPlatformAttributesApplied(fOperation.Field.GetAttributes(), fOperation.Field.ContainingType); } + else if (operation is IDelegateCreationOperation dOperation) + { + if (dOperation.Target is IMethodReferenceOperation mrOperation) + attributes = FindAllPlatformAttributesApplied(mrOperation.Method.GetAttributes(), mrOperation.Method.ContainingType); + } + else if (operation is IEventReferenceOperation eOperation) + { + attributes = FindAllPlatformAttributesApplied(eOperation.Event.GetAttributes(), eOperation.Event.ContainingType); + } if (attributes != null) { foreach (AttributeData attribute in attributes) { - if (PlatformAttributeInfo.TryParseAttributeData(attribute, out PlatformAttributeInfo parsedAttribute)) + if (PlatformAttributeInfo.TryParsePlatformAttributeInfo(attribute, out PlatformAttributeInfo parsedAttribute)) { - if (!(IsSuppressedByTfm(parsedTfms, parsedAttribute) || IsSuppressedByAttribute(parsedAttribute, context.ContainingSymbol))) + if (!IsSuppressedByAttribute(parsedAttribute, context.ContainingSymbol)) { builder.Add(parsedAttribute); } @@ -269,7 +305,8 @@ private static void AnalyzeInvocationOperation(IOperation operation, OperationAn } private static bool IsWithinConditionalOperation(IOperation pOperation) => - pOperation.Parent is IBinaryOperation bo && + pOperation.ConstantValue.HasValue && + pOperation.Parent is IBinaryOperation bo && (bo.OperatorKind == BinaryOperatorKind.Equals || bo.OperatorKind == BinaryOperatorKind.NotEquals || bo.OperatorKind == BinaryOperatorKind.GreaterThan || @@ -277,30 +314,28 @@ pOperation.Parent is IBinaryOperation bo && bo.OperatorKind == BinaryOperatorKind.GreaterThanOrEqual || bo.OperatorKind == BinaryOperatorKind.LessThanOrEqual); - private static bool IsSuppressedByTfm(List? parsedTfms, PlatformAttributeInfo parsedAttribute) + private static bool IsOSPlatformsEqual(string left, string right) { - if (parsedTfms != null) + if (left.Equals(right, StringComparison.OrdinalIgnoreCase)) { - foreach (PlatformAttributeInfo tfm in parsedTfms) - { - if (IsOSPlatformsEqual(parsedAttribute.OsPlatformName, tfm.OsPlatformName)) - { - return AttributeVersionsMatch(parsedAttribute, tfm); - } - } + return true; + } + else + { + var normalizedLeft = NormalizeOSName(left); + var normalizedRight = NormalizeOSName(right); + return string.Equals(normalizedLeft, normalizedRight, StringComparison.OrdinalIgnoreCase); } - - return false; } - private static bool IsOSPlatformsEqual(string firstOs, string secondOs) + private static string NormalizeOSName(string name) { - return firstOs.Equals(secondOs, StringComparison.OrdinalIgnoreCase) - || (firstOs.Equals("OSX", StringComparison.OrdinalIgnoreCase) && secondOs.Equals("MACOS", StringComparison.OrdinalIgnoreCase)) - || (secondOs.Equals("OSX", StringComparison.OrdinalIgnoreCase) && firstOs.Equals("MACOS", StringComparison.OrdinalIgnoreCase)); + if (name.Equals(OSX, StringComparison.OrdinalIgnoreCase)) + return macOS; + return name; } - private static DiagnosticDescriptor SwitchRule(PlatformAttributeType attributeType) + private static DiagnosticDescriptor SelectRule(PlatformAttributeType attributeType) { if (attributeType == PlatformAttributeType.MinimumOSPlatformAttribute) return MinimumOsRule; @@ -309,56 +344,6 @@ private static DiagnosticDescriptor SwitchRule(PlatformAttributeType attributeTy return RemovedRule; } - private static List? ParseTfm(AnalyzerOptions options, ISymbol containingSymbol, Compilation compilation, CancellationToken cancellationToken) - { - string? tfmString = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetFramework, MinimumOsRule, containingSymbol, compilation, cancellationToken); - if (tfmString != null) - { - using var builder = ArrayBuilder.GetInstance(); - - var tfms = tfmString.Split(SeparatorSemicolon); - - foreach (var tfm in tfms) - { - var tokens = tfm.Split(SeparatorDash); - - if (tokens.Length == 1) - { - if (!s_neutralTfmRegex.IsMatch(tokens[0])) - { - builder.Add(CreateWindowsOnlyPlatformInfo()); - } - } - else - { - Debug.Assert(tokens.Length == 2); - if (PlatformAttributeInfo.TryParseTfmString(tokens[1], out PlatformAttributeInfo parsedTfm)) - { - var tpmv = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetPlatformMinVersion, MinimumOsRule, containingSymbol, compilation, cancellationToken); - if (tpmv != null) - { - if (Version.TryParse(tpmv, out Version version)) - { - parsedTfm.Version = version; - } - } - builder.Add(parsedTfm); - } - } - } - return builder.ToList(); - } - return null; - } - - private static PlatformAttributeInfo CreateWindowsOnlyPlatformInfo() - { - var platformInfo = new PlatformAttributeInfo(); - platformInfo.Version = new Version(7, 0); - platformInfo.OsPlatformName = Windows; - return platformInfo; - } - private static AttributeData[] FindAllPlatformAttributesApplied(ImmutableArray immediateAttributes, INamedTypeSymbol parent) { using var builder = ArrayBuilder.GetInstance(); @@ -375,7 +360,8 @@ private static AttributeData[] FindAllPlatformAttributesApplied(ImmutableArray _platformCheckMethods; - private readonly INamedTypeSymbol _osPlatformType; - - public OperationVisitor( - ImmutableArray platformCheckMethods, - INamedTypeSymbol osPlatformType, - GlobalFlowStateAnalysisContext analysisContext) - : base(analysisContext, hasPredicatedGlobalState: true) - { - _platformCheckMethods = platformCheckMethods; - _osPlatformType = osPlatformType; - } - - public override GlobalFlowStateAnalysisValueSet VisitInvocation_NonLambdaOrDelegateOrLocalFunction( - IMethodSymbol method, - IOperation? visitedInstance, - ImmutableArray visitedArguments, - bool invokedAsDelegate, - IOperation originalOperation, - GlobalFlowStateAnalysisValueSet defaultValue) - { - var value = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(method, visitedInstance, visitedArguments, invokedAsDelegate, originalOperation, defaultValue); - - if (_platformCheckMethods.Contains(method.OriginalDefinition) && !visitedArguments.IsEmpty) - { - return RuntimeOSPlatformInfo.TryDecode(method, visitedArguments, DataFlowAnalysisContext.ValueContentAnalysisResultOpt, _osPlatformType, out var platformInfo) ? - new GlobalFlowStateAnalysisValueSet(platformInfo) : - GlobalFlowStateAnalysisValueSet.Unknown; - } - - return GetValueOrDefault(value); - } - } - } -} 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 b7b97ffff3..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/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs index 7009b13250..f02cc7939f 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs @@ -1,5 +1,7 @@ // 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.Runtime.InteropServices; using System.Threading.Tasks; using Xunit; @@ -27,8 +29,25 @@ public void M2() { } } -" + MockAttributesSource + MockRuntimeApiSource; +" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); + + var vbSource = @" +Imports System.Runtime.Versioning +Imports System.Runtime.InteropServices + +Public Class Test + Public Sub M1() + [|M2()|] + If RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3) Then M2() + End Sub + + + Public Sub M2() + End Sub +End Class +" + MockAttributesVbSource + MockRuntimeApiSourceVb; + await VerifyAnalyzerAsyncVb(vbSource); } [Fact] @@ -57,12 +76,12 @@ public void M2() { } } -" + MockAttributesSource + MockRuntimeApiSource; +" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } [Fact] - public async Task GuardedWith_IsOSPlatformOrLater_SimpleIfElseTest() + public async Task GuardedWith_IsOSPlatformOrLater_SimpleIfElse() { var source = @" using System.Runtime.Versioning; @@ -86,13 +105,13 @@ void M1() void M2() { } -}" + MockAttributesSource + MockRuntimeApiSource; +}" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } [Fact] - public async Task GuardedWith_IsOSPlatformEarlierThan_SimpleIfElseTest() + public async Task GuardedWith_IsOSPlatformEarlierThan_SimpleIfElse() { var source = @" using System.Runtime.Versioning; @@ -123,13 +142,12 @@ void M2() void M3 () { } -}" + MockAttributesSource + MockRuntimeApiSource; - +}" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } [Fact] - public async Task GuardedWith_StringOverload_SimpleIfElseTest() + public async Task GuardedWith_StringOverload_SimpleIfElse() { var source = @" using System.Runtime.Versioning; @@ -171,13 +189,46 @@ void M2() void M3 () { } -}" + MockAttributesSource + MockRuntimeApiSource; - +}" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); + + var vbSource = @" +Imports System.Runtime.Versioning +Imports System.Runtime.InteropServices + +Class Test + Private Sub M1() + If RuntimeInformationHelper.IsOSPlatformEarlierThan(""Windows10.1"") Then + [|M2()|] + M3() + Else + [|M2()|] + [|M3()|] + End If + + If RuntimeInformationHelper.IsOSPlatformOrLater(""Windows10.1.3"") Then + [|M3()|] + M2() + Else + [|M2()|] + [|M3()|] + End If + End Sub + + + Private Sub M2() + End Sub + + + Private Sub M3() + End Sub +End Class +" + MockAttributesVbSource + MockRuntimeApiSourceVb; + await VerifyAnalyzerAsyncVb(vbSource); } [Fact] - public async Task OsDependentEnumValue_GuardedWith_SimpleIfElseTest() + public async Task OsDependentEnumValue_GuardedCall_SimpleIfElse() { var source = @" using System.Runtime.Versioning; @@ -213,10 +264,305 @@ public enum PlatformEnum Linux48, NoPlatform } +" + MockAttributesCsSource + MockRuntimeApiSource; + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task OsDependentProperty_GuardedCall_SimpleIfElse() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + [RemovedInOSPlatform(""Linux4.1"")] + public string RemovedProperty { get; set;} + + public void M1() + { + if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 4)) + { + RemovedProperty = ""Hello""; + string s = RemovedProperty; + M2(RemovedProperty); + } + else + { + [|RemovedProperty|] = ""Hello""; + string s = [|RemovedProperty|]; + M2([|RemovedProperty|]); + } + } + + public string M2(string option) + { + return option; + } +} +" + MockAttributesCsSource + MockRuntimeApiSource; + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task OsDependentConstructorOfClass_GuardedCall_SimpleIfElse() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + { + C instance = new C(); + instance.M2(); + } + else + { + C instance2 = [|new C()|]; + instance2.M2(); + } + } +} + +public class C +{ + [MinimumOSPlatform(""Windows10.1.2.3"")] + public C() + { + } + public void M2() + { + } +} +" + MockAttributesCsSource + MockRuntimeApiSource; + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task ConstructorAndMethodOfOsDependentClass_GuardedCall_SimpleIfElse() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + { + OsDependentClass odc = new OsDependentClass(); + odc.M2(); + } + else + { + OsDependentClass odc = [|new OsDependentClass()|]; + [|odc.M2()|]; + } + } +} +[MinimumOSPlatform(""Windows10.1.2.3"")] +public class OsDependentClass +{ + public void M2() + { + } +} +" + MockAttributesCsSource + MockRuntimeApiSource; + await VerifyAnalyzerAsyncCs(source); + + var vbSource = @" +Imports System.Runtime.Versioning +Imports System.Runtime.InteropServices + +Public Class Test + Public Sub M1() + If RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) Then + Dim odc As OsDependentClass = New OsDependentClass() + odc.M2() + Else + Dim odc As OsDependentClass = [|New OsDependentClass()|] + [|odc.M2()|] + End If + End Sub +End Class + + +Public Class OsDependentClass + Public Sub M2() + End Sub +End Class +" + MockRuntimeApiSourceVb + MockAttributesVbSource; + await VerifyAnalyzerAsyncVb(vbSource); + } + + [Fact] + public async Task LocalFunctionCallsOsDependentMember_GuardedCall_SimpleIfElse() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public void M1() + { + void Test() + { + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2, 1)) + { + M2(); + } + else + { + [|M2()|]; + } + } + Test(); + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockAttributesCsSource + MockRuntimeApiSource; + await VerifyAnalyzerAsyncCs(source); + } + + /*[Fact] // TODO: Need to be fixed + public async Task LambdaCallsOsDependentMember_GuardedCall_SimpleIfElse() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; +using System; + +public class Test +{ + public void M1() + { + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2, 1)) + { + void Test() => M2(); + Test(); + } + else + { + void Test() => [|M2()|]; + Test(); + } + + Action action = () => + { + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2, 1)) + { + M2(); + } + else + { + [|M2()|]; + } + }; + action.Invoke(); + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} " + MockAttributesSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); + }*/ + + [Fact] + public async Task OsDependentEventAccessed_GuardedCall_SimpleIfElse() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public delegate void Del(); + + [MinimumOSPlatform(""Windows10.1.2.3"")] + public event Del SampleEvent; + + public void M1() + { + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + SampleEvent += M3; + } + else + { + [|SampleEvent|] += M4; + } + } + + public void M2() + { + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + SampleEvent?.Invoke(); + } + else + { + [|SampleEvent|]?.Invoke(); + } + } + + public void M3() + { + } + public void M4() + { + } +} +" + MockAttributesCsSource + MockRuntimeApiSource; + await VerifyAnalyzerAsyncCs(source); } + /*[Fact] // TODO: need to be fixed + public async Task OsDependentMethodAssignedToDelegate_GuardedCall_SimpleIfElse() + { + var source = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + public delegate void Del(); + + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void DelegateMethod() + { + } + public void M1() + { + if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + { + Del handler = DelegateMethod; + handler(); + } + else + { + Del handler = [|DelegateMethod|]; + handler(); + } + } +} +" + MockAttributesSource + MockRuntimeApiSource; + await VerifyAnalyzerAsyncCs(source); + }*/ + [Fact] public async Task GuardedCall_SimpleIfElseIfElseTest() { @@ -256,8 +602,7 @@ void M1() void M2() { } -}" + MockAttributesSource + MockRuntimeApiSource; - +}" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } @@ -283,7 +628,7 @@ public void M2() { } } -" + MockAttributesSource + MockRuntimeApiSource; +" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } @@ -311,7 +656,7 @@ public void M2() { } } -" + MockAttributesSource + MockRuntimeApiSource; +" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } @@ -336,8 +681,26 @@ public void M2() { } } -" + MockAttributesSource + MockRuntimeApiSource; +" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); + + var vbSource = @" +Imports System.Runtime.Versioning +Imports System.Runtime.InteropServices + +Public Class Test + Public Sub M1() + [|M2()|] + If Not RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3) Then Return + M2() + End Sub + + + Public Sub M2() + End Sub +End Class +" + MockAttributesVbSource + MockRuntimeApiSourceVb; + await VerifyAnalyzerAsyncVb(vbSource); } [Fact] @@ -381,7 +744,7 @@ public void M2() { } } -" + MockAttributesSource + MockRuntimeApiSource; +" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } @@ -423,7 +786,7 @@ public void M2() { } } -" + MockAttributesSource + MockRuntimeApiSource; +" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } @@ -463,7 +826,7 @@ public void M2() { } } -" + MockAttributesSource + MockRuntimeApiSource; +" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } @@ -515,7 +878,7 @@ public void M2() { } } -" + MockAttributesSource + MockRuntimeApiSource; +" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } @@ -563,7 +926,7 @@ public void M2() { } } -" + MockAttributesSource + MockRuntimeApiSource; +" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } @@ -604,8 +967,30 @@ void M2() { } }" -+ MockAttributesSource + MockRuntimeApiSource; ++ MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); + + var vbSource = @" +Imports System.Runtime.Versioning +Imports System.Runtime.InteropServices + +Class Test + Private Sub M1() + If (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1) OrElse RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1)) AndAlso (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12) OrElse RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 2)) Then + [|M2()|] + ElseIf RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 13) OrElse RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 3) OrElse RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 4) Then + [|M2()|] + Else + [|M2()|] + End If + End Sub + + + Private Sub M2() + End Sub +End Class +" + MockRuntimeApiSourceVb + MockAttributesVbSource; + await VerifyAnalyzerAsyncVb(vbSource); } [Fact] @@ -650,7 +1035,7 @@ void M1() void M2() { } -}" + MockAttributesSource + MockRuntimeApiSource; +}" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } @@ -678,9 +1063,27 @@ void M1() void M2() { } -}" + MockAttributesSource + MockRuntimeApiSource; - +}" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); + + var vbSource = @" +Imports System.Diagnostics +Imports System.Runtime.Versioning +Imports System.Runtime.InteropServices + +Class Test + Private Sub M1() + [|M2()|] + Debug.Assert(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + M2() + End Sub + + + Private Sub M2() + End Sub +End Class +" + MockAttributesVbSource + MockRuntimeApiSourceVb; + await VerifyAnalyzerAsyncVb(vbSource); } [Fact] @@ -715,8 +1118,7 @@ void M1() void M2() { } -}" + MockAttributesSource + MockRuntimeApiSource; - +}" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } @@ -742,8 +1144,7 @@ void M1() void M2() { } -}" + MockAttributesSource + MockRuntimeApiSource; - +}" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } @@ -769,8 +1170,7 @@ void M1() void M2() { } -}" + MockAttributesSource + MockRuntimeApiSource; - +}" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); } @@ -817,9 +1217,42 @@ void M1(bool flag1, bool flag2) void M2() { } -}" + MockAttributesSource + MockRuntimeApiSource; - +}" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); + + var vbSource = @" +Imports System.Runtime.Versioning +Imports System.Runtime.InteropServices + +Class Test + Private Sub M1(ByVal flag1 As Boolean, ByVal flag2 As Boolean) + [|M2()|] + + If RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11) Then + M2() + + If flag1 OrElse flag2 Then + M2() + Else + M2() + End If + + M2() + End If + + If flag1 OrElse flag2 Then + [|M2()|] + Else + [|M2()|] + End If + End Sub + + + Private Sub M2() + End Sub +End Class +" + MockRuntimeApiSourceVb + MockAttributesVbSource; + await VerifyAnalyzerAsyncVb(vbSource); } /*[Fact] //TODO: Not working anymore, fix this @@ -833,14 +1266,14 @@ class Test { void M1() { - {|#0:M2()|}; + [|M2()|]; if (IsWindows11OrLater()) { M2(); } - {|#1:M2()|}; + [|M2()|]; } [MinimumOSPlatform(""Windows10.1.2.3"")] @@ -854,22 +1287,7 @@ bool IsWindows11OrLater() } }" + MockAttributesSource + MockRuntimeApiSource; - var test = new VerifyCS.Test - { - TestState = - { - Sources = { source }, - AdditionalFiles = { (".editorconfig", "dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive") } - } - }; - - test.ExpectedDiagnostics.AddRange(new[] - { - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithLocation(0).WithArguments("M2", "Windows", "10.1.2.3"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithLocation(1).WithArguments("M2", "Windows", "10.1.2.3"), - }); - - await test.RunAsync(); + await VerifyAnalyzerAsyncCs(source, @"{ ("".editorconfig"", ""dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive"") }"); }*/ private readonly string MockRuntimeApiSource = @" @@ -915,5 +1333,51 @@ public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int #pragma warning restore CA1801, IDE0060 // Review unused parameters } }"; + + private readonly string MockRuntimeApiSourceVb = @" +Namespace System.Runtime.InteropServices + Module RuntimeInformationHelper + Function IsOSPlatformOrLater(ByVal osPlatform As OSPlatform, ByVal major As Integer) As Boolean + Return True + End Function + + Function IsOSPlatformOrLater(ByVal osPlatform As OSPlatform, ByVal major As Integer, ByVal minor As Integer) As Boolean + Return True + End Function + + Function IsOSPlatformOrLater(ByVal osPlatform As OSPlatform, ByVal major As Integer, ByVal minor As Integer, ByVal build As Integer) As Boolean + Return True + End Function + + Function IsOSPlatformOrLater(ByVal osPlatform As OSPlatform, ByVal major As Integer, ByVal minor As Integer, ByVal build As Integer, ByVal revision As Integer) As Boolean + Return True + End Function + + Function IsOSPlatformEarlierThan(ByVal osPlatform As OSPlatform, ByVal major As Integer) As Boolean + Return False + End Function + + Function IsOSPlatformEarlierThan(ByVal osPlatform As OSPlatform, ByVal major As Integer, ByVal minor As Integer) As Boolean + Return False + End Function + + Function IsOSPlatformEarlierThan(ByVal osPlatform As OSPlatform, ByVal major As Integer, ByVal minor As Integer, ByVal build As Integer) As Boolean + Return False + End Function + + Function IsOSPlatformEarlierThan(ByVal osPlatform As OSPlatform, ByVal major As Integer, ByVal minor As Integer, ByVal build As Integer, ByVal revision As Integer) As Boolean + Return False + End Function + + Function IsOSPlatformOrLater(ByVal platformName As String) As Boolean + Return True + End Function + + Function IsOSPlatformEarlierThan(ByVal platformName As String) As Boolean + Return True + End Function + End Module +End Namespace +"; } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index cede6d5012..21bf0c5cd7 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -8,6 +8,10 @@ Microsoft.NetCore.Analyzers.InteropServices.PlatformCompatabilityAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.NetCore.Analyzers.InteropServices.PlatformCompatabilityAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests { public partial class PlatformCompatabilityAnalyzerTests @@ -15,7 +19,7 @@ public partial class PlatformCompatabilityAnalyzerTests [Fact] public async Task OsDependentMethodsCalledWarns() { - var source = @" + var csSource = @" using System.Runtime.Versioning; public class Test @@ -39,8 +43,33 @@ public void Removed() { } } -" + MockAttributesSource; - await VerifyAnalyzerAsyncCs(source); +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(csSource); + + var vbSource = @" +Imports System.Runtime.Versioning + +Public Class Test + Public Sub M1() + [|WindowsOnly()|] + [|Obsoleted()|] + [|Removed()|] + End Sub + + + Public Sub WindowsOnly() + End Sub + + + Public Sub Obsoleted() + End Sub + + + Public Sub Removed() + End Sub +End Class +" + MockAttributesVbSource; + await VerifyAnalyzerAsyncVb(vbSource); } [Fact] @@ -61,6 +90,7 @@ public void M1() ObsoletedWithNullString(); RemovedWithEmptyString(); } + [MinimumOSPlatform(""Windows10"")] public void Windows10() { @@ -90,7 +120,7 @@ public void RemovedWithEmptyString() { } } -" + MockAttributesSource; +" + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source); } @@ -126,13 +156,13 @@ public int M3(int option) return option; } } -" + MockAttributesSource; +" + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source); } [Theory] [MemberData(nameof(Create_AtrrbiuteProperty_WithCondtions))] - public async Task OsDependentPropertyConditionalCheckNotWarns(string attribute, string property, string condition, string setter, string getter) + public async Task OsDependentPropertyConditionalCheckWarns(string attribute, string property, string condition, string setter, string getter) { var source = @" using System.Runtime.Versioning; @@ -154,15 +184,15 @@ public object M2(object option) return option; } } -" + MockAttributesSource; +" + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source); } public static IEnumerable Create_AtrrbiuteProperty_WithCondtions() { - yield return new object[] { "MinimumOSPlatform", "string StringProperty", " == StringProperty", @"StringProperty|] = ""Hello""", "StringProperty" }; - yield return new object[] { "ObsoletedInOSPlatform", "int IntProperty", " > IntProperty", "IntProperty|] = 5", "IntProperty" }; - yield return new object[] { "RemovedInOSPlatform", "int RemovedProperty", " <= RemovedProperty", "RemovedProperty|] = 3", "RemovedProperty" }; + yield return new object[] { "MinimumOSPlatform", "string StringProperty", " == [|StringProperty|]", @"StringProperty|] = ""Hello""", "StringProperty" }; + yield return new object[] { "ObsoletedInOSPlatform", "int IntProperty", " > [|IntProperty|]", "IntProperty|] = 5", "IntProperty" }; + yield return new object[] { "RemovedInOSPlatform", "int RemovedProperty", " <= [|RemovedProperty|]", "RemovedProperty|] = 3", "RemovedProperty" }; } [Fact] @@ -194,7 +224,7 @@ public enum PlatformEnum Linux48, NoPlatform } -" + MockAttributesSource; +" + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source); } @@ -229,8 +259,34 @@ public enum PlatformEnum Linux48, NoPlatform } -" + MockAttributesSource; +" + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source); + + var vbSource = @" +Imports System.Runtime.Versioning + +Public Class Test2 + Public Sub M1() + Dim val As PlatformEnum = [|PlatformEnum.Windows10|] + If val = [|PlatformEnum.Windows10|] Then Return + M2([|PlatformEnum.Windows10|]) + M2([|PlatformEnum.Linux48|]) + M2(PlatformEnum.NoPlatform) + End Sub + + Public Sub M2(ByVal [option] As PlatformEnum) + End Sub +End Class + +Public Enum PlatformEnum + + Windows10 + < MinimumOSPlatform(""Linux4.8"") > + Linux48 + NoPlatform +End Enum +" + MockAttributesVbSource; + await VerifyAnalyzerAsyncVb(vbSource); } [Fact] @@ -263,11 +319,11 @@ public int M3(int option) return option; } } -" + MockAttributesSource; +" + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source); } - /*[Fact] TODO: test with assembly level attribute + /*[Fact] TODO: enable the test when preview 8 consumed public async Task MethodWithTargetPlatrofrmAttributeDoesNotWarn() { var source = @" @@ -309,7 +365,7 @@ public void M2() { } } -" + MockAttributesSource; +" + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source); } @@ -339,13 +395,63 @@ public void M2() } } } -" + MockAttributesSource; +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source); + + var vbSource = @" +Imports System.Runtime.Versioning +Imports Ns + +Public Class Test + Private field As B = New B() + + Public Sub M1() + [|field.M2()|] + End Sub +End Class + +Namespace Ns + Public Class B + + Public Sub M2() + End Sub + End Class +End Namespace +" + MockAttributesVbSource; + await VerifyAnalyzerAsyncVb(vbSource); + } + [Fact] + public async Task OsDependentConstructorOfClassUsedCalledWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + public void M1() + { + C instance = [|new C()|]; + instance.M2(); + } +} + +public class C +{ + [MinimumOSPlatform(""Windows10.1.2.3"")] + public C() + { + } + public void M2() + { + } +} +" + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source); } [Fact] - public async Task MethodOfOsDependentClassCalledWarns() + public async Task ConstructorAndMethodOfOsDependentClassCalledWarns() { var source = @" using System.Runtime.Versioning; @@ -354,7 +460,7 @@ public class Test { public void M1() { - OsDependentClass odc = new OsDependentClass(); + OsDependentClass odc = [|new OsDependentClass()|]; [|odc.M2()|]; } } @@ -365,7 +471,216 @@ public void M2() { } } -" + MockAttributesSource; +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source); + + var vbSource = @" +Imports System.Runtime.Versioning + +Public Class Test + Public Sub M1() + Dim odc As OsDependentClass = [|New OsDependentClass()|] + [|odc.M2()|] + End Sub +End Class + + +Public Class OsDependentClass + Public Sub M2() + End Sub +End Class +" + MockAttributesVbSource; + await VerifyAnalyzerAsyncVb(vbSource); + } + + [Fact] + public async Task LocalFunctionCallsOsDependentMemberWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + public void M1() + { + void Test() + { + [|M2()|]; + } + Test(); + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task LocalGuardedFunctionCallsOsDependentMemberNotWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + [MinimumOSPlatform(""Windows10.2"")] + public void M1() + { + void Test() + { + M2(); + } + Test(); + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task LambdaCallsOsDependentMemberWarns() + { + var source = @" +using System.Runtime.Versioning; +using System; + +public class Test +{ + public void M1() + { + void Test() => [|M2()|]; + Test(); + + Action action = () => + { + [|M2()|]; + }; + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task AttributedLambdaCallsOsDependentMemberNotWarn() + { + var source = @" +using System.Runtime.Versioning; +using System; + +public class Test +{ + [MinimumOSPlatform(""Windows10.13"")] + public void M1() + { + void Test() => M2(); + Test(); + + Action action = () => + { + M2(); + }; + } + + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void M2() + { + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task OsDependentEventAccessedWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + public delegate void Del(); + + [MinimumOSPlatform(""Windows10.1.2.3"")] + public event Del SampleEvent; + + public void M1() + { + [|SampleEvent|] += M3; + M2(); + } + + public void M2() + { + [|SampleEvent|]?.Invoke(); + } + + public void M3() + { + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source); + + var vbSource = @" +Imports System.Runtime.Versioning + +Public Class Test + Public Delegate Sub Del() + + Public Event SampleEvent As Del + + Public Sub M1() + AddHandler [|SampleEvent|], AddressOf M3 + M2() + End Sub + + Public Sub M2() + RaiseEvent [|SampleEvent|] + End Sub + + Public Sub M3() + End Sub +End Class" ++ MockAttributesVbSource; + await VerifyAnalyzerAsyncVb(vbSource); + } + + [Fact] + public async Task OsDependentMethodAssignedToDelegateWarns() + { + var source = @" +using System.Runtime.Versioning; + +public class Test +{ + public delegate void Del(); // The attribute not supported on delegates, so no tets for that + + [MinimumOSPlatform(""Windows10.1.2.3"")] + public void DelegateMethod() + { + } + public void M1() + { + Del handler = [|DelegateMethod|]; + handler(); // assume it shouldn't warn here + } +} +" + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source); } @@ -441,12 +756,18 @@ public void M2() { } } -" + MockAttributesSource; +" + MockAttributesCsSource; if (warn) - await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + { + await VerifyAnalyzerAsyncCs(source, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(9, 32, 9, 54).WithArguments(".ctor", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + } else + { await VerifyAnalyzerAsyncCs(source); + } } [Theory] @@ -473,12 +794,18 @@ public void M2() { } } -" + MockAttributesSource; +" + MockAttributesCsSource; if (warn) - await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + { + await VerifyAnalyzerAsyncCs(source, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(9, 32, 9, 54).WithArguments(".ctor", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + } else + { await VerifyAnalyzerAsyncCs(source); + } } public static IEnumerable ObsoletedRemovedAttributeTestData() @@ -522,96 +849,21 @@ public void M2() { } } -" + MockAttributesSource; +" + MockAttributesCsSource; if (warn) - await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RemovedRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + { + await VerifyAnalyzerAsyncCs(source, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(9, 32, 9, 54).WithArguments(".ctor", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RemovedRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + } else + { await VerifyAnalyzerAsyncCs(source); + } } - [Theory] - [MemberData(nameof(MinimumOsAttributeTfmTestData))] - public async Task TfmAndTargetPlatformMinVersionWithMinimumOsAttribute(string editorConfigText, bool expectDiagnostic) - { - var invocation = expectDiagnostic ? @"[|M2()|]" : "M2()"; - var source =$@" -using System.Runtime.Versioning; - -public class Test -{{ - public void M1() - {{ - {invocation}; - }} - [MinimumOSPlatform(""Windows10.1.1.1"")] - public void M2() - {{ - }} -}} -" + MockAttributesSource; - await VerifyAnalyzerAsyncCs(source, editorConfigText); - } - - public static IEnumerable MinimumOsAttributeTfmTestData() - { - yield return new object[] { "", true }; - yield return new object[] { "build_property.TargetFramework=net5.0", true }; - yield return new object[] { "build_property.TargetFramework=net472", true }; - yield return new object[] { "build_property.TargetFramework=net5.0-linux", true }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows", true }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows10.1.1.1", false }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows10.2", false }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows11.0", false }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows10.0", true }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows11.0\nbuild_property.TargetPlatformMinVersion=10.0;", true }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows10.2\nbuild_property.TargetPlatformMinVersion=10.1;", true }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows10.1.1.2\nbuild_property.TargetPlatformMinVersion=10.0.0.1;", true }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows10.2.1\nbuild_property.TargetPlatformMinVersion=9.1.1.1;", true }; - } - - public static IEnumerable ObsoletedRemovedTfmTestData() - { - yield return new object[] { "", true }; - yield return new object[] { "build_property.TargetFramework=net5.0", true }; - yield return new object[] { "build_property.TargetFramework=net472", false }; // because no version part it is 0.0.0.0 - yield return new object[] { "build_property.TargetFramework=net5.0-linux", true }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows", true }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows10.1.1.1", true }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows10.2", true }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows11.0", true }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows10.0", false }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows11.0\nbuild_property.TargetPlatformMinVersion=10.0;", false }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows10.2\nbuild_property.TargetPlatformMinVersion=10.1;", false }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows10.1.1.2\nbuild_property.TargetPlatformMinVersion=10.0.0.1;", false }; - yield return new object[] { "build_property.TargetFramework=net5.0-windows10.2.1\nbuild_property.TargetPlatformMinVersion=9.1.1.1;", false }; - } - - [Theory] - [MemberData(nameof(ObsoletedRemovedTfmTestData))] - public async Task TfmAndTargetPlatformMinVersionWithObsoleteAttribute(string editorConfigText, bool expectDiagnostic) - { - var invocation = expectDiagnostic ? @"[|M2()|]" : "M2()"; - var source =$@" -using System.Runtime.Versioning; - -public class Test -{{ - public void M1() - {{ - {invocation}; - }} - [ObsoletedInOSPlatform(""Windows10.1.1.1"")] - public void M2() - {{ - }} -}} -" + MockAttributesSource; - - await VerifyAnalyzerAsyncCs(source, editorConfigText); - } - - private static VerifyCS.Test PopulateTest(string sourceCode) + private static VerifyCS.Test PopulateTestCs(string sourceCode) { return new VerifyCS.Test { @@ -622,23 +874,38 @@ private static VerifyCS.Test PopulateTest(string sourceCode) }; } - private static async Task VerifyAnalyzerAsyncCs(string sourceCode) => await PopulateTest(sourceCode).RunAsync(); + private static async Task VerifyAnalyzerAsyncCs(string sourceCode) => await PopulateTestCs(sourceCode).RunAsync(); - private static async Task VerifyAnalyzerAsyncCs(string sourceCode, DiagnosticResult expectedDiagnostic) + private static async Task VerifyAnalyzerAsyncCs(string sourceCode, params DiagnosticResult[] expectedDiagnostics) { - var test = PopulateTest(sourceCode); - test.ExpectedDiagnostics.Add(expectedDiagnostic); + var test = PopulateTestCs(sourceCode); + foreach (DiagnosticResult expectedDiagnostic in expectedDiagnostics) + { + test.ExpectedDiagnostics.Add(expectedDiagnostic); + } await test.RunAsync(); } private static async Task VerifyAnalyzerAsyncCs(string sourceCode, string additionalFiles) { - var test = PopulateTest(sourceCode); + var test = PopulateTestCs(sourceCode); test.TestState.AdditionalFiles.Add((".editorconfig", additionalFiles)); await test.RunAsync(); } - private readonly string MockAttributesSource = @" + private static async Task VerifyAnalyzerAsyncVb(string sourceCode) => await PopulateTestVb(sourceCode).RunAsync(); + + private static VerifyVB.Test PopulateTestVb(string sourceCode) + { + return new VerifyVB.Test + { + TestCode = sourceCode, + ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp50, + MarkupOptions = MarkupOptions.UseFirstDescriptor, + }; + } + + private readonly string MockAttributesCsSource = @" namespace System.Runtime.Versioning { public abstract class OSPlatformAttribute : Attribute @@ -713,6 +980,64 @@ public ObsoletedInOSPlatformAttribute(string platformName, string message) : bas public string Url { get; set; } } } +"; + + private readonly string MockAttributesVbSource = @" +Namespace System.Runtime.Versioning + Public MustInherit Class OSPlatformAttribute + Inherits Attribute + + Private Protected Sub New(ByVal platformName As String) + PlatformName = platformName + End Sub + + Public ReadOnly Property PlatformName As String + End Class + + + Public NotInheritable Class TargetPlatformAttribute + Inherits OSPlatformAttribute + + Public Sub New(ByVal platformName As String) + MyBase.New(platformName) + End Sub + End Class + + + Public NotInheritable Class MinimumOSPlatformAttribute + Inherits OSPlatformAttribute + + Public Sub New(ByVal platformName As String) + MyBase.New(platformName) + End Sub + End Class + + + Public NotInheritable Class RemovedInOSPlatformAttribute + Inherits OSPlatformAttribute + + Public Sub New(ByVal platformName As String) + MyBase.New(platformName) + End Sub + End Class + + + Public NotInheritable Class ObsoletedInOSPlatformAttribute + Inherits OSPlatformAttribute + + Public Sub New(ByVal platformName As String) + MyBase.New(platformName) + End Sub + + Public Sub New(ByVal platformName As String, ByVal message As String) + MyBase.New(platformName) + Message = message + End Sub + + Public ReadOnly Property Message As String + Public Property Url As String + End Class +End Namespace "; } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzerTests.cs deleted file mode 100644 index d8e7174fc1..0000000000 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/RuntimePlatformCheckAnalyzerTests.cs +++ /dev/null @@ -1,820 +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.Threading.Tasks; -using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; -using Xunit; -using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< - Microsoft.NetCore.Analyzers.InteropServices.RuntimePlatformCheckAnalyzer, - Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; - -namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests -{ - public class RuntimePlatformCheckAnalyzerTests - { - private readonly string PlatformCheckApiSource = @" -namespace System.Runtime.InteropServices -{ - public class RuntimeInformationHelper - { - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major) => true; - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor) => true; - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build) => true; - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build, int revision) => true; - - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major) => true; - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor) => true; - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build) => true; - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build, int revision) => true; - } -}"; - - [Fact] - public async Task SimpleIfTest() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1)) - { - {|#1:M2()|}; // Platform checks:'IsOSPlatformOrLater;Windows;1' - } - - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 1, 1)) - { - {|#2:M2()|}; // Platform checks:'IsOSPlatformEarlierThan;Windows;1.1' - } - - {|#3:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("IsOSPlatformEarlierThan;Windows;1.1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("")); - } - - [Fact] - public async Task SimpleIfTest_02() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1)) - { - {|#0:M2()|}; // Platform checks:'IsOSPlatformOrLater;Windows;1' - } - - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 1, 1)) - { - {|#1:M2()|}; // Platform checks:'IsOSPlatformEarlierThan;Windows;1.1' - } - - {|#2:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments("IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("IsOSPlatformEarlierThan;Windows;1.1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("")); - } - - [Fact] - public async Task SimpleIfElseTest() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1)) - { - {|#1:M2()|}; // Platform checks:'IsOSPlatformOrLater;Windows;1' - } - else - { - {|#2:M2()|}; // Platform checks:'!IsOSPlatformOrLater;Windows;1' - } - - {|#3:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("!IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("")); - } - - [Fact] - public async Task SimpleIfElseIfElseTest() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1)) - { - {|#1:M2()|}; // Platform checks:'IsOSPlatformOrLater;Windows;1' - } - else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 1, 1)) - { - {|#2:M2()|}; // Platform checks:'!IsOSPlatformOrLater;Windows;1 && IsOSPlatformEarlierThan;Linux;1.1' - } - else - { - {|#3:M2()|}; // Platform checks:'!IsOSPlatformEarlierThan;Linux;1.1 && !IsOSPlatformOrLater;Windows;1' - } - - {|#4:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("!IsOSPlatformOrLater;Windows;1 && IsOSPlatformEarlierThan;Linux;1.1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("!IsOSPlatformEarlierThan;Linux;1.1 && !IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(4).WithArguments("")); - } - - [Fact] - public async Task SimpleIfTestWithNegation() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1)) - { - {|#1:M2()|}; // Platform checks:'!IsOSPlatformOrLater;Windows;1' - } - - if(!RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 1, 1)) - { - {|#2:M2()|}; // Platform checks:'!IsOSPlatformEarlierThan;Windows;1.1' - } - - {|#3:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("!IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("!IsOSPlatformEarlierThan;Windows;1.1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("")); - } - - [Fact] - public async Task SimpleIfElseTestWithNegation() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1)) - { - {|#1:M2()|}; // Platform checks:'!IsOSPlatformOrLater;Windows;1' - } - else - { - {|#2:M2()|}; // Platform checks:'IsOSPlatformOrLater;Windows;1' - } - - {|#3:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("!IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("")); - } - - [Fact] - public async Task SimpleIfElseIfElseTestWithNegation() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1)) - { - {|#1:M2()|}; // Platform checks:'!IsOSPlatformOrLater;Windows;1' - } - else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 1, 1)) - { - {|#2:M2()|}; // Platform checks:'IsOSPlatformEarlierThan;Linux;1.1 && IsOSPlatformOrLater;Windows;1' - } - else - { - {|#3:M2()|}; // Platform checks:'!IsOSPlatformEarlierThan;Linux;1.1 && IsOSPlatformOrLater;Windows;1' - } - - {|#4:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("!IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("IsOSPlatformEarlierThan;Linux;1.1 && IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("!IsOSPlatformEarlierThan;Linux;1.1 && IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(4).WithArguments("")); - } - - [Fact] - public async Task SimpleIfTestWithLogicalAnd() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1) && - RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 2)) - { - {|#1:M2()|}; // Platform checks:'IsOSPlatformEarlierThan;Windows;2 && IsOSPlatformOrLater;Windows;1' - } - - {|#2:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("IsOSPlatformEarlierThan;Windows;2 && IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("")); - } - - [Fact] - public async Task SimpleIfElseTestWithLogicalAnd() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1) && - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1)) - { - {|#1:M2()|}; // Platform checks:'IsOSPlatformOrLater;Linux;1 && IsOSPlatformOrLater;Windows;1' - } - else - { - {|#2:M2()|}; // Platform checks:'' - } - - {|#3:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("IsOSPlatformOrLater;Linux;1 && IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("")); - } - - [Fact] - public async Task SimpleIfTestWithLogicalOr() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1)) - { - {|#1:M2()|}; // Platform checks:'(!IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Linux;1 || IsOSPlatformOrLater;Windows;1)' - } - - {|#2:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("(!IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Linux;1 || IsOSPlatformOrLater;Windows;1)"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("")); - } - - [Fact] - public async Task SimpleIfElseTestWithLogicalOr() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1)) - { - {|#1:M2()|}; // Platform checks:'(!IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Linux;1 || IsOSPlatformOrLater;Windows;1)' - } - else - { - {|#2:M2()|}; // Platform checks:'!IsOSPlatformOrLater;Linux;1 && !IsOSPlatformOrLater;Windows;1' - } - - {|#3:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("(!IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Linux;1 || IsOSPlatformOrLater;Windows;1)"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("!IsOSPlatformOrLater;Linux;1 && !IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("")); - } - - [Fact] - public async Task SimpleIfElseIfElseTestWithLogicalOr() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1)) - { - {|#1:M2()|}; // Platform checks:'(!IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Linux;1 || IsOSPlatformOrLater;Windows;1)' - } - else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 3)) - { - {|#2:M2()|}; // Platform checks:'!IsOSPlatformOrLater;Linux;1 && !IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Windows;3' - } - else - { - {|#3:M2()|}; // Platform checks:'!IsOSPlatformOrLater;Linux;1 && !IsOSPlatformOrLater;Windows;1 && !IsOSPlatformOrLater;Windows;3' - } - - {|#4:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("(!IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Linux;1 || IsOSPlatformOrLater;Windows;1)"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("!IsOSPlatformOrLater;Linux;1 && !IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Windows;3"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("!IsOSPlatformOrLater;Linux;1 && !IsOSPlatformOrLater;Windows;1 && !IsOSPlatformOrLater;Windows;3"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(4).WithArguments("")); - } - - [Fact] - public async Task SimpleIfElseIfTestWithLogicalOr_02() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if((RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1)) && - (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 2) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 2))) - { - {|#1:M2()|}; // Platform checks:'((!IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Linux;1 || IsOSPlatformOrLater;Windows;1) && !IsOSPlatformOrLater;Windows;2 && IsOSPlatformOrLater;Linux;2 || (!IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Linux;1 || IsOSPlatformOrLater;Windows;1) && IsOSPlatformOrLater;Windows;2)' - } - else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 3) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 3) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 4)) - { - {|#2:M2()|}; // Platform checks:'(!IsOSPlatformOrLater;Linux;3 && !IsOSPlatformOrLater;Windows;3 && IsOSPlatformOrLater;Linux;4 || (!IsOSPlatformOrLater;Windows;3 && IsOSPlatformOrLater;Linux;3 || IsOSPlatformOrLater;Windows;3))' - } - - {|#3:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("((!IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Linux;1 || IsOSPlatformOrLater;Windows;1) && !IsOSPlatformOrLater;Windows;2 && IsOSPlatformOrLater;Linux;2 || (!IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Linux;1 || IsOSPlatformOrLater;Windows;1) && IsOSPlatformOrLater;Windows;2)"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("(!IsOSPlatformOrLater;Linux;3 && !IsOSPlatformOrLater;Windows;3 && IsOSPlatformOrLater;Linux;4 || (!IsOSPlatformOrLater;Windows;3 && IsOSPlatformOrLater;Linux;3 || IsOSPlatformOrLater;Windows;3))"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("")); - } - - [Fact] - public async Task ControlFlowAndMultipleChecks() - { - var source = @" -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1)) - { - {|#1:M2()|}; // Platform checks:'IsOSPlatformOrLater;Linux;1' - - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 2, 0)) - { - {|#2:M2()|}; // Platform checks:'IsOSPlatformOrLater;Linux;1 && IsOSPlatformOrLater;Linux;2' - } - else if (!RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 3, 1, 1)) - { - {|#3:M2()|}; // Platform checks:'!IsOSPlatformEarlierThan;Windows;3.1.1 && !IsOSPlatformOrLater;Linux;2 && IsOSPlatformOrLater;Linux;1' - } - - {|#4:M2()|}; // Platform checks:'IsOSPlatformOrLater;Linux;1' - } - else - { - {|#5:M2()|}; // Platform checks:'!IsOSPlatformOrLater;Linux;1' - } - - {|#6:M2()|}; // Platform checks:'' - - if ({|#7:IsWindows3OrLater()|}) // Platform checks:'' - { - {|#8:M2()|}; // Platform checks:'' - } - - {|#9:M2()|}; // Platform checks:'' - } - - void M2() - { - } - - bool IsWindows3OrLater() - { - return RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 3, 0, 0, 0); - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("IsOSPlatformOrLater;Linux;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("IsOSPlatformOrLater;Linux;1 && IsOSPlatformOrLater;Linux;2"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("!IsOSPlatformEarlierThan;Windows;3.1.1 && !IsOSPlatformOrLater;Linux;2 && IsOSPlatformOrLater;Linux;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(4).WithArguments("IsOSPlatformOrLater;Linux;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(5).WithArguments("!IsOSPlatformOrLater;Linux;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(6).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(7).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(8).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(9).WithArguments("")); - } - - [Fact] - public async Task DebugAssertAnalysisTest() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - {|#1:Debug.Assert(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 3, 0, 0, 0))|}; // Platform checks:'IsOSPlatformOrLater;Windows;3' - - {|#2:M2()|}; // Platform checks:'IsOSPlatformOrLater;Windows;3' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("IsOSPlatformOrLater;Windows;3"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("IsOSPlatformOrLater;Windows;3")); - } - - [Fact] - public async Task ResultSavedInLocal() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - var x1 = RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1); - var x2 = RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1); - if (x1 || x2) - { - {|#1:M2()|}; // Platform checks:'(!IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Linux;1 || IsOSPlatformOrLater;Windows;1)' - } - - {|#2:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("(!IsOSPlatformOrLater;Windows;1 && IsOSPlatformOrLater;Linux;1 || IsOSPlatformOrLater;Windows;1)"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("")); - } - - [Fact] - public async Task VersionSavedInLocal() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - var v1 = 1; - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, v1)) - { - {|#1:M2()|}; // Platform checks:'IsOSPlatformOrLater;Windows;1' - } - - {|#2:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("")); - } - - [Fact] - public async Task PlatformSavedInLocal_NotYetSupported() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - var platform = OSPlatform.Windows; - if (RuntimeInformationHelper.IsOSPlatformOrLater(platform, 1)) - { - {|#1:M2()|}; // Platform checks:'' - } - - {|#2:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("")); - } - - [Fact] - public async Task UnrelatedConditionCheckDoesNotInvalidateState() - { - var source = @" -using System.Diagnostics; -using System.Runtime.InteropServices; - -class Test -{ - void M1(bool flag1, bool flag2) - { - {|#0:M2()|}; // Platform checks:'' - - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1)) - { - {|#1:M2()|}; // Platform checks:'IsOSPlatformOrLater;Windows;1' - - if (flag1 || flag2) - { - {|#2:M2()|}; // Platform checks:'IsOSPlatformOrLater;Windows;1' - } - else - { - {|#3:M2()|}; // Platform checks:'IsOSPlatformOrLater;Windows;1' - } - - {|#4:M2()|}; // Platform checks:'IsOSPlatformOrLater;Windows;1' - } - - {|#5:M2()|}; // Platform checks:'' - } - - void M2() - { - } -}" + PlatformCheckApiSource; - - await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments("IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments("IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(4).WithArguments("IsOSPlatformOrLater;Windows;1"), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(5).WithArguments("")); - } - - [Theory] - [InlineData("")] - [InlineData("dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive")] - public async Task InterproceduralAnalysisTest(string editorconfig) - { - var source = @" -using System.Runtime.InteropServices; - -class Test -{ - void M1() - { - {|#0:M2()|}; // Platform checks:'' - - if ({|#1:IsWindows3OrLater()|}) // Platform checks:'' - { - {|#2:M2()|}; // Platform checks:'IsOSPlatformOrLater;Windows;3' - } - - {|#3:M2()|}; // Platform checks:'' - } - - void M2() - { - } - - bool IsWindows3OrLater() - { - return RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 3, 0, 0, 0); - } -}" + PlatformCheckApiSource; - - var test = new VerifyCS.Test - { - TestState = - { - Sources = { source }, - AdditionalFiles = { (".editorconfig", editorconfig) } - } - }; - - var argForInterprocDiagnostics = editorconfig.Length == 0 ? "" : "IsOSPlatformOrLater;Windows;3"; - test.ExpectedDiagnostics.AddRange(new[] - { - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(0).WithArguments(""), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(1).WithArguments(argForInterprocDiagnostics), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(2).WithArguments(argForInterprocDiagnostics), - VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer.Rule).WithLocation(3).WithArguments("") - }); - - await test.RunAsync(); - } - } -} diff --git a/src/Utilities/Compiler/Extensions/IOperationExtensions.cs b/src/Utilities/Compiler/Extensions/IOperationExtensions.cs index 775c10dfb1..f77f082a1b 100644 --- a/src/Utilities/Compiler/Extensions/IOperationExtensions.cs +++ b/src/Utilities/Compiler/Extensions/IOperationExtensions.cs @@ -508,6 +508,27 @@ public static bool IsWithinLambdaOrLocalFunction(this IOperation operation) => operation.GetAncestor(OperationKind.AnonymousFunction) != null || operation.GetAncestor(OperationKind.LocalFunction) != null; + public static bool TryGetContainingLocalOrLambdaFunctionSymbol(this IOperation operation, [NotNullWhen(true)] out IMethodSymbol? containingSymbol) + { + var localOperation = operation.GetAncestor(OperationKind.LocalFunction); + if (localOperation != null) + { + containingSymbol = localOperation.Symbol; + return true; + } + else + { + var anonymousOperation = operation.GetAncestor(OperationKind.AnonymousFunction); + if (anonymousOperation != null) + { + containingSymbol = anonymousOperation.Symbol; + return true; + } + } + containingSymbol = null; + return false; + } + public static ITypeSymbol? GetPatternType(this IPatternOperation pattern) { return pattern switch diff --git a/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs b/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs index 8fb77f80bf..b3b4a77421 100644 --- a/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs +++ b/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs @@ -135,6 +135,19 @@ public TAbstractAnalysisValue this[IOperation operation] return null; } + internal DataFlowAnalysisResult? TryGetInterproceduralResultByDefinition(IMethodSymbol symbol) + { + foreach (var kvp in _interproceduralResultsMap) + { + if (kvp.Key is IInvocationOperation iOperation && iOperation.TargetMethod.OriginalDefinition.Equals(symbol)) + { + return (DataFlowAnalysisResult)kvp.Value; + } + } + + return null; + } + public ControlFlowGraph ControlFlowGraph { get; } public (TAbstractAnalysisValue Value, PredicateValueKind PredicateValueKind)? ReturnValueAndPredicateKindOpt { get; } public TBlockAnalysisResult EntryBlockOutput { get; } From 473ce5c8b4fdc544e7e27be155c6d82fc2dbb24f Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Wed, 29 Jul 2020 15:06:40 -0700 Subject: [PATCH 12/48] Support platform name without version --- .../PlatformCompatabilityAnalyzer.Data.cs | 30 ++++++++++++------- .../PlatformCompatabilityAnalyzerTests.cs | 5 ++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index e60863bc9e..1705e04b4a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -52,8 +52,9 @@ public static bool TryParsePlatformAttributeInfo(AttributeData osAttribute, out break; } - if (osAttribute.ConstructorArguments.Length == 1 && osAttribute.ConstructorArguments[0].Type.SpecialType == SpecialType.System_String && - !osAttribute.ConstructorArguments[0].IsNull && TryParsePlatformNameAndVersion(osAttribute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version)) + if (!osAttribute.ConstructorArguments.IsEmpty && osAttribute.ConstructorArguments[0].Type.SpecialType == SpecialType.System_String && + !osAttribute.ConstructorArguments[0].IsNull && !osAttribute.ConstructorArguments[0].Value.Equals(string.Empty) && + TryParsePlatformNameAndVersion(osAttribute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version)) { parsedAttribute.PlatformName = platformName; parsedAttribute.Version = version; @@ -90,20 +91,29 @@ private static bool TryParsePlatformNameAndVersion(string osString, out string o { if (char.IsDigit(osString[i])) { - if (i > 0 && Version.TryParse(osString.Substring(i), out Version? parsedVersion)) + try { - osPlatformName = osString.Substring(0, i); - version = parsedVersion; - return true; + if (i > 0 && Version.TryParse(osString.Substring(i), out Version? parsedVersion)) + { + osPlatformName = osString.Substring(0, i); + version = parsedVersion; + return true; + } + else + { + return false; + } } - else + catch (ArgumentException) { - break; + // When version string was not valid we don't want to do furter diagnostics + return false; } } } - - return false; + osPlatformName = osString; + version = new Version(0, 0); + return true; } private struct RuntimeMethodValue : IAbstractAnalysisValue, IEquatable diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index 21bf0c5cd7..713a644831 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -89,8 +89,13 @@ public void M1() [|RemovedLinu4_1()|]; ObsoletedWithNullString(); RemovedWithEmptyString(); + [|WindowsOnly()|]; } + [MinimumOSPlatform(""Windows"")] + public void WindowsOnly() + { + } [MinimumOSPlatform(""Windows10"")] public void Windows10() { From 6ae011e74dbb07ad0dbf00c4ad181d1c45d97953 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Wed, 29 Jul 2020 22:46:51 -0700 Subject: [PATCH 13/48] Apply some review comments --- .../Core/AnalyzerReleases.Unshipped.md | 2 +- .../PlatformCompatabilityAnalyzer.Data.cs | 228 +++--------------- .../PlatformCompatabilityAnalyzer.Value.cs | 179 ++++++++++++++ .../PlatformCompatabilityAnalyzer.cs | 113 +++------ .../MicrosoftNetCoreAnalyzersResources.resx | 2 +- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 4 +- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 4 +- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 4 +- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 4 +- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 4 +- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 4 +- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 4 +- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 4 +- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 4 +- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 4 +- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 4 +- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 4 +- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 4 +- ...CompatabilityAnalyzerTests.GuardedCalls.cs | 6 +- .../PlatformCompatabilityAnalyzerTests.cs | 21 +- 20 files changed, 284 insertions(+), 319 deletions(-) create mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index 8083f4cf17..8fc2e7d519 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -10,7 +10,7 @@ CA1047 | Design | Info | DoNotDeclareProtectedMembersInSealedTypes, [Documentati CA1069 | Design | Info | EnumShouldNotHaveDuplicatedValues, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1069) CA1070 | Design | Info | DoNotDeclareEventFieldsAsVirtual, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1070) 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) +CA1417 | Interoperability | Warning | DoNotUseOutAttributeStringPInvokeParametersAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1417) CA1700 | Naming | Disabled | DoNotNameEnumValuesReserved, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1700) CA1713 | Naming | Disabled | EventsShouldNotHaveBeforeOrAfterPrefix, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1713) CA1805 | Performance | Info | DoNotInitializeUnnecessarilyAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1805) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index 1705e04b4a..1c06d9b25f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -1,69 +1,61 @@ // 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 PlatformCompatabilityAnalyzer { private enum PlatformAttributeType { - None, MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute, TargetPlatformAttribute + None, + MinimumOSPlatformAttribute, + ObsoletedInOSPlatformAttribute, + RemovedInOSPlatformAttribute, + TargetPlatformAttribute } - private struct PlatformAttributeInfo : IEquatable + private readonly struct PlatformAttributeInfo : IEquatable { - public PlatformAttributeType AttributeType { get; set; } - public string PlatformName { get; set; } - public Version Version { get; set; } + public PlatformAttributeType AttributeType { get; } + public string PlatformName { get; } + public Version Version { get; } - public static bool TryParsePlatformAttributeInfo(AttributeData osAttribute, out PlatformAttributeInfo parsedAttribute) + private PlatformAttributeInfo(PlatformAttributeType attributeType, string platformName, Version version) { - parsedAttribute = new PlatformAttributeInfo(); - switch (osAttribute.AttributeClass.Name) - { - case MinimumOSPlatformAttribute: - parsedAttribute.AttributeType = PlatformAttributeType.MinimumOSPlatformAttribute; - break; - case ObsoletedInOSPlatformAttribute: - parsedAttribute.AttributeType = PlatformAttributeType.ObsoletedInOSPlatformAttribute; - break; - case RemovedInOSPlatformAttribute: - parsedAttribute.AttributeType = PlatformAttributeType.RemovedInOSPlatformAttribute; - break; - case TargetPlatformAttribute: - parsedAttribute.AttributeType = PlatformAttributeType.TargetPlatformAttribute; - break; - default: - parsedAttribute.AttributeType = PlatformAttributeType.None; - break; - } + AttributeType = attributeType; + PlatformName = platformName; + Version = version; + } - if (!osAttribute.ConstructorArguments.IsEmpty && osAttribute.ConstructorArguments[0].Type.SpecialType == SpecialType.System_String && - !osAttribute.ConstructorArguments[0].IsNull && !osAttribute.ConstructorArguments[0].Value.Equals(string.Empty) && + public static bool TryParsePlatformAttributeInfo(AttributeData osAttribute, out PlatformAttributeInfo parsedAttribute) + { + if (!osAttribute.ConstructorArguments.IsEmpty && + osAttribute.ConstructorArguments[0].Type.SpecialType == SpecialType.System_String && + !osAttribute.ConstructorArguments[0].IsNull && + !osAttribute.ConstructorArguments[0].Value.Equals(string.Empty) && TryParsePlatformNameAndVersion(osAttribute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version)) { - parsedAttribute.PlatformName = platformName; - parsedAttribute.Version = version; + parsedAttribute = new PlatformAttributeInfo(SwitchAttrributeType(osAttribute.AttributeClass.Name), platformName, version); return true; } - + parsedAttribute = default; return false; } + private static PlatformAttributeType SwitchAttrributeType(string osAttributeName) + => osAttributeName switch + { + MinimumOSPlatformAttribute => PlatformAttributeType.MinimumOSPlatformAttribute, + ObsoletedInOSPlatformAttribute => PlatformAttributeType.ObsoletedInOSPlatformAttribute, + RemovedInOSPlatformAttribute => PlatformAttributeType.RemovedInOSPlatformAttribute, + TargetPlatformAttribute => PlatformAttributeType.TargetPlatformAttribute, + _ => PlatformAttributeType.None, + }; + public override bool Equals(object obj) { if (obj is PlatformAttributeInfo info) @@ -115,161 +107,5 @@ private static bool TryParsePlatformNameAndVersion(string osString, out string o version = new Version(0, 0); return true; } - - private struct RuntimeMethodValue : IAbstractAnalysisValue, IEquatable - { - private RuntimeMethodValue(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 RuntimeMethodValue(InvokedPlatformCheckMethodName, PlatformPropertyName, Version, !Negated); - - public static bool TryDecode( - IMethodSymbol invokedPlatformCheckMethod, - ImmutableArray arguments, - ValueContentAnalysisResult? valueContentAnalysisResult, - INamedTypeSymbol osPlatformType, - [NotNullWhen(returnValue: true)] out RuntimeMethodValue? info) - { - Debug.Assert(!arguments.IsEmpty); - - if (arguments[0].Value is ILiteralOperation literal && literal.Type.SpecialType == SpecialType.System_String) - { - if (literal.ConstantValue.HasValue && TryParsePlatformNameAndVersion(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) - { - info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); - return true; - } - } - - if (!TryDecodeOSPlatform(arguments, osPlatformType, out var osPlatformName) || - !TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var osVersion)) - { - // Bail out - info = default; - return false; - } - - info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, osPlatformName, osVersion, negated: false); - return true; - } - - private static bool TryDecodeOSPlatform( - ImmutableArray arguments, - INamedTypeSymbol osPlatformType, - [NotNullWhen(returnValue: true)] out string? osPlatformName) - { - return TryDecodeOSPlatform(arguments[0].Value, osPlatformType, out osPlatformName); - } - - private static bool TryDecodeOSPlatform( - 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) - { - 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 result = $"{InvokedPlatformCheckMethodName};{PlatformPropertyName};{Version}"; - if (Negated) - { - result = $"!{result}"; - } - - return result; - } - - public bool Equals(RuntimeMethodValue 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 RuntimeMethodValue otherInfo && Equals(otherInfo); - - public override int GetHashCode() - => HashUtilities.Combine(InvokedPlatformCheckMethodName.GetHashCode(), PlatformPropertyName.GetHashCode(), Version.GetHashCode(), Negated.GetHashCode()); - - bool IEquatable.Equals(IAbstractAnalysisValue other) - => other is RuntimeMethodValue otherInfo && Equals(otherInfo); - - public static bool operator ==(RuntimeMethodValue left, RuntimeMethodValue right) - { - return left.Equals(right); - } - - public static bool operator !=(RuntimeMethodValue left, RuntimeMethodValue right) - { - return !(left == right); - } - } } } 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..1e89a561e3 --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -0,0 +1,179 @@ +// 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 Analyzer.Utilities.PooledObjects; +using System.Linq; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; +using Analyzer.Utilities; + +namespace Microsoft.NetCore.Analyzers.InteropServices +{ + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + public sealed partial class PlatformCompatabilityAnalyzer + { + private readonly struct RuntimeMethodValue : IAbstractAnalysisValue, IEquatable + { + private RuntimeMethodValue(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 RuntimeMethodValue(InvokedPlatformCheckMethodName, PlatformPropertyName, Version, !Negated); + + public static bool TryDecode( + IMethodSymbol invokedPlatformCheckMethod, + ImmutableArray arguments, + ValueContentAnalysisResult? valueContentAnalysisResult, + INamedTypeSymbol osPlatformType, + [NotNullWhen(returnValue: true)] out RuntimeMethodValue? info) + { + Debug.Assert(!arguments.IsEmpty); + + if (arguments[0].Value is ILiteralOperation literal && literal.Type?.SpecialType == SpecialType.System_String) + { + if (literal.ConstantValue.HasValue && + TryParsePlatformNameAndVersion(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) + { + info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); + return true; + } + } + + if (!TryDecodeOSPlatform(arguments, osPlatformType, out var osPlatformName) || + !TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var osVersion)) + { + // Bail out + info = default; + return false; + } + + info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, osPlatformName, osVersion, negated: false); + return true; + } + + private static bool TryDecodeOSPlatform( + ImmutableArray arguments, + INamedTypeSymbol osPlatformType, + [NotNullWhen(returnValue: true)] out string? osPlatformName) + { + return TryDecodeOSPlatform(arguments[0].Value, osPlatformType, out osPlatformName); + } + + private static bool TryDecodeOSPlatform( + 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) + { + 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 result = $"{InvokedPlatformCheckMethodName};{PlatformPropertyName};{Version}"; + if (Negated) + { + result = $"!{result}"; + } + + return result; + } + + public bool Equals(RuntimeMethodValue 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 RuntimeMethodValue otherInfo && Equals(otherInfo); + + public override int GetHashCode() + => HashUtilities.Combine(InvokedPlatformCheckMethodName.GetHashCode(), PlatformPropertyName.GetHashCode(), Version.GetHashCode(), Negated.GetHashCode()); + + bool IEquatable.Equals(IAbstractAnalysisValue other) + => other is RuntimeMethodValue otherInfo && Equals(otherInfo); + + public static bool operator ==(RuntimeMethodValue left, RuntimeMethodValue right) + { + return left.Equals(right); + } + + public static bool operator !=(RuntimeMethodValue left, RuntimeMethodValue 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 index fa935d7369..fc5b722c5a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Analyzer.Utilities.PooledObjects; @@ -31,8 +32,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private const string ObsoletedInOSPlatformAttribute = nameof(ObsoletedInOSPlatformAttribute); private const string RemovedInOSPlatformAttribute = nameof(RemovedInOSPlatformAttribute); private const string TargetPlatformAttribute = nameof(TargetPlatformAttribute); - private const string Windows = nameof(Windows); - private const string OSX = nameof(OSX); + private const string OSX = nameof(OSPlatform.OSX); private const string macOS = nameof(macOS); internal static DiagnosticDescriptor MinimumOsRule = DiagnosticDescriptorHelper.Create(RuleId, @@ -98,9 +98,9 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, context.RegisterOperationAction(context => { - AnalyzeInvocationOperation(context.Operation, context, ref platformSpecificOperations); + AnalyzeOperation(context.Operation, context, platformSpecificOperations); } - , OperationKind.DelegateCreation, OperationKind.EventReference, OperationKind.FieldReference, + , OperationKind.MethodReference, OperationKind.EventReference, OperationKind.FieldReference, OperationKind.Invocation, OperationKind.ObjectCreation, OperationKind.PropertyReference); context.RegisterOperationBlockEndAction(context => @@ -126,7 +126,6 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, if (analysisResult == null) { - ReportDiagnosticsForAll(platformSpecificOperations, context); return; } @@ -219,73 +218,32 @@ private static void ReportDiagnosticsForAll(PooledDictionary> platformSpecificOperations) + private static ISymbol? GetOperationSymbol(IOperation operation) + => operation switch + { + IInvocationOperation iOperation => iOperation.TargetMethod, + IObjectCreationOperation cOperation => cOperation.Constructor, + IFieldReferenceOperation fOperation => IsWithinConditionalOperation(operation) ? null : fOperation.Field, + IMemberReferenceOperation mOperation => mOperation.Member, + _ => null, + }; + + private static void AnalyzeOperation(IOperation operation, OperationAnalysisContext context, + PooledDictionary> platformSpecificOperations) { using var builder = ArrayBuilder.GetInstance(); - AttributeData[]? attributes = null; + var symbol = GetOperationSymbol(operation); - if (operation is IInvocationOperation iOperation) - { - attributes = FindAllPlatformAttributesApplied(iOperation.TargetMethod.GetAttributes(), iOperation.TargetMethod.ContainingType); - } - else if (operation is IObjectCreationOperation cOperation) + if (symbol != null) { - attributes = FindAllPlatformAttributesApplied(cOperation.Constructor.GetAttributes(), cOperation.Constructor.ContainingType); - } - else if (operation is IPropertyReferenceOperation pOperation) - { - attributes = FindAllPlatformAttributesApplied(pOperation.Property.GetAttributes(), pOperation.Property.ContainingType); - } - else if (operation is IFieldReferenceOperation fOperation) - { - if (!IsWithinConditionalOperation(operation)) - attributes = FindAllPlatformAttributesApplied(fOperation.Field.GetAttributes(), fOperation.Field.ContainingType); - } - else if (operation is IDelegateCreationOperation dOperation) - { - if (dOperation.Target is IMethodReferenceOperation mrOperation) - attributes = FindAllPlatformAttributesApplied(mrOperation.Method.GetAttributes(), mrOperation.Method.ContainingType); - } - else if (operation is IEventReferenceOperation eOperation) - { - attributes = FindAllPlatformAttributesApplied(eOperation.Event.GetAttributes(), eOperation.Event.ContainingType); - } + var attributes = FindAllPlatformAttributesApplied(symbol.GetAttributes(), symbol.ContainingType); - if (attributes != null) - { foreach (AttributeData attribute in attributes) { if (PlatformAttributeInfo.TryParsePlatformAttributeInfo(attribute, out PlatformAttributeInfo parsedAttribute)) @@ -328,23 +286,19 @@ private static bool IsOSPlatformsEqual(string left, string right) } } - private static string NormalizeOSName(string name) - { - if (name.Equals(OSX, StringComparison.OrdinalIgnoreCase)) - return macOS; - return name; - } + private static string NormalizeOSName(string name) => name.Equals(OSX, StringComparison.OrdinalIgnoreCase) + ? macOS + : name; private static DiagnosticDescriptor SelectRule(PlatformAttributeType attributeType) - { - if (attributeType == PlatformAttributeType.MinimumOSPlatformAttribute) - return MinimumOsRule; - if (attributeType == PlatformAttributeType.ObsoletedInOSPlatformAttribute) - return ObsoleteRule; - return RemovedRule; - } + => attributeType switch + { + PlatformAttributeType.MinimumOSPlatformAttribute => MinimumOsRule, + PlatformAttributeType.ObsoletedInOSPlatformAttribute => ObsoleteRule, + _ => RemovedRule, + }; - private static AttributeData[] FindAllPlatformAttributesApplied(ImmutableArray immediateAttributes, INamedTypeSymbol parent) + private static ImmutableArray FindAllPlatformAttributesApplied(ImmutableArray immediateAttributes, INamedTypeSymbol parent) { using var builder = ArrayBuilder.GetInstance(); foreach (AttributeData attribute in immediateAttributes) @@ -368,7 +322,8 @@ private static AttributeData[] FindAllPlatformAttributesApplied(ImmutableArray'{0}' requires {1} {2} version or later. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. '{0}' has been removed since {1} {2} version. 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 20ce2451f2..8a6be9b6e1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1468,8 +1468,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. 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 57318ccb94..16a6fe4222 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1468,8 +1468,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. 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 eb98f49c00..a3aa8bfda5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1468,8 +1468,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. 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 55b9549e87..38cdd2c065 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1468,8 +1468,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. 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 88f17a357f..adfc4c21a1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1468,8 +1468,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. 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 3e17482c0a..9978f3c498 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1468,8 +1468,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. 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 05de2acf3c..ea491b624e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1468,8 +1468,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. 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 0ddb01fb44..2795ab235d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1469,8 +1469,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. 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 564fb3649c..4ccad506bc 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 @@ -1468,8 +1468,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. 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 19dac0926d..9f56f9833a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1468,8 +1468,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. 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 3e052240a2..6a0db53782 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1468,8 +1468,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. 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 2330b8d56b..87c82fa812 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 @@ -1468,8 +1468,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. 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 97fe83c42e..34931702ae 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 @@ -1468,8 +1468,8 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version. diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs index f02cc7939f..6f70306084 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs @@ -1,7 +1,5 @@ // 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.Runtime.InteropServices; using System.Threading.Tasks; using Xunit; @@ -477,7 +475,7 @@ public void M2() { } } -" + MockAttributesSource + MockRuntimeApiSource; +" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); }*/ @@ -559,7 +557,7 @@ public void M1() } } } -" + MockAttributesSource + MockRuntimeApiSource; +" + MockAttributesCsSource + MockRuntimeApiSource; await VerifyAnalyzerAsyncCs(source); }*/ diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index 713a644831..d89407f1c9 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -868,28 +868,23 @@ await VerifyAnalyzerAsyncCs(source, } } - private static VerifyCS.Test PopulateTestCs(string sourceCode) + private static VerifyCS.Test PopulateTestCs(string sourceCode, params DiagnosticResult[] expected) { - return new VerifyCS.Test + var test = new VerifyCS.Test { TestCode = sourceCode, ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp50, MarkupOptions = MarkupOptions.UseFirstDescriptor, TestState = { } }; + test.ExpectedDiagnostics.AddRange(expected); + return test; } private static async Task VerifyAnalyzerAsyncCs(string sourceCode) => await PopulateTestCs(sourceCode).RunAsync(); private static async Task VerifyAnalyzerAsyncCs(string sourceCode, params DiagnosticResult[] expectedDiagnostics) - { - var test = PopulateTestCs(sourceCode); - foreach (DiagnosticResult expectedDiagnostic in expectedDiagnostics) - { - test.ExpectedDiagnostics.Add(expectedDiagnostic); - } - await test.RunAsync(); - } + => await PopulateTestCs(sourceCode, expectedDiagnostics).RunAsync(); private static async Task VerifyAnalyzerAsyncCs(string sourceCode, string additionalFiles) { @@ -900,14 +895,16 @@ private static async Task VerifyAnalyzerAsyncCs(string sourceCode, string additi private static async Task VerifyAnalyzerAsyncVb(string sourceCode) => await PopulateTestVb(sourceCode).RunAsync(); - private static VerifyVB.Test PopulateTestVb(string sourceCode) + private static VerifyVB.Test PopulateTestVb(string sourceCode, params DiagnosticResult[] expected) { - return new VerifyVB.Test + var test = new VerifyVB.Test { TestCode = sourceCode, ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp50, MarkupOptions = MarkupOptions.UseFirstDescriptor, }; + test.ExpectedDiagnostics.AddRange(expected); + return test; } private readonly string MockAttributesCsSource = @" From 9a31b5e5da6d455dff26b035943fb3af59948df5 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 30 Jul 2020 16:53:41 -0700 Subject: [PATCH 14/48] Address more feedback --- .../PlatformCompatabilityAnalyzer.Data.cs | 31 +++----- .../PlatformCompatabilityAnalyzer.cs | 75 ++++++++++--------- .../MicrosoftNetCoreAnalyzersResources.resx | 6 +- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 12 +-- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 12 +-- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 12 +-- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 12 +-- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 12 +-- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 12 +-- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 12 +-- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 12 +-- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 12 +-- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 12 +-- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 12 +-- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 12 +-- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 12 +-- .../PlatformCompatabilityAnalyzerTests.cs | 75 +++++++++++++++++++ 17 files changed, 209 insertions(+), 134 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index 1c06d9b25f..c7e23aa2bd 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -11,11 +11,10 @@ public sealed partial class PlatformCompatabilityAnalyzer { private enum PlatformAttributeType { - None, + TargetPlatformAttribute, MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute, - TargetPlatformAttribute } private readonly struct PlatformAttributeInfo : IEquatable @@ -34,9 +33,11 @@ private PlatformAttributeInfo(PlatformAttributeType attributeType, string platfo public static bool TryParsePlatformAttributeInfo(AttributeData osAttribute, out PlatformAttributeInfo parsedAttribute) { if (!osAttribute.ConstructorArguments.IsEmpty && - osAttribute.ConstructorArguments[0].Type.SpecialType == SpecialType.System_String && - !osAttribute.ConstructorArguments[0].IsNull && - !osAttribute.ConstructorArguments[0].Value.Equals(string.Empty) && + osAttribute.ConstructorArguments[0] is { } argument && + argument.Kind == TypedConstantKind.Primitive && + argument.Type.SpecialType == SpecialType.System_String && + !argument.IsNull && + !argument.Value.Equals(string.Empty) && TryParsePlatformNameAndVersion(osAttribute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version)) { parsedAttribute = new PlatformAttributeInfo(SwitchAttrributeType(osAttribute.AttributeClass.Name), platformName, version); @@ -53,7 +54,7 @@ private static PlatformAttributeType SwitchAttrributeType(string osAttributeName ObsoletedInOSPlatformAttribute => PlatformAttributeType.ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute => PlatformAttributeType.RemovedInOSPlatformAttribute, TargetPlatformAttribute => PlatformAttributeType.TargetPlatformAttribute, - _ => PlatformAttributeType.None, + _ => throw new NotImplementedException(), }; public override bool Equals(object obj) @@ -83,22 +84,14 @@ private static bool TryParsePlatformNameAndVersion(string osString, out string o { if (char.IsDigit(osString[i])) { - try + if (i > 0 && Version.TryParse(osString.Substring(i), out Version? parsedVersion)) { - if (i > 0 && Version.TryParse(osString.Substring(i), out Version? parsedVersion)) - { - osPlatformName = osString.Substring(0, i); - version = parsedVersion; - return true; - } - else - { - return false; - } + osPlatformName = osString.Substring(0, i); + version = parsedVersion; + return true; } - catch (ArgumentException) + else { - // When version string was not valid we don't want to do furter diagnostics return false; } } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index fc5b722c5a..cb5013cf1b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -19,7 +19,7 @@ namespace Microsoft.NetCore.Analyzers.InteropServices public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA1416"; - private static readonly ImmutableArray s_platformCheckMethodNames = ImmutableArray.Create("IsOSPlatformOrLater", "IsOSPlatformEarlierThan"); + private static readonly ImmutableArray s_platformCheckMethodNames = ImmutableArray.Create(IsOSPlatformOrLater, IsOSPlatformEarlierThan); private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute); private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); @@ -28,12 +28,20 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private static readonly LocalizableString s_localizableRemovedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckRemovedMessage), 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 MinimumOSPlatformAttribute = nameof(MinimumOSPlatformAttribute); private const string ObsoletedInOSPlatformAttribute = nameof(ObsoletedInOSPlatformAttribute); private const string RemovedInOSPlatformAttribute = nameof(RemovedInOSPlatformAttribute); private const string TargetPlatformAttribute = nameof(TargetPlatformAttribute); + + // Platform guard method names + private const string IsOSPlatformOrLater = nameof(IsOSPlatformOrLater); + private const string IsOSPlatformEarlierThan = nameof(IsOSPlatformEarlierThan); + + // Equivalent OS names private const string OSX = nameof(OSPlatform.OSX); - private const string macOS = nameof(macOS); + private const string macOS = nameof(macOS); // TODO use 'OSPlatform.macOS' when new OS is available internal static DiagnosticDescriptor MinimumOsRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, @@ -73,6 +81,7 @@ public override void Initialize(AnalysisContext context) if (!context.Compilation.TryGetOrCreateTypeByMetadataName(typeName + "Helper", out var runtimeInformationType)) { + // TODO: remove 'typeName + "Helper"' load after tests able to use runtimeInformationType = context.Compilation.GetOrCreateTypeByMetadataName(typeName); } if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesOSPlatform, out var osPlatformType)) @@ -184,7 +193,7 @@ private static bool IsKnownValueGuarded(PlatformAttributeInfo attribute, GlobalF { if (IsOSPlatformsEqual(attribute.PlatformName, info.PlatformPropertyName)) { - if (info.InvokedPlatformCheckMethodName == s_platformCheckMethodNames[0]) + if (info.InvokedPlatformCheckMethodName == IsOSPlatformOrLater) { if (attribute.AttributeType == PlatformAttributeType.MinimumOSPlatformAttribute && AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) @@ -229,7 +238,7 @@ private static void ReportDiagnostics(IOperation operation, PlatformAttributeInf { IInvocationOperation iOperation => iOperation.TargetMethod, IObjectCreationOperation cOperation => cOperation.Constructor, - IFieldReferenceOperation fOperation => IsWithinConditionalOperation(operation) ? null : fOperation.Field, + IFieldReferenceOperation fOperation => IsWithinConditionalOperation(fOperation) ? null : fOperation.Field, IMemberReferenceOperation mOperation => mOperation.Member, _ => null, }; @@ -240,18 +249,20 @@ private static void AnalyzeOperation(IOperation operation, OperationAnalysisCont using var builder = ArrayBuilder.GetInstance(); var symbol = GetOperationSymbol(operation); - if (symbol != null) + if (symbol == null) { - var attributes = FindAllPlatformAttributesApplied(symbol.GetAttributes(), symbol.ContainingType); + return; + } - foreach (AttributeData attribute in attributes) + var attributes = FindAllPlatformAttributesApplied(symbol.GetAttributes(), symbol.ContainingSymbol); + + foreach (AttributeData attribute in attributes) + { + if (PlatformAttributeInfo.TryParsePlatformAttributeInfo(attribute, out PlatformAttributeInfo parsedAttribute)) { - if (PlatformAttributeInfo.TryParsePlatformAttributeInfo(attribute, out PlatformAttributeInfo parsedAttribute)) + if (!IsSuppressedByAttribute(parsedAttribute, context.ContainingSymbol)) { - if (!IsSuppressedByAttribute(parsedAttribute, context.ContainingSymbol)) - { - builder.Add(parsedAttribute); - } + builder.Add(parsedAttribute); } } } @@ -262,7 +273,8 @@ private static void AnalyzeOperation(IOperation operation, OperationAnalysisCont } } - private static bool IsWithinConditionalOperation(IOperation pOperation) => + // Do not warn for conditional checks of platfomr specific enum value; 'if (value != FooEnum.WindowsOnlyValue)' + private static bool IsWithinConditionalOperation(IFieldReferenceOperation pOperation) => pOperation.ConstantValue.HasValue && pOperation.Parent is IBinaryOperation bo && (bo.OperatorKind == BinaryOperatorKind.Equals || @@ -298,7 +310,7 @@ private static DiagnosticDescriptor SelectRule(PlatformAttributeType attributeTy _ => RemovedRule, }; - private static ImmutableArray FindAllPlatformAttributesApplied(ImmutableArray immediateAttributes, INamedTypeSymbol parent) + private static ImmutableArray FindAllPlatformAttributesApplied(ImmutableArray immediateAttributes, ISymbol parent) { using var builder = ArrayBuilder.GetInstance(); foreach (AttributeData attribute in immediateAttributes) @@ -314,13 +326,12 @@ private static ImmutableArray FindAllPlatformAttributesApplied(Im var current = parent.GetAttributes(); foreach (var attribute in current) { - if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name) && - TargetPlatformAttribute != attribute.AttributeClass.Name) + if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name)) { builder.Add(attribute); } } - parent = parent.BaseType; + parent = parent.ContainingSymbol; } return builder.ToImmutableArray(); @@ -331,32 +342,28 @@ private static bool IsSuppressedByAttribute(PlatformAttributeInfo diagnosingAttr while (containingSymbol != null) { var attributes = containingSymbol.GetAttributes(); - if (!attributes.IsEmpty) + containingSymbol = containingSymbol.ContainingSymbol; + + if (attributes.IsEmpty) + { + continue; + } + foreach (AttributeData attribute in attributes) { - foreach (AttributeData attribute in attributes) + if (diagnosingAttribute.AttributeType.ToString() == attribute.AttributeClass.Name && + PlatformAttributeInfo.TryParsePlatformAttributeInfo(attribute, out PlatformAttributeInfo parsedAttribute) && + IsOSPlatformsEqual(diagnosingAttribute.PlatformName, parsedAttribute.PlatformName) && + AttributeVersionsMatch(diagnosingAttribute, parsedAttribute)) { - if (diagnosingAttribute.AttributeType.ToString() == attribute.AttributeClass.Name) - { - if (PlatformAttributeInfo.TryParsePlatformAttributeInfo(attribute, out PlatformAttributeInfo parsedAttribute)) - { - if (IsOSPlatformsEqual(diagnosingAttribute.PlatformName, parsedAttribute.PlatformName) && - AttributeVersionsMatch(diagnosingAttribute, parsedAttribute)) - { - return true; - } - } - } + return true; } } - containingSymbol = containingSymbol.ContainingSymbol; } return false; } private static bool AttributeVersionsMatch(PlatformAttributeInfo diagnosingAttribute, PlatformAttributeInfo osAttribute) - { - return AttributeVersionsMatch(diagnosingAttribute.AttributeType, diagnosingAttribute.Version, osAttribute.Version); - } + => AttributeVersionsMatch(diagnosingAttribute.AttributeType, diagnosingAttribute.Version, osAttribute.Version); private static bool AttributeVersionsMatch(PlatformAttributeType attributeType, Version diagnosingVersion, Version suppressingVersion) { diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 0746ac1517..02a9295255 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1420,13 +1420,13 @@ Using platform dependendent API on a component makes the code no longer work across all platforms. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version String parameters passed by value with the 'OutAttribute' can destabilize the runtime if the string is an interned string. 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 8a6be9b6e1..e60cbdd83d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1468,13 +1468,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1483,8 +1483,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later 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 16a6fe4222..015dd51a8a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1468,13 +1468,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1483,8 +1483,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later 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 a3aa8bfda5..df8de2fedf 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1468,13 +1468,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1483,8 +1483,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later 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 38cdd2c065..19c3362153 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1468,13 +1468,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1483,8 +1483,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later 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 adfc4c21a1..bf32b98004 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1468,13 +1468,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1483,8 +1483,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later 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 9978f3c498..b0b5e6b283 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1468,13 +1468,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1483,8 +1483,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later 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 ea491b624e..5a1533ebf4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1468,13 +1468,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1483,8 +1483,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later 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 2795ab235d..1b560e7286 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1469,13 +1469,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1484,8 +1484,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later 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 4ccad506bc..7b47a3cf6a 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 @@ -1468,13 +1468,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1483,8 +1483,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later 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 9f56f9833a..bb2bf95cb5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1468,13 +1468,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1483,8 +1483,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later 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 6a0db53782..0c0b2bea28 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1468,13 +1468,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1483,8 +1483,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later 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 87c82fa812..de170bb208 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 @@ -1468,13 +1468,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1483,8 +1483,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later 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 34931702ae..abbf75e425 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 @@ -1468,13 +1468,13 @@ - '{0}' has been deprecated since {1} {2} version. - '{0}' has been deprecated since {1} {2} version. + '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since {1} {2} version - '{0}' has been removed since {1} {2} version. - '{0}' has been removed since {1} {2} version. + '{0}' has been removed since {1} {2} version + '{0}' has been removed since {1} {2} version @@ -1483,8 +1483,8 @@ - '{0}' requires {1} {2} version or later. - '{0}' requires {1} {2} version or later. + '{0}' requires {1} {2} version or later + '{0}' requires {1} {2} version or later diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index d89407f1c9..f145b3b228 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -1,5 +1,6 @@ // 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.Generic; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; @@ -14,6 +15,80 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests { + namespace System.Runtime.Versioning + { + public abstract class OSPlatformAttribute : Attribute + { + private protected OSPlatformAttribute(string platformName) + { + PlatformName = platformName; + } + + public string PlatformName { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, + AllowMultiple = false, Inherited = false)] + public sealed class TargetPlatformAttribute : OSPlatformAttribute + { + public TargetPlatformAttribute(string platformName) : base(platformName) + { } + } + + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Event | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Field | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] + public sealed class MinimumOSPlatformAttribute : OSPlatformAttribute + { + public MinimumOSPlatformAttribute(string platformName) : base(platformName) + { } + } + + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Event | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Field | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] + public sealed class RemovedInOSPlatformAttribute : OSPlatformAttribute + { + public RemovedInOSPlatformAttribute(string platformName) : base(platformName) + { } + } + + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Event | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Field | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] + public sealed class ObsoletedInOSPlatformAttribute : OSPlatformAttribute + { + public ObsoletedInOSPlatformAttribute(string platformName) : base(platformName) + { } + public ObsoletedInOSPlatformAttribute(string platformName, string message) : base(platformName) + { + Message = message; + } + public string Message { get; } + public string Url { get; set; } + } + } public partial class PlatformCompatabilityAnalyzerTests { [Fact] From fb70d09e29e6090c8f270932555ae06182e07dea Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 7 Aug 2020 12:04:59 -0700 Subject: [PATCH 15/48] Add caching, attributes dependency logic --- .../PlatformCompatabilityAnalyzer.Data.cs | 28 +- .../PlatformCompatabilityAnalyzer.cs | 502 ++++++++-- .../PlatformCompatabilityAnalyzerTests.cs | 110 +-- .../Compiler/Analyzer.Utilities.projitems | 1 + src/Utilities/Compiler/SmallDictionary.cs | 860 ++++++++++++++++++ 5 files changed, 1321 insertions(+), 180 deletions(-) create mode 100644 src/Utilities/Compiler/SmallDictionary.cs diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index c7e23aa2bd..238b2b7b88 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -2,7 +2,9 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Analyzer.Utilities; +using Analyzer.Utilities.PooledObjects; using Microsoft.CodeAnalysis; namespace Microsoft.NetCore.Analyzers.InteropServices @@ -11,12 +13,31 @@ public sealed partial class PlatformCompatabilityAnalyzer { private enum PlatformAttributeType { - TargetPlatformAttribute, MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute, } +#pragma warning disable CA1815 // Override equals and operator equals on value types + private readonly struct PlatformAttributes +#pragma warning restore CA1815 // Override equals and operator equals on value types + { + public PlatformAttributes(SmallDictionary> supportedPlatforms, + SmallDictionary> unsupportedPlatforms, + SmallDictionary obsoletedPlatforms) + { + SupportedPlatforms = supportedPlatforms; + UnsupportedPlatforms = unsupportedPlatforms; + ObsoletedPlatforms = obsoletedPlatforms; + } + public bool HasAttribute => SupportedPlatforms.Any() || UnsupportedPlatforms.Any() || ObsoletedPlatforms.Any(); + public bool Initialized => SupportedPlatforms != null; + public SmallDictionary> SupportedPlatforms { get; } + public SmallDictionary> UnsupportedPlatforms { get; } + public SmallDictionary ObsoletedPlatforms { get; } + } + + /* TODO : Might remove later private readonly struct PlatformAttributeInfo : IEquatable { public PlatformAttributeType AttributeType { get; } @@ -53,7 +74,6 @@ private static PlatformAttributeType SwitchAttrributeType(string osAttributeName MinimumOSPlatformAttribute => PlatformAttributeType.MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute => PlatformAttributeType.ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute => PlatformAttributeType.RemovedInOSPlatformAttribute, - TargetPlatformAttribute => PlatformAttributeType.TargetPlatformAttribute, _ => throw new NotImplementedException(), }; @@ -73,8 +93,8 @@ public override bool Equals(object obj) public static bool operator !=(PlatformAttributeInfo left, PlatformAttributeInfo right) => !(left == right); public bool Equals(PlatformAttributeInfo other) => - AttributeType == other.AttributeType && IsOSPlatformsEqual(PlatformName, other.PlatformName) && Version.Equals(other.Version); - } + AttributeType == other.AttributeType && PlatformName.Equals(other.PlatformName, StringComparison.InvariantCultureIgnoreCase) && Version.Equals(other.Version); + }*/ private static bool TryParsePlatformNameAndVersion(string osString, out string osPlatformName, [NotNullWhen(true)] out Version? version) { diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index cb5013cf1b..dcd35edb00 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using System.Runtime.InteropServices; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Analyzer.Utilities.PooledObjects; @@ -33,16 +32,11 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private const string MinimumOSPlatformAttribute = nameof(MinimumOSPlatformAttribute); private const string ObsoletedInOSPlatformAttribute = nameof(ObsoletedInOSPlatformAttribute); private const string RemovedInOSPlatformAttribute = nameof(RemovedInOSPlatformAttribute); - private const string TargetPlatformAttribute = nameof(TargetPlatformAttribute); // Platform guard method names private const string IsOSPlatformOrLater = nameof(IsOSPlatformOrLater); private const string IsOSPlatformEarlierThan = nameof(IsOSPlatformEarlierThan); - // Equivalent OS names - private const string OSX = nameof(OSPlatform.OSX); - private const string macOS = nameof(macOS); // TODO use 'OSPlatform.macOS' when new OS is available - internal static DiagnosticDescriptor MinimumOsRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, s_localizableAddedMessage, @@ -91,7 +85,9 @@ public override void Initialize(AnalysisContext context) var guardMethods = GetRuntimePlatformGuardMethods(runtimeInformationType!); - context.RegisterOperationBlockStartAction(context => AnalyzerOperationBlock(context, guardMethods, osPlatformType)); + //context.RegisterSymbolAction(AnalyzeSymbolAction, SymbolKind.Field, SymbolKind.Property, SymbolKind.Event, SymbolKind.Assembly, SymbolKind.Method, SymbolKind.NamedType); + + context.RegisterOperationBlockStartAction(context => AnalyzeOperationBlock(context, guardMethods, osPlatformType)); }); static ImmutableArray GetRuntimePlatformGuardMethods(INamedTypeSymbol runtimeInformationType) @@ -101,22 +97,32 @@ static ImmutableArray GetRuntimePlatformGuardMethods(INamedTypeSy } } - private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, ImmutableArray guardMethods, INamedTypeSymbol osPlatformType) + private void AnalyzeSymbolAction(SymbolAnalysisContext obj) + { + throw new NotImplementedException(); + } + + private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, ImmutableArray guardMethods, INamedTypeSymbol osPlatformType) { - var platformSpecificOperations = PooledDictionary>.GetInstance(); + var platformSpecificOperations = PooledConcurrentDictionary.GetInstance(); + var platformSpecificMembers = PooledConcurrentDictionary.GetInstance(); context.RegisterOperationAction(context => { - AnalyzeOperation(context.Operation, context, platformSpecificOperations); - } - , OperationKind.MethodReference, OperationKind.EventReference, OperationKind.FieldReference, - OperationKind.Invocation, OperationKind.ObjectCreation, OperationKind.PropertyReference); + AnalyzeOperation(context.Operation, context, platformSpecificOperations, platformSpecificMembers); + }, + OperationKind.MethodReference, + OperationKind.EventReference, + OperationKind.FieldReference, + OperationKind.Invocation, + OperationKind.ObjectCreation, + OperationKind.PropertyReference); context.RegisterOperationBlockEndAction(context => { try { - if (platformSpecificOperations.Count == 0) + if (platformSpecificOperations.IsEmpty) { return; } @@ -141,7 +147,7 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, foreach (var platformSpecificOperation in platformSpecificOperations) { var value = analysisResult[platformSpecificOperation.Key.Kind, platformSpecificOperation.Key.Syntax]; - PlatformAttributeInfo attribute = platformSpecificOperation.Value.FirstOrDefault(); + PlatformAttributes attribute = platformSpecificOperation.Value; if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown) { @@ -183,7 +189,7 @@ private void AnalyzerOperationBlock(OperationBlockStartAnalysisContext context, }); } - private static bool IsKnownValueGuarded(PlatformAttributeInfo attribute, GlobalFlowStateAnalysisValueSet value) + private static bool IsKnownValueGuarded(PlatformAttributes attribute, GlobalFlowStateAnalysisValueSet value) { foreach (var analysisValue in value.AnalysisValues) { @@ -191,21 +197,37 @@ private static bool IsKnownValueGuarded(PlatformAttributeInfo attribute, GlobalF { if (!info.Negated) { - if (IsOSPlatformsEqual(attribute.PlatformName, info.PlatformPropertyName)) + if (attribute.SupportedPlatforms.TryGetValue(info.PlatformPropertyName, out var versions)) { if (info.InvokedPlatformCheckMethodName == IsOSPlatformOrLater) { - if (attribute.AttributeType == PlatformAttributeType.MinimumOSPlatformAttribute && - AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) + foreach (var version in versions) { - return true; + if (AttributeVersionsMatch(PlatformAttributeType.MinimumOSPlatformAttribute, version, info.Version)) + { + return true; + } } } - else + } + if (attribute.UnsupportedPlatforms.TryGetValue(info.PlatformPropertyName, out versions)) + { + if (info.InvokedPlatformCheckMethodName == IsOSPlatformEarlierThan) { - if ((attribute.AttributeType == PlatformAttributeType.ObsoletedInOSPlatformAttribute || - attribute.AttributeType == PlatformAttributeType.RemovedInOSPlatformAttribute) && - AttributeVersionsMatch(attribute.AttributeType, attribute.Version, info.Version)) + foreach (var version in versions) + { + if (AttributeVersionsMatch(PlatformAttributeType.RemovedInOSPlatformAttribute, version, info.Version)) + { + return true; + } + } + } + } + if (attribute.ObsoletedPlatforms.TryGetValue(info.PlatformPropertyName, out var obsoletedVersion)) + { + if (info.InvokedPlatformCheckMethodName == IsOSPlatformEarlierThan) + { + if (AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, obsoletedVersion, info.Version)) { return true; } @@ -217,20 +239,48 @@ private static bool IsKnownValueGuarded(PlatformAttributeInfo attribute, GlobalF return false; } - private static void ReportDiagnosticsForAll(PooledDictionary> platformSpecificOperations, OperationBlockAnalysisContext context) + private static void ReportDiagnosticsForAll(PooledConcurrentDictionary platformSpecificOperations, OperationBlockAnalysisContext context) { foreach (var platformSpecificOperation in platformSpecificOperations) { - ReportDiagnostics(platformSpecificOperation.Key, platformSpecificOperation.Value.FirstOrDefault(), context); + ReportDiagnostics(platformSpecificOperation.Key, platformSpecificOperation.Value, context); } } - private static void ReportDiagnostics(IOperation operation, PlatformAttributeInfo attribute, OperationBlockAnalysisContext context) + private static void ReportDiagnostics(IOperation operation, PlatformAttributes attributes, OperationBlockAnalysisContext context) { var operationName = GetOperationSymbol(operation)?.Name; - context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(attribute.AttributeType), - operationName ?? string.Empty, attribute.PlatformName, attribute.Version.ToString())); + if (attributes.SupportedPlatforms.Any()) + { + foreach (var attr in attributes.SupportedPlatforms) + { + foreach (var version in attr.Value) + { + context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.MinimumOSPlatformAttribute), + operationName ?? string.Empty, attr.Key, version.ToString())); + } + } + } + if (attributes.UnsupportedPlatforms.Any()) + { + foreach (var attr in attributes.UnsupportedPlatforms) + { + foreach (var version in attr.Value) + { + context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.RemovedInOSPlatformAttribute), + operationName ?? string.Empty, attr.Key, version.ToString())); + } + } + } + if (attributes.ObsoletedPlatforms.Any()) + { + foreach (var attr in attributes.ObsoletedPlatforms) + { + context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.MinimumOSPlatformAttribute), + operationName ?? string.Empty, attr.Key, attr.Value.ToString())); + } + } } private static ISymbol? GetOperationSymbol(IOperation operation) @@ -244,9 +294,9 @@ private static void ReportDiagnostics(IOperation operation, PlatformAttributeInf }; private static void AnalyzeOperation(IOperation operation, OperationAnalysisContext context, - PooledDictionary> platformSpecificOperations) + PooledConcurrentDictionary platformSpecificOperations, + PooledConcurrentDictionary platformSpecificMembers) { - using var builder = ArrayBuilder.GetInstance(); var symbol = GetOperationSymbol(operation); if (symbol == null) @@ -254,23 +304,279 @@ private static void AnalyzeOperation(IOperation operation, OperationAnalysisCont return; } - var attributes = FindAllPlatformAttributesApplied(symbol.GetAttributes(), symbol.ContainingSymbol); + if (!platformSpecificMembers.TryGetValue(symbol.OriginalDefinition, out var operationAttributes)) + { + if (FindPlatformAttributesApplied(symbol.GetAttributes(), symbol.OriginalDefinition.ContainingSymbol, out operationAttributes)) + { + platformSpecificMembers.TryAdd(symbol.OriginalDefinition, operationAttributes); + } + } + + if (operationAttributes.Initialized && operationAttributes.HasAttribute) + { + if (!platformSpecificMembers.TryGetValue(context.ContainingSymbol, out var callSiteAttribute)) + { + if (FindContainingSymbolPlatformAttributes(context.ContainingSymbol, out callSiteAttribute)) + { + platformSpecificMembers.TryAdd(context.ContainingSymbol, callSiteAttribute); + } + } + + if (callSiteAttribute.Initialized && callSiteAttribute.HasAttribute) + { + if (!IsSuppressedByCallSite(operationAttributes, callSiteAttribute, out PlatformAttributes notSuppressedAttributes)) + { + platformSpecificOperations.TryAdd(operation, notSuppressedAttributes); + } + } + else + { + platformSpecificOperations.TryAdd(operation, operationAttributes); + } + } + } + + /// + /// If a member has any SupportedPlatforms attribute + /// + /// Platform specific attributes applied to the invoked member + /// Platform specific attributes applied to the call site where the member invoked + /// - foreach (AttributeData attribute in attributes) + private static bool IsSuppressedByCallSite(PlatformAttributes operationAttributes, PlatformAttributes callSiteAttribute, out PlatformAttributes notSuppressedAttributes) + { + Debug.Assert(operationAttributes.HasAttribute && callSiteAttribute.HasAttribute); + notSuppressedAttributes = new PlatformAttributes(new SmallDictionary>(StringComparer.InvariantCultureIgnoreCase), + new SmallDictionary>(StringComparer.InvariantCultureIgnoreCase), + new SmallDictionary(StringComparer.InvariantCultureIgnoreCase)); + if (operationAttributes.SupportedPlatforms.Any()) { - if (PlatformAttributeInfo.TryParsePlatformAttributeInfo(attribute, out PlatformAttributeInfo parsedAttribute)) + bool? mandatoryList = null; + foreach (string key in operationAttributes.SupportedPlatforms.Keys) { - if (!IsSuppressedByAttribute(parsedAttribute, context.ContainingSymbol)) + var supportedVersions = operationAttributes.SupportedPlatforms[key]; + + if (operationAttributes.UnsupportedPlatforms.TryGetValue(key, out PooledSortedSet? unsupportedVersion)) { - builder.Add(parsedAttribute); + if (supportedVersions.Min < unsupportedVersion.Min) // only for current platform + { + if (mandatoryList.HasValue && !mandatoryList.Value) + { + // report inconsistent list diagnostic + return false; + } + else + { + mandatoryList = true; + } + + if (!MandatoryOsVersionsSuppressed(callSiteAttribute.SupportedPlatforms, key, supportedVersions, notSuppressedAttributes.SupportedPlatforms)) + { + return false; + } + } + else if (supportedVersions.Min == unsupportedVersion.Min) + { + // report inconsistent list diagnostic + return false; + } + else // supported for all platforms + { + if (mandatoryList.HasValue && mandatoryList.Value) + { + // report Inconsistent list diagnostic + } + else + { + mandatoryList = false; + } + + if (!OptionalOsVersionsSuppressed(callSiteAttribute.SupportedPlatforms, key, supportedVersions, notSuppressedAttributes.SupportedPlatforms)) + { + return false; + } + } + } + else + { + if (!MandatoryOsVersionsSuppressed(callSiteAttribute.SupportedPlatforms, key, supportedVersions, notSuppressedAttributes.SupportedPlatforms)) + { + return false; + } } } } - if (builder.Count > 0) + if (operationAttributes.UnsupportedPlatforms.Any()) { - platformSpecificOperations.Add(operation, builder.ToImmutable()); + foreach (string key in operationAttributes.UnsupportedPlatforms.Keys) + { + var unsupportedVersions = operationAttributes.UnsupportedPlatforms[key]; + + if (callSiteAttribute.SupportedPlatforms.TryGetValue(key, out PooledSortedSet? callSiteSupportedVersions)) + { + if (!SuppressedBySupported(operationAttributes.SupportedPlatforms, key, callSiteSupportedVersions)) + { + if (callSiteAttribute.UnsupportedPlatforms.TryGetValue(key, out PooledSortedSet? callSiteUnsupportedVersions)) + { + foreach (var unsupportedVersion in unsupportedVersions) + { + if (!callSiteUnsupportedVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, unsupportedVersion, v))) //unsupportedVersion < v)) + { + AddToDiagnostics(key, notSuppressedAttributes.UnsupportedPlatforms, unsupportedVersion); + return false; + } + } + } + } + } + else + { + // No any supported OS specified means for all OS + if (!operationAttributes.SupportedPlatforms.Any()) + { + if (callSiteAttribute.UnsupportedPlatforms.TryGetValue(key, out PooledSortedSet? callSiteUnsupportedVersions)) + { + foreach (var unsupportedVersion in unsupportedVersions) + { + if (!callSiteUnsupportedVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, unsupportedVersion, v))) //unsupportedVersion < v)) + { + AddToDiagnostics(key, notSuppressedAttributes.UnsupportedPlatforms, unsupportedVersion); + return false; + } + } + } + else + { + foreach (var unsupportedVersion in unsupportedVersions) + { + AddToDiagnostics(key, notSuppressedAttributes.UnsupportedPlatforms, unsupportedVersion); + } + return false; + } + } + } + } } + + if (operationAttributes.ObsoletedPlatforms.Any()) + { + foreach (string key in operationAttributes.ObsoletedPlatforms.Keys) + { + var obsoletedVersion = operationAttributes.ObsoletedPlatforms[key]; + if (operationAttributes.SupportedPlatforms.TryGetValue(key, out PooledSortedSet? supportedVersion)) + { + if (supportedVersion.Max < obsoletedVersion) + { + if (!callSiteAttribute.ObsoletedPlatforms.TryGetValue(key, out Version? suppressingVersion) || + !AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, obsoletedVersion, suppressingVersion)) //obsoletedVersion >= suppressingVersion) + { + notSuppressedAttributes.ObsoletedPlatforms[key] = obsoletedVersion; + return false; + } + } + else + { + // Can supported version be greater than obsoleted? Do we want to report diagnostic about wrong version? + } + } + else + { + if (!operationAttributes.SupportedPlatforms.Any()) + { + // No any os specified means for all OS + if (!callSiteAttribute.ObsoletedPlatforms.TryGetValue(key, out Version? suppressingVersion) || + !AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, obsoletedVersion, suppressingVersion)) //obsoletedVersion >= suppressingVersion) + { + notSuppressedAttributes.ObsoletedPlatforms[key] = obsoletedVersion; + return false; + } + } + } + } + } + + return true; + } + + private static bool SuppressedBySupported(SmallDictionary> supportedPlatforms, string key, PooledSortedSet callSiteSupportedVersions) + { + if (supportedPlatforms.TryGetValue(key, out PooledSortedSet? supportedVersions)) + { + foreach (var calledVersion in callSiteSupportedVersions) + { + if (supportedVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.MinimumOSPlatformAttribute, calledVersion, v))) //v <= calledVersion)) + { + return true; + } + } + } + + return false; + } + + private static bool OptionalOsVersionsSuppressed(SmallDictionary> callSitePlatforms, string key, + PooledSortedSet supportedVersions, SmallDictionary> notSuppressedVersions) + { + if (callSitePlatforms.TryGetValue(key, out PooledSortedSet? suppressingVersions)) + { + foreach (var supportedVersion in supportedVersions) + { + if (!suppressingVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.MinimumOSPlatformAttribute, supportedVersion, v)))//supportedVersion <= v)) + { + AddToDiagnostics(key, notSuppressedVersions, supportedVersion); + + return false; + } + } + } + return true; + } + + private static void AddToDiagnostics(string key, SmallDictionary> versions, Version version) + { + if (versions.TryGetValue(key, out var existing)) + { + existing.Add(version); + } + else + { + var set = PooledSortedSet.GetInstance(); + set.Add(version); + versions.Add(key, set); + } + } + + private static bool MandatoryOsVersionsSuppressed(SmallDictionary> callSitePlatforms, + string key, PooledSortedSet checkingVersions, SmallDictionary> notSuppressedVersions) + { + if (callSitePlatforms.TryGetValue(key, out PooledSortedSet? suppressingVersions)) + { + foreach (var checkingVersion in checkingVersions) + { + if (!suppressingVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.MinimumOSPlatformAttribute, checkingVersion, v))) //checkingVersion <= v)) + { + AddToDiagnostics(key, notSuppressedVersions, checkingVersion); + + return false; + } + } + } + else + { + if (!notSuppressedVersions.TryGetValue(key, out var existing)) + { + existing = PooledSortedSet.GetInstance(); + notSuppressedVersions.Add(key, existing); + } + + foreach (var checkingVersion in checkingVersions) + { + existing.Add(checkingVersion); + } + return false; + } + return true; } // Do not warn for conditional checks of platfomr specific enum value; 'if (value != FooEnum.WindowsOnlyValue)' @@ -284,24 +590,6 @@ pOperation.Parent is IBinaryOperation bo && bo.OperatorKind == BinaryOperatorKind.GreaterThanOrEqual || bo.OperatorKind == BinaryOperatorKind.LessThanOrEqual); - private static bool IsOSPlatformsEqual(string left, string right) - { - if (left.Equals(right, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - else - { - var normalizedLeft = NormalizeOSName(left); - var normalizedRight = NormalizeOSName(right); - return string.Equals(normalizedLeft, normalizedRight, StringComparison.OrdinalIgnoreCase); - } - } - - private static string NormalizeOSName(string name) => name.Equals(OSX, StringComparison.OrdinalIgnoreCase) - ? macOS - : name; - private static DiagnosticDescriptor SelectRule(PlatformAttributeType attributeType) => attributeType switch { @@ -310,60 +598,96 @@ private static DiagnosticDescriptor SelectRule(PlatformAttributeType attributeTy _ => RemovedRule, }; - private static ImmutableArray FindAllPlatformAttributesApplied(ImmutableArray immediateAttributes, ISymbol parent) + private static bool FindPlatformAttributesApplied(ImmutableArray immediateAttributes, ISymbol containingSymbol, out PlatformAttributes attributes) { - using var builder = ArrayBuilder.GetInstance(); - foreach (AttributeData attribute in immediateAttributes) + attributes = new PlatformAttributes(new SmallDictionary>(StringComparer.InvariantCultureIgnoreCase), + new SmallDictionary>(StringComparer.InvariantCultureIgnoreCase), + new SmallDictionary(StringComparer.InvariantCultureIgnoreCase)); + AddPlatformAttributes(immediateAttributes, attributes); + + while (containingSymbol != null) { - if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name)) - { - builder.Add(attribute); - } + AddPlatformAttributes(containingSymbol.GetAttributes(), attributes); + containingSymbol = containingSymbol.ContainingSymbol; } + return attributes.HasAttribute; + } - while (parent != null) + private static void AddPlatformAttributes(ImmutableArray immediateAttributes, PlatformAttributes attributes) + { + foreach (AttributeData attribute in immediateAttributes) { - var current = parent.GetAttributes(); - foreach (var attribute in current) + if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name)) { - if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name)) - { - builder.Add(attribute); - } + AddValidAttribute(attributes, attribute); } - parent = parent.ContainingSymbol; } - - return builder.ToImmutableArray(); } - private static bool IsSuppressedByAttribute(PlatformAttributeInfo diagnosingAttribute, ISymbol containingSymbol) + private static bool FindContainingSymbolPlatformAttributes(ISymbol containingSymbol, out PlatformAttributes attributes) { + attributes = new PlatformAttributes(new SmallDictionary>(StringComparer.InvariantCultureIgnoreCase), + new SmallDictionary>(StringComparer.InvariantCultureIgnoreCase), + new SmallDictionary(StringComparer.InvariantCultureIgnoreCase)); while (containingSymbol != null) { - var attributes = containingSymbol.GetAttributes(); + AddPlatformAttributes(containingSymbol.GetAttributes(), attributes); containingSymbol = containingSymbol.ContainingSymbol; + } - if (attributes.IsEmpty) - { - continue; - } - foreach (AttributeData attribute in attributes) + return attributes.HasAttribute; + } + + private static PlatformAttributes AddValidAttribute(PlatformAttributes 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)) + { + if (TryParsePlatformNameAndVersion(argument.Value.ToString(), out string platformName, out Version? version)) { - if (diagnosingAttribute.AttributeType.ToString() == attribute.AttributeClass.Name && - PlatformAttributeInfo.TryParsePlatformAttributeInfo(attribute, out PlatformAttributeInfo parsedAttribute) && - IsOSPlatformsEqual(diagnosingAttribute.PlatformName, parsedAttribute.PlatformName) && - AttributeVersionsMatch(diagnosingAttribute, parsedAttribute)) + if (attribute.AttributeClass.Name == ObsoletedInOSPlatformAttribute) { - return true; + attributes.ObsoletedPlatforms[platformName] = version; + } + else + { + AddOrSetVersion(SwitchAttrributes(attribute.AttributeClass.Name, attributes), platformName, version); } } + // else report diagnostic = Diagnostic.Create(PlatformNameNullOrEmptyRule, osAttribute.ApplicationSyntaxReference.GetSyntax().GetLocation()); } - return false; + else + { + //diagnostic = Diagnostic.Create(InvalidPlatformVersionRule, osAttribute.ApplicationSyntaxReference.GetSyntax().GetLocation()); + } + + return attributes; } - private static bool AttributeVersionsMatch(PlatformAttributeInfo diagnosingAttribute, PlatformAttributeInfo osAttribute) - => AttributeVersionsMatch(diagnosingAttribute.AttributeType, diagnosingAttribute.Version, osAttribute.Version); + private static SmallDictionary> SwitchAttrributes(string osAttributeName, PlatformAttributes attributes) + => osAttributeName switch + { + MinimumOSPlatformAttribute => attributes.SupportedPlatforms, + RemovedInOSPlatformAttribute => attributes.UnsupportedPlatforms, + _ => throw new NotImplementedException(), + }; + + private static void AddOrSetVersion(SmallDictionary> dictionary, string platformName, Version version) + { + if (dictionary.TryGetValue(platformName, out PooledSortedSet? existingVersion)) + { + existingVersion.Add(version); + } + else + { + dictionary[platformName] = PooledSortedSet.GetInstance(); + dictionary[platformName].Add(version); + } + } private static bool AttributeVersionsMatch(PlatformAttributeType attributeType, Version diagnosingVersion, Version suppressingVersion) { @@ -401,7 +725,7 @@ private static bool AttributeVersionsMatch(PlatformAttributeType attributeType, return diagnosingVersion.Build > suppressingVersion.Build; } - return diagnosingVersion.Revision > suppressingVersion.Revision; + return diagnosingVersion.Revision >= suppressingVersion.Revision; } } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index f145b3b228..cff4ee4c28 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -1,6 +1,5 @@ // 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.Generic; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; @@ -15,80 +14,7 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests { - namespace System.Runtime.Versioning - { - public abstract class OSPlatformAttribute : Attribute - { - private protected OSPlatformAttribute(string platformName) - { - PlatformName = platformName; - } - - public string PlatformName { get; } - } - [AttributeUsage(AttributeTargets.Assembly, - AllowMultiple = false, Inherited = false)] - public sealed class TargetPlatformAttribute : OSPlatformAttribute - { - public TargetPlatformAttribute(string platformName) : base(platformName) - { } - } - - [AttributeUsage(AttributeTargets.Assembly | - AttributeTargets.Class | - AttributeTargets.Constructor | - AttributeTargets.Event | - AttributeTargets.Method | - AttributeTargets.Module | - AttributeTargets.Property | - AttributeTargets.Field | - AttributeTargets.Struct, - AllowMultiple = true, Inherited = false)] - public sealed class MinimumOSPlatformAttribute : OSPlatformAttribute - { - public MinimumOSPlatformAttribute(string platformName) : base(platformName) - { } - } - - [AttributeUsage(AttributeTargets.Assembly | - AttributeTargets.Class | - AttributeTargets.Constructor | - AttributeTargets.Event | - AttributeTargets.Method | - AttributeTargets.Module | - AttributeTargets.Property | - AttributeTargets.Field | - AttributeTargets.Struct, - AllowMultiple = true, Inherited = false)] - public sealed class RemovedInOSPlatformAttribute : OSPlatformAttribute - { - public RemovedInOSPlatformAttribute(string platformName) : base(platformName) - { } - } - - [AttributeUsage(AttributeTargets.Assembly | - AttributeTargets.Class | - AttributeTargets.Constructor | - AttributeTargets.Event | - AttributeTargets.Method | - AttributeTargets.Module | - AttributeTargets.Property | - AttributeTargets.Field | - AttributeTargets.Struct, - AllowMultiple = true, Inherited = false)] - public sealed class ObsoletedInOSPlatformAttribute : OSPlatformAttribute - { - public ObsoletedInOSPlatformAttribute(string platformName) : base(platformName) - { } - public ObsoletedInOSPlatformAttribute(string platformName, string message) : base(platformName) - { - Message = message; - } - public string Message { get; } - public string Url { get; set; } - } - } public partial class PlatformCompatabilityAnalyzerTests { [Fact] @@ -104,6 +30,7 @@ public void M1() [|WindowsOnly()|]; [|Obsoleted()|]; [|Removed()|]; + [|ObsoletedOverload()|]; } [MinimumOSPlatform(""Windows10.1.1.1"")] public void WindowsOnly() @@ -113,6 +40,10 @@ public void WindowsOnly() public void Obsoleted() { } + [ObsoletedInOSPlatform(""Linux4.1"", ""Obsolete message"")] + public void ObsoletedOverload() + { + } [RemovedInOSPlatform(""Linux4.1"")] public void Removed() { @@ -128,6 +59,7 @@ Public Class Test Public Sub M1() [|WindowsOnly()|] [|Obsoleted()|] + [|ObsoletedOverload()|] [|Removed()|] End Sub @@ -139,6 +71,10 @@ End Sub Public Sub Obsoleted() End Sub + + Public Sub ObsoletedOverload() + End Sub + Public Sub Removed() End Sub @@ -662,7 +598,7 @@ public async Task AttributedLambdaCallsOsDependentMemberNotWarn() using System.Runtime.Versioning; using System; -public class Test +public class C { [MinimumOSPlatform(""Windows10.13"")] public void M1() @@ -797,15 +733,15 @@ public static IEnumerable MinimumOsAttributeTestData() { yield return new object[] { "Windows10.1.2.3", "Windows10.1.2.3", false }; yield return new object[] { "Windows10.1.2.3", "Windows10.1.3.3", false }; - yield return new object[] { "WINDOWS10.1.2.3", "Windows10.1.3.1", false }; - yield return new object[] { "Windows10.1.2.3", "Windows11.1.2.3", false }; + yield return new object[] { "WINDOWS10.1.2.3", "Windows10.1.3", false }; + yield return new object[] { "Windows10.1.2.3", "Windows11.0", false }; yield return new object[] { "Windows10.1.2.3", "windows10.2.2.0", false }; yield return new object[] { "Windows10.1.2.3", "Windows10.1.1.3", true }; yield return new object[] { "Windows10.1.2.3", "WINDOWS11.1.1.3", false }; yield return new object[] { "Windows10.1.2.3", "Windows10.1.1.4", true }; - yield return new object[] { "osx10.1.2.3", "macos10.2.2.0", false }; - yield return new object[] { "macOs10.1.2.3", "Osx11.1.1.0", false }; - yield return new object[] { "MACOS10.1.2.3", "osx10.2.2.0", false }; + yield return new object[] { "MACOS10.1.2.3", "macos10.2.2.0", false }; + yield return new object[] { "OSX10.1.2.3", "Osx11.1.1.0", false }; + yield return new object[] { "Osx10.1.2.3", "osx10.2", false }; yield return new object[] { "Windows10.1.2.3", "Osx11.1.1.4", true }; yield return new object[] { "Windows10.1.2.3", "Windows10.0.1.9", true }; yield return new object[] { "Windows10.1.2.3", "Windows10.1.1.4", true }; @@ -857,6 +793,7 @@ public async Task MethodOfOsDependentClassSuppressedWithObsoleteAttribute(string var source = @" using System.Runtime.Versioning; +[MinimumOSPlatform(""Windows"")] public class Test { [ObsoletedInOSPlatform(""" + suppressingVersion + @""")] @@ -867,6 +804,7 @@ public void M1() } } +[MinimumOSPlatform(""Windows"")] [ObsoletedInOSPlatform(""" + dependentVersion + @""")] public class OsDependentClass { @@ -879,8 +817,8 @@ public void M2() if (warn) { await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(9, 32, 9, 54).WithArguments(".ctor", "Windows", "10.1.2.3"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteRule).WithLocation(10, 32).WithArguments(".ctor", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteRule).WithLocation(11, 9).WithArguments("M2", "Windows", "10.1.2.3")); } else { @@ -890,16 +828,14 @@ await VerifyAnalyzerAsyncCs(source, public static IEnumerable ObsoletedRemovedAttributeTestData() { - yield return new object[] { "Windows10.1.2.3", "Windows10.1.2.3", true }; - yield return new object[] { "Windows10.1.2.3", "Windows10.1.3.3", true }; + yield return new object[] { "Windows10.1.2.3", "Windows10.1.2.3", false }; + yield return new object[] { "Windows10.1.2.3", "MacOs10.1.3.3", true }; yield return new object[] { "Windows10.1.2.3", "Windows10.1.3.1", true }; - yield return new object[] { "Windows10.1.2.3", "Windows11.1.2.3", true }; + yield return new object[] { "Windows10.1.2.3", "Windows11.1", true }; yield return new object[] { "Windows10.1.2.3", "Windows10.2.2.0", true }; yield return new object[] { "Windows10.1.2.3", "Windows10.1.1.3", false }; yield return new object[] { "Windows10.1.2.3", "WINDOWS10.1.1.3", false }; yield return new object[] { "Windows10.1.2.3", "Windows10.1.1.4", false }; - yield return new object[] { "osx10.1.2.3", "MacOs10.1.1.4", false }; - yield return new object[] { "macOs10.1.2.3", "Osx10.1.1.4", false }; yield return new object[] { "Windows10.1.2.3", "Osx10.1.1.4", true }; yield return new object[] { "windows10.1.2.3", "Windows10.1.0.1", false }; yield return new object[] { "Windows10.1.2.3", "Windows8.2.3.4", false }; diff --git a/src/Utilities/Compiler/Analyzer.Utilities.projitems b/src/Utilities/Compiler/Analyzer.Utilities.projitems index 8faa931f36..fdf23610d4 100644 --- a/src/Utilities/Compiler/Analyzer.Utilities.projitems +++ b/src/Utilities/Compiler/Analyzer.Utilities.projitems @@ -104,6 +104,7 @@ + diff --git a/src/Utilities/Compiler/SmallDictionary.cs b/src/Utilities/Compiler/SmallDictionary.cs new file mode 100644 index 0000000000..137f28d81a --- /dev/null +++ b/src/Utilities/Compiler/SmallDictionary.cs @@ -0,0 +1,860 @@ +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace Analyzer.Utilities +{ + /// + /// Dictionary designed to hold small number of items. + /// Compared to the regular Dictionary, average overhead per-item is roughly the same, but + /// unlike regular dictionary, this one is based on an AVL tree and as such does not require + /// rehashing when items are added. + /// It does require rebalancing, but that is allocation-free. + /// + /// Major caveats: + /// 1) There is no Remove method. (can be added, but we do not seem to use Remove that much) + /// 2) foreach [keys|values|pairs] may allocate a small array. + /// 3) Performance is no longer O(1). At a certain count it becomes slower than regular Dictionary. + /// In comparison to regular Dictionary on my machine: + /// On trivial number of elements (5 or so) it is more than 2x faster. + /// The break even count is about 120 elements for read and 55 for write operations (with unknown initial size). + /// At UShort.MaxValue elements, this dictionary is 6x slower to read and 4x slower to write + /// + /// Generally, this dictionary is a win if number of elements is small, not known beforehand or both. + /// + /// If the size of the dictionary is known at creation and it is likely to contain more than 10 elements, + /// then regular Dictionary is a better choice. + /// +#pragma warning disable CA1051, CA1716 // Do not declare visible instance fields + internal sealed class SmallDictionary : IEnumerable> + where K : notnull + { + private AvlNode? _root; + + public readonly IEqualityComparer Comparer; + + // https://github.com/dotnet/roslyn/issues/40344 + public static readonly SmallDictionary Empty = new SmallDictionary(null!); + + public SmallDictionary() : this(EqualityComparer.Default) { } + + public SmallDictionary(IEqualityComparer comparer) + { + Comparer = comparer; + } + + public SmallDictionary(SmallDictionary other, IEqualityComparer comparer) + : this(comparer) + { + // TODO: if comparers are same (often they are), then just need to clone the tree. + foreach (var kv in other) + { + this.Add(kv.Key, kv.Value); + } + } + + private bool CompareKeys(K k1, K k2) + { + return Comparer.Equals(k1, k2); + } + + private int GetHashCode(K k) + { + return Comparer.GetHashCode(k); + } + + public bool TryGetValue(K key, [MaybeNullWhen(returnValue: false)] out V value) + { + if (_root != null) + { + return TryGetValue(GetHashCode(key), key, out value!); + } + + value = default!; + return false; + } + + public void Add(K key, V value) + { + Insert(GetHashCode(key), key, value, add: true); + } + + public V this[K key] + { + get + { + V value; + if (!TryGetValue(key, out value!)) + { + throw new KeyNotFoundException($"Could not find key {key}"); + } + + return value; + } + + set + { + this.Insert(GetHashCode(key), key, value, add: false); + } + } + + public bool ContainsKey(K key) + { + return TryGetValue(key, out V _); + } + + [Conditional("DEBUG")] + internal void AssertBalanced() + { +#if DEBUG + AvlNode.AssertBalanced(_root); +#endif + } + + private abstract class Node + { + public readonly K Key; + public V Value; + + protected Node(K key, V value) + { + this.Key = key; + this.Value = value; + } + + public virtual Node? Next => null; + } + + private sealed class NodeLinked : Node + { + public NodeLinked(K key, V value, Node next) + : base(key, value) + { + this.Next = next; + } + + public override Node Next { get; } + } + + private sealed class AvlNodeHead : AvlNode + { + public Node next; + + public AvlNodeHead(int hashCode, K key, V value, Node next) + : base(hashCode, key, value) + { + this.next = next; + } + + public override Node Next => next; + } + + // separate class to ensure that HashCode field + // is located before other AvlNode fields + // Balance is also here for better packing of AvlNode on 64bit + private abstract class HashedNode : Node + { + public readonly int HashCode; + public sbyte Balance; + + protected HashedNode(int hashCode, K key, V value) + : base(key, value) + { + this.HashCode = hashCode; + } + } + + private class AvlNode : HashedNode + { + public AvlNode? Left; + public AvlNode? Right; + + public AvlNode(int hashCode, K key, V value) + : base(hashCode, key, value) + { } + +#if DEBUG +#pragma warning disable CA1000 // Do not declare static members on generic types + public static int AssertBalanced(AvlNode? V) +#pragma warning restore CA1000 // Do not declare static members on generic types + { + if (V == null) return 0; + + int a = AssertBalanced(V.Left); + int b = AssertBalanced(V.Right); + + if (a - b != V.Balance || + Math.Abs(a - b) >= 2) + { + throw new InvalidOperationException(); + } + + return 1 + Math.Max(a, b); + } +#endif + } + + private bool TryGetValue(int hashCode, K key, [MaybeNullWhen(returnValue: false)] out V value) + { + RoslynDebug.Assert(_root is object); + AvlNode? b = _root; + + do + { + if (b.HashCode > hashCode) + { + b = b.Left; + } + else if (b.HashCode < hashCode) + { + b = b.Right; + } + else + { + goto hasBucket; + } + } while (b != null); + + value = default!; + return false; + + hasBucket: + if (CompareKeys(b.Key, key)) + { + value = b.Value; + return true; + } + + return GetFromList(b.Next, key, out value!); + } + + private bool GetFromList(Node? next, K key, [MaybeNullWhen(returnValue: false)] out V value) + { + while (next != null) + { + if (CompareKeys(key, next.Key)) + { + value = next.Value; + return true; + } + + next = next.Next; + } + + value = default!; + return false; + } + + private void Insert(int hashCode, K key, V value, bool add) + { + AvlNode? currentNode = _root; + + if (currentNode == null) + { + _root = new AvlNode(hashCode, key, value); + return; + } + + AvlNode? currentNodeParent = null; + AvlNode unbalanced = currentNode; + AvlNode? unbalancedParent = null; + + // ====== insert new node + // also make a note of the last unbalanced node and its parent (for rotation if needed) + // nodes on the search path from rotation candidate downwards will change balances because of the node added + // unbalanced node itself will become balanced or will be rotated + // either way nodes above unbalanced do not change their balance + for (; ; ) + { + // schedule hk read + var hc = currentNode.HashCode; + + if (currentNode.Balance != 0) + { + unbalancedParent = currentNodeParent; + unbalanced = currentNode; + } + + if (hc > hashCode) + { + if (currentNode.Left == null) + { + var previousNode = currentNode; + currentNode = new AvlNode(hashCode, key, value); + previousNode.Left = currentNode; + break; + } + + currentNodeParent = currentNode; + currentNode = currentNode.Left; + } + else if (hc < hashCode) + { + if (currentNode.Right == null) + { + var previousNode = currentNode; + currentNode = new AvlNode(hashCode, key, value); + previousNode.Right = currentNode; + break; + } + + currentNodeParent = currentNode; + currentNode = currentNode.Right; + } + else // (p.HashCode == hashCode) + { + this.HandleInsert(currentNode, currentNodeParent, key, value, add); + return; + } + } + + Debug.Assert(unbalanced != currentNode); + + // ====== update balances on the path from unbalanced downwards + var n = unbalanced; + do + { + Debug.Assert(n.HashCode != hashCode); + + if (n.HashCode < hashCode) + { + n.Balance--; + n = n.Right!; + } + else + { + n.Balance++; + n = n.Left!; + } + } + while (n != currentNode); + + // ====== rotate unbalanced node if needed + AvlNode rotated; + var balance = unbalanced.Balance; + if (balance == -2) + { + rotated = unbalanced.Right!.Balance < 0 ? + LeftSimple(unbalanced) : + LeftComplex(unbalanced); + } + else if (balance == 2) + { + rotated = unbalanced.Left!.Balance > 0 ? + RightSimple(unbalanced) : + RightComplex(unbalanced); + } + else + { + return; + } + + // ===== make parent to point to rotated + if (unbalancedParent == null) + { + _root = rotated; + } + else if (unbalanced == unbalancedParent.Left) + { + unbalancedParent.Left = rotated; + } + else + { + unbalancedParent.Right = rotated; + } + } + + private static AvlNode LeftSimple(AvlNode unbalanced) + { + RoslynDebug.Assert(unbalanced.Right is object); + var right = unbalanced.Right; + unbalanced.Right = right.Left; + right.Left = unbalanced; + + unbalanced.Balance = 0; + right.Balance = 0; + return right; + } + + private static AvlNode RightSimple(AvlNode unbalanced) + { + RoslynDebug.Assert(unbalanced.Left is object); + var left = unbalanced.Left; + unbalanced.Left = left.Right; + left.Right = unbalanced; + + unbalanced.Balance = 0; + left.Balance = 0; + return left; + } + + private static AvlNode LeftComplex(AvlNode unbalanced) + { + RoslynDebug.Assert(unbalanced.Right is object); + RoslynDebug.Assert(unbalanced.Right.Left is object); + var right = unbalanced.Right; + var rightLeft = right.Left; + right.Left = rightLeft.Right; + rightLeft.Right = right; + unbalanced.Right = rightLeft.Left; + rightLeft.Left = unbalanced; + + var rightLeftBalance = rightLeft.Balance; + rightLeft.Balance = 0; + + if (rightLeftBalance < 0) + { + right.Balance = 0; + unbalanced.Balance = 1; + } + else + { + right.Balance = (sbyte)-rightLeftBalance; + unbalanced.Balance = 0; + } + + return rightLeft; + } + + private static AvlNode RightComplex(AvlNode unbalanced) + { + RoslynDebug.Assert(unbalanced.Left != null); + RoslynDebug.Assert(unbalanced.Left.Right != null); + var left = unbalanced.Left; + var leftRight = left.Right; + left.Right = leftRight.Left; + leftRight.Left = left; + unbalanced.Left = leftRight.Right; + leftRight.Right = unbalanced; + + var leftRightBalance = leftRight.Balance; + leftRight.Balance = 0; + + if (leftRightBalance < 0) + { + left.Balance = 1; + unbalanced.Balance = 0; + } + else + { + left.Balance = 0; + unbalanced.Balance = (sbyte)-leftRightBalance; + } + + return leftRight; + } + + private void HandleInsert(AvlNode node, AvlNode? parent, K key, V value, bool add) + { + Node? currentNode = node; + do + { + if (CompareKeys(currentNode.Key, key)) + { + if (add) + { + throw new InvalidOperationException(); + } + + currentNode.Value = value; + return; + } + + currentNode = currentNode.Next; + } while (currentNode != null); + + AddNode(node, parent, key, value); + } + + private void AddNode(AvlNode node, AvlNode? parent, K key, V value) + { + if (node is AvlNodeHead head) + { + var newNext = new NodeLinked(key, value, head.next); + head.next = newNext; + return; + } + + var newHead = new AvlNodeHead(node.HashCode, key, value, node); + newHead.Balance = node.Balance; + newHead.Left = node.Left; + newHead.Right = node.Right; + + if (parent == null) + { + _root = newHead; + return; + } + + if (node == parent.Left) + { + parent.Left = newHead; + } + else + { + parent.Right = newHead; + } + } + + public KeyCollection Keys => new KeyCollection(this); + +#pragma warning disable CA1815 // Override equals and operator equals on value types + internal struct KeyCollection : IEnumerable + { + private readonly SmallDictionary _dict; + + public KeyCollection(SmallDictionary dict) + { + _dict = dict; + } + + public struct Enumerator + { + private readonly Stack _stack; + private Node? _next; + private Node _current; + + public Enumerator(SmallDictionary dict) + : this() + { + var root = dict._root; + if (root != null) + { + // left == right only if both are nulls + if (root.Left == root.Right) + { + _next = root; + } + else + { + _stack = new Stack(dict.HeightApprox()); + _stack.Push(root); + } + } + } + + public K Current => _current.Key; + + public bool MoveNext() + { + if (_next != null) + { + _current = _next; + _next = _next.Next; + return true; + } + + if (_stack == null || _stack.Count == 0) + { + return false; + } + + var curr = _stack.Pop(); + _current = curr; + _next = curr.Next; + + PushIfNotNull(curr.Left); + PushIfNotNull(curr.Right); + + return true; + } + + private void PushIfNotNull(AvlNode? child) + { + if (child != null) + { + _stack.Push(child); + } + } + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_dict); + } + +#pragma warning disable CA1063, CA1816 // Implement IDisposable Correctly + public class EnumerableImpl : IEnumerator + { + private Enumerator _e; + + public EnumerableImpl(Enumerator e) + { + _e = e; + } + + K IEnumerator.Current => _e.Current; + + void IDisposable.Dispose() + { + } + + object IEnumerator.Current => _e.Current; + + bool IEnumerator.MoveNext() + { + return _e.MoveNext(); + } + + void IEnumerator.Reset() + { + throw new NotSupportedException(); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new EnumerableImpl(GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public ValueCollection Values => new ValueCollection(this); + + internal struct ValueCollection : IEnumerable + { + private readonly SmallDictionary _dict; + + public ValueCollection(SmallDictionary dict) + { + _dict = dict; + } + + public struct Enumerator + { + private readonly Stack _stack; + private Node? _next; + private Node _current; + + public Enumerator(SmallDictionary dict) + : this() + { + var root = dict._root; + if (root == null) + { + return; + } + + // left == right only if both are nulls + if (root.Left == root.Right) + { + _next = root; + } + else + { + _stack = new Stack(dict.HeightApprox()); + _stack.Push(root); + } + } + + public V Current => _current.Value; + + public bool MoveNext() + { + if (_next != null) + { + _current = _next; + _next = _next.Next; + return true; + } + + if (_stack == null || _stack.Count == 0) + { + return false; + } + + var curr = _stack.Pop(); + _current = curr; + _next = curr.Next; + + PushIfNotNull(curr.Left); + PushIfNotNull(curr.Right); + + return true; + } + + private void PushIfNotNull(AvlNode? child) + { + if (child != null) + { + _stack.Push(child); + } + } + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_dict); + } + + public class EnumerableImpl : IEnumerator + { + private Enumerator _e; + + public EnumerableImpl(Enumerator e) + { + _e = e; + } + + V IEnumerator.Current => _e.Current; + + void IDisposable.Dispose() + { + } + + object? IEnumerator.Current => _e.Current; + + bool IEnumerator.MoveNext() + { + return _e.MoveNext(); + } + + void IEnumerator.Reset() + { + throw new NotImplementedException(); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new EnumerableImpl(GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public struct Enumerator + { + private readonly Stack _stack; + private Node? _next; + private Node _current; + + public Enumerator(SmallDictionary dict) + : this() + { + var root = dict._root; + if (root == null) + { + return; + } + + // left == right only if both are nulls + if (root.Left == root.Right) + { + _next = root; + } + else + { + _stack = new Stack(dict.HeightApprox()); + _stack.Push(root); + } + } + + public KeyValuePair Current => new KeyValuePair(_current.Key, _current.Value); + + public bool MoveNext() + { + if (_next != null) + { + _current = _next; + _next = _next.Next; + return true; + } + + if (_stack == null || _stack.Count == 0) + { + return false; + } + + var curr = _stack.Pop(); + _current = curr; + _next = curr.Next; + + PushIfNotNull(curr.Left); + PushIfNotNull(curr.Right); + + return true; + } + + private void PushIfNotNull(AvlNode? child) + { + if (child != null) + { + _stack.Push(child); + } + } + } + + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + public class EnumerableImpl : IEnumerator> + { + private Enumerator _e; + + public EnumerableImpl(Enumerator e) + { + _e = e; + } + + KeyValuePair IEnumerator>.Current => _e.Current; + + void IDisposable.Dispose() + { + } + + object IEnumerator.Current => _e.Current; + + bool IEnumerator.MoveNext() + { + return _e.MoveNext(); + } + + void IEnumerator.Reset() + { + throw new NotImplementedException(); + } + } +#pragma warning restore CA1063, CA1816 // Implement IDisposable Correctly +#pragma warning restore CA1815 // Override equals and operator equals on value types + + IEnumerator> IEnumerable>.GetEnumerator() + { + return new EnumerableImpl(GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + private int HeightApprox() + { + // height is less than 1.5 * depth(leftmost node) + var h = 0; + var cur = _root; + while (cur != null) + { + h++; + cur = cur.Left; + } + + h += h / 2; + return h; + } + } +#pragma warning restore CA1051, CA1716 // Do not declare visible instance fields +} From f210ff4135153fc9edcf4ab1e35475561a3607dd Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 7 Aug 2020 13:33:35 -0700 Subject: [PATCH 16/48] Rename OS attributes and all references --- .../PlatformCompatabilityAnalyzer.Data.cs | 64 +------- .../PlatformCompatabilityAnalyzer.Value.cs | 18 +-- .../PlatformCompatabilityAnalyzer.cs | 81 +++++----- .../MicrosoftNetCoreAnalyzersResources.resx | 6 +- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 12 +- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 12 +- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 12 +- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 12 +- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 12 +- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 12 +- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 12 +- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 12 +- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 12 +- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 12 +- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 12 +- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 12 +- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 12 +- ...abilityAnalyzerTests.GuardedCallsTests.cs} | 76 ++++----- .../PlatformCompatabilityAnalyzerTests.cs | 144 +++++++++--------- 19 files changed, 240 insertions(+), 305 deletions(-) rename src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/{PlatformCompatabilityAnalyzerTests.GuardedCalls.cs => PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs} (94%) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index 238b2b7b88..ae6c245129 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -13,9 +13,9 @@ public sealed partial class PlatformCompatabilityAnalyzer { private enum PlatformAttributeType { - MinimumOSPlatformAttribute, + SupportedOSPlatformAttribute, ObsoletedInOSPlatformAttribute, - RemovedInOSPlatformAttribute, + UnsupportedOSPlatformAttribute, } #pragma warning disable CA1815 // Override equals and operator equals on value types @@ -31,71 +31,11 @@ public PlatformAttributes(SmallDictionary> supp ObsoletedPlatforms = obsoletedPlatforms; } public bool HasAttribute => SupportedPlatforms.Any() || UnsupportedPlatforms.Any() || ObsoletedPlatforms.Any(); - public bool Initialized => SupportedPlatforms != null; public SmallDictionary> SupportedPlatforms { get; } public SmallDictionary> UnsupportedPlatforms { get; } public SmallDictionary ObsoletedPlatforms { get; } } - /* TODO : Might remove later - private readonly struct PlatformAttributeInfo : IEquatable - { - public PlatformAttributeType AttributeType { get; } - public string PlatformName { get; } - public Version Version { get; } - - private PlatformAttributeInfo(PlatformAttributeType attributeType, string platformName, Version version) - { - AttributeType = attributeType; - PlatformName = platformName; - Version = version; - } - - public static bool TryParsePlatformAttributeInfo(AttributeData osAttribute, out PlatformAttributeInfo parsedAttribute) - { - if (!osAttribute.ConstructorArguments.IsEmpty && - osAttribute.ConstructorArguments[0] is { } argument && - argument.Kind == TypedConstantKind.Primitive && - argument.Type.SpecialType == SpecialType.System_String && - !argument.IsNull && - !argument.Value.Equals(string.Empty) && - TryParsePlatformNameAndVersion(osAttribute.ConstructorArguments[0].Value.ToString(), out string platformName, out Version? version)) - { - parsedAttribute = new PlatformAttributeInfo(SwitchAttrributeType(osAttribute.AttributeClass.Name), platformName, version); - return true; - } - parsedAttribute = default; - return false; - } - - private static PlatformAttributeType SwitchAttrributeType(string osAttributeName) - => osAttributeName switch - { - MinimumOSPlatformAttribute => PlatformAttributeType.MinimumOSPlatformAttribute, - ObsoletedInOSPlatformAttribute => PlatformAttributeType.ObsoletedInOSPlatformAttribute, - RemovedInOSPlatformAttribute => PlatformAttributeType.RemovedInOSPlatformAttribute, - _ => throw new NotImplementedException(), - }; - - public override bool Equals(object obj) - { - if (obj is PlatformAttributeInfo info) - { - return Equals(info); - } - return false; - } - - public override int GetHashCode() => HashUtilities.Combine(AttributeType.GetHashCode(), PlatformName.GetHashCode(), Version.GetHashCode()); - - public static bool operator ==(PlatformAttributeInfo left, PlatformAttributeInfo right) => left.Equals(right); - - public static bool operator !=(PlatformAttributeInfo left, PlatformAttributeInfo right) => !(left == right); - - public bool Equals(PlatformAttributeInfo other) => - AttributeType == other.AttributeType && PlatformName.Equals(other.PlatformName, StringComparison.InvariantCultureIgnoreCase) && Version.Equals(other.Version); - }*/ - private static bool TryParsePlatformNameAndVersion(string osString, out string osPlatformName, [NotNullWhen(true)] out Version? version) { version = null; diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs index 1e89a561e3..54107d0345 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -23,19 +23,19 @@ public sealed partial class PlatformCompatabilityAnalyzer { private RuntimeMethodValue(string invokedPlatformCheckMethodName, string platformPropertyName, Version version, bool negated) { - InvokedPlatformCheckMethodName = invokedPlatformCheckMethodName ?? throw new ArgumentNullException(nameof(invokedPlatformCheckMethodName)); - PlatformPropertyName = platformPropertyName ?? throw new ArgumentNullException(nameof(platformPropertyName)); + 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 InvokedPlatformCheckMethodName { get; } - public string PlatformPropertyName { get; } + public string InvokedMethodName { get; } + public string PlatformName { get; } public Version Version { get; } public bool Negated { get; } public IAbstractAnalysisValue GetNegatedValue() - => new RuntimeMethodValue(InvokedPlatformCheckMethodName, PlatformPropertyName, Version, !Negated); + => new RuntimeMethodValue(InvokedMethodName, PlatformName, Version, !Negated); public static bool TryDecode( IMethodSymbol invokedPlatformCheckMethod, @@ -141,7 +141,7 @@ static bool TryDecodeOSVersionPart(IArgumentOperation argument, ValueContentAnal public override string ToString() { - var result = $"{InvokedPlatformCheckMethodName};{PlatformPropertyName};{Version}"; + var result = $"{InvokedMethodName};{PlatformName};{Version}"; if (Negated) { result = $"!{result}"; @@ -151,8 +151,8 @@ public override string ToString() } public bool Equals(RuntimeMethodValue other) - => InvokedPlatformCheckMethodName.Equals(other.InvokedPlatformCheckMethodName, StringComparison.OrdinalIgnoreCase) && - PlatformPropertyName.Equals(other.PlatformPropertyName, StringComparison.OrdinalIgnoreCase) && + => InvokedMethodName.Equals(other.InvokedMethodName, StringComparison.OrdinalIgnoreCase) && + PlatformName.Equals(other.PlatformName, StringComparison.OrdinalIgnoreCase) && Version.Equals(other.Version) && Negated == other.Negated; @@ -160,7 +160,7 @@ public override bool Equals(object obj) => obj is RuntimeMethodValue otherInfo && Equals(otherInfo); public override int GetHashCode() - => HashUtilities.Combine(InvokedPlatformCheckMethodName.GetHashCode(), PlatformPropertyName.GetHashCode(), Version.GetHashCode(), Negated.GetHashCode()); + => HashUtilities.Combine(InvokedMethodName.GetHashCode(), PlatformName.GetHashCode(), Version.GetHashCode(), Negated.GetHashCode()); bool IEquatable.Equals(IAbstractAnalysisValue other) => other is RuntimeMethodValue otherInfo && Equals(otherInfo); diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index dcd35edb00..66844d6ec3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -19,50 +19,50 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA1416"; private static readonly ImmutableArray s_platformCheckMethodNames = ImmutableArray.Create(IsOSPlatformOrLater, IsOSPlatformEarlierThan); - private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(MinimumOSPlatformAttribute, ObsoletedInOSPlatformAttribute, RemovedInOSPlatformAttribute); + private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(SupportedOSPlatformAttribute, ObsoletedInOSPlatformAttribute, UnsupportedOSPlatformAttribute); private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); - private static readonly LocalizableString s_localizableAddedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckAddedMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private static readonly LocalizableString s_localizableSupportedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckSupportedMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableObsoleteMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckObsoleteMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); - private static readonly LocalizableString s_localizableRemovedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckRemovedMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private static readonly LocalizableString s_localizableUnsupportedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckUnsupportedMessage), 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 MinimumOSPlatformAttribute = nameof(MinimumOSPlatformAttribute); + private const string SupportedOSPlatformAttribute = nameof(SupportedOSPlatformAttribute); private const string ObsoletedInOSPlatformAttribute = nameof(ObsoletedInOSPlatformAttribute); - private const string RemovedInOSPlatformAttribute = nameof(RemovedInOSPlatformAttribute); + private const string UnsupportedOSPlatformAttribute = nameof(UnsupportedOSPlatformAttribute); // Platform guard method names private const string IsOSPlatformOrLater = nameof(IsOSPlatformOrLater); private const string IsOSPlatformEarlierThan = nameof(IsOSPlatformEarlierThan); - internal static DiagnosticDescriptor MinimumOsRule = DiagnosticDescriptorHelper.Create(RuleId, + internal static DiagnosticDescriptor SupportedOsRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, - s_localizableAddedMessage, + s_localizableSupportedMessage, DiagnosticCategory.Interoperability, RuleLevel.BuildWarning, description: s_localizableDescription, isPortedFxCopRule: false, isDataflowRule: false); - internal static DiagnosticDescriptor ObsoleteRule = DiagnosticDescriptorHelper.Create(RuleId, + internal static DiagnosticDescriptor ObsoleteOsRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, - s_localizableAddedMessage, + s_localizableSupportedMessage, DiagnosticCategory.Interoperability, RuleLevel.BuildWarning, description: s_localizableObsoleteMessage, isPortedFxCopRule: false, isDataflowRule: false); - internal static DiagnosticDescriptor RemovedRule = DiagnosticDescriptorHelper.Create(RuleId, + internal static DiagnosticDescriptor UnsupportedOsRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, - s_localizableAddedMessage, + s_localizableSupportedMessage, DiagnosticCategory.Interoperability, RuleLevel.BuildWarning, - description: s_localizableRemovedMessage, + description: s_localizableUnsupportedMessage, isPortedFxCopRule: false, isDataflowRule: false); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(MinimumOsRule, ObsoleteRule, RemovedRule); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(SupportedOsRule, ObsoleteOsRule, UnsupportedOsRule); public override void Initialize(AnalysisContext context) { @@ -97,11 +97,6 @@ static ImmutableArray GetRuntimePlatformGuardMethods(INamedTypeSy } } - private void AnalyzeSymbolAction(SymbolAnalysisContext obj) - { - throw new NotImplementedException(); - } - private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, ImmutableArray guardMethods, INamedTypeSymbol osPlatformType) { var platformSpecificOperations = PooledConcurrentDictionary.GetInstance(); @@ -136,7 +131,7 @@ private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, I var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); var analysisResult = GlobalFlowStateAnalysis.TryGetOrComputeResult( cfg, context.OwningSymbol, CreateOperationVisitor, wellKnownTypeProvider, - context.Options, MinimumOsRule, performValueContentAnalysis: true, + context.Options, SupportedOsRule, performValueContentAnalysis: true, context.CancellationToken, out var valueContentAnalysisResult); if (analysisResult == null) @@ -197,35 +192,35 @@ private static bool IsKnownValueGuarded(PlatformAttributes attribute, GlobalFlow { if (!info.Negated) { - if (attribute.SupportedPlatforms.TryGetValue(info.PlatformPropertyName, out var versions)) + if (attribute.SupportedPlatforms.TryGetValue(info.PlatformName, out var versions)) { - if (info.InvokedPlatformCheckMethodName == IsOSPlatformOrLater) + if (info.InvokedMethodName == IsOSPlatformOrLater) { foreach (var version in versions) { - if (AttributeVersionsMatch(PlatformAttributeType.MinimumOSPlatformAttribute, version, info.Version)) + if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, version, info.Version)) { return true; } } } } - if (attribute.UnsupportedPlatforms.TryGetValue(info.PlatformPropertyName, out versions)) + if (attribute.UnsupportedPlatforms.TryGetValue(info.PlatformName, out versions)) { - if (info.InvokedPlatformCheckMethodName == IsOSPlatformEarlierThan) + if (info.InvokedMethodName == IsOSPlatformEarlierThan) { foreach (var version in versions) { - if (AttributeVersionsMatch(PlatformAttributeType.RemovedInOSPlatformAttribute, version, info.Version)) + if (AttributeVersionsMatch(PlatformAttributeType.UnsupportedOSPlatformAttribute, version, info.Version)) { return true; } } } } - if (attribute.ObsoletedPlatforms.TryGetValue(info.PlatformPropertyName, out var obsoletedVersion)) + if (attribute.ObsoletedPlatforms.TryGetValue(info.PlatformName, out var obsoletedVersion)) { - if (info.InvokedPlatformCheckMethodName == IsOSPlatformEarlierThan) + if (info.InvokedMethodName == IsOSPlatformEarlierThan) { if (AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, obsoletedVersion, info.Version)) { @@ -257,7 +252,7 @@ private static void ReportDiagnostics(IOperation operation, PlatformAttributes a { foreach (var version in attr.Value) { - context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.MinimumOSPlatformAttribute), + context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.SupportedOSPlatformAttribute), operationName ?? string.Empty, attr.Key, version.ToString())); } } @@ -268,7 +263,7 @@ private static void ReportDiagnostics(IOperation operation, PlatformAttributes a { foreach (var version in attr.Value) { - context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.RemovedInOSPlatformAttribute), + context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.UnsupportedOSPlatformAttribute), operationName ?? string.Empty, attr.Key, version.ToString())); } } @@ -277,7 +272,7 @@ private static void ReportDiagnostics(IOperation operation, PlatformAttributes a { foreach (var attr in attributes.ObsoletedPlatforms) { - context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.MinimumOSPlatformAttribute), + context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.SupportedOSPlatformAttribute), operationName ?? string.Empty, attr.Key, attr.Value.ToString())); } } @@ -312,7 +307,7 @@ private static void AnalyzeOperation(IOperation operation, OperationAnalysisCont } } - if (operationAttributes.Initialized && operationAttributes.HasAttribute) + if (operationAttributes.HasAttribute) { if (!platformSpecificMembers.TryGetValue(context.ContainingSymbol, out var callSiteAttribute)) { @@ -322,7 +317,7 @@ private static void AnalyzeOperation(IOperation operation, OperationAnalysisCont } } - if (callSiteAttribute.Initialized && callSiteAttribute.HasAttribute) + if (callSiteAttribute.HasAttribute) { if (!IsSuppressedByCallSite(operationAttributes, callSiteAttribute, out PlatformAttributes notSuppressedAttributes)) { @@ -505,7 +500,7 @@ private static bool SuppressedBySupported(SmallDictionary AttributeVersionsMatch(PlatformAttributeType.MinimumOSPlatformAttribute, calledVersion, v))) //v <= calledVersion)) + if (supportedVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, calledVersion, v))) //v <= calledVersion)) { return true; } @@ -522,7 +517,7 @@ private static bool OptionalOsVersionsSuppressed(SmallDictionary AttributeVersionsMatch(PlatformAttributeType.MinimumOSPlatformAttribute, supportedVersion, v)))//supportedVersion <= v)) + if (!suppressingVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, supportedVersion, v)))//supportedVersion <= v)) { AddToDiagnostics(key, notSuppressedVersions, supportedVersion); @@ -554,7 +549,7 @@ private static bool MandatoryOsVersionsSuppressed(SmallDictionary AttributeVersionsMatch(PlatformAttributeType.MinimumOSPlatformAttribute, checkingVersion, v))) //checkingVersion <= v)) + if (!suppressingVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, checkingVersion, v))) //checkingVersion <= v)) { AddToDiagnostics(key, notSuppressedVersions, checkingVersion); @@ -593,9 +588,9 @@ pOperation.Parent is IBinaryOperation bo && private static DiagnosticDescriptor SelectRule(PlatformAttributeType attributeType) => attributeType switch { - PlatformAttributeType.MinimumOSPlatformAttribute => MinimumOsRule, - PlatformAttributeType.ObsoletedInOSPlatformAttribute => ObsoleteRule, - _ => RemovedRule, + PlatformAttributeType.SupportedOSPlatformAttribute => SupportedOsRule, + PlatformAttributeType.ObsoletedInOSPlatformAttribute => ObsoleteOsRule, + _ => UnsupportedOsRule, }; private static bool FindPlatformAttributesApplied(ImmutableArray immediateAttributes, ISymbol containingSymbol, out PlatformAttributes attributes) @@ -662,7 +657,7 @@ attribute.ConstructorArguments[0] is { } argument && } else { - //diagnostic = Diagnostic.Create(InvalidPlatformVersionRule, osAttribute.ApplicationSyntaxReference.GetSyntax().GetLocation()); + // report Diagnostic.Create(InvalidPlatformVersionRule, osAttribute.ApplicationSyntaxReference.GetSyntax().GetLocation()); } return attributes; @@ -671,8 +666,8 @@ attribute.ConstructorArguments[0] is { } argument && private static SmallDictionary> SwitchAttrributes(string osAttributeName, PlatformAttributes attributes) => osAttributeName switch { - MinimumOSPlatformAttribute => attributes.SupportedPlatforms, - RemovedInOSPlatformAttribute => attributes.UnsupportedPlatforms, + SupportedOSPlatformAttribute => attributes.SupportedPlatforms, + UnsupportedOSPlatformAttribute => attributes.UnsupportedPlatforms, _ => throw new NotImplementedException(), }; @@ -691,7 +686,7 @@ private static void AddOrSetVersion(SmallDictionary Using platform dependendent API on a component makes the code no longer work across all platforms. - + '{0}' requires {1} {2} version or later '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version + + '{0}' is not supported or has been removed since {1} {2} version String parameters passed by value with the 'OutAttribute' can destabilize the runtime if the string is an interned string. 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 e60cbdd83d..cfaa75475e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1472,17 +1472,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 015dd51a8a..07e2c1badd 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1472,17 +1472,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 df8de2fedf..1ae193f5f7 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1472,17 +1472,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 19c3362153..df3a4c2e9a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1472,17 +1472,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 bf32b98004..3ae4e1808e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1472,17 +1472,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 b0b5e6b283..c307275095 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1472,17 +1472,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 5a1533ebf4..8b9218f8ef 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1472,17 +1472,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 1b560e7286..05101d83dc 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1473,17 +1473,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 7b47a3cf6a..ba77bec00d 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 @@ -1472,17 +1472,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 bb2bf95cb5..01722be3d0 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1472,17 +1472,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 0c0b2bea28..aaaedd5b38 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1472,17 +1472,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 de170bb208..d0784cadae 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 @@ -1472,17 +1472,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 abbf75e425..70f2985606 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 @@ -1472,17 +1472,17 @@ '{0}' has been deprecated since {1} {2} version - - '{0}' has been removed since {1} {2} version - '{0}' has been removed since {1} {2} version - - Call of platform dependent API Call of platform dependent API - + + '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since {1} {2} version + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs similarity index 94% rename from src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs rename to src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index 6f70306084..25c3095db4 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCalls.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -22,7 +22,7 @@ public void M1() if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) M2(); } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -40,7 +40,7 @@ Public Sub M1() If RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3) Then M2() End Sub - + Public Sub M2() End Sub End Class @@ -69,7 +69,7 @@ public void M1() if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 8, 1, 2, 3)) [|M2()|]; } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -99,7 +99,7 @@ void M1() } } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } @@ -131,7 +131,7 @@ void M1() } } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } @@ -178,7 +178,7 @@ void M1() } } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } @@ -213,7 +213,7 @@ If RuntimeInformationHelper.IsOSPlatformOrLater(""Windows10.1.3"") Then End If End Sub - + Private Sub M2() End Sub @@ -256,9 +256,9 @@ public PlatformEnum M2(PlatformEnum option) public enum PlatformEnum { - [MinimumOSPlatform(""Windows10.0"")] + [SupportedOSPlatform(""Windows10.0"")] Windows10, - [MinimumOSPlatform(""Linux4.8"")] + [SupportedOSPlatform(""Linux4.8"")] Linux48, NoPlatform } @@ -275,7 +275,7 @@ public async Task OsDependentProperty_GuardedCall_SimpleIfElse() public class Test { - [RemovedInOSPlatform(""Linux4.1"")] + [UnsupportedOSPlatform(""Linux4.1"")] public string RemovedProperty { get; set;} public void M1() @@ -329,7 +329,7 @@ public void M1() public class C { - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public C() { } @@ -364,7 +364,7 @@ public void M1() } } } -[MinimumOSPlatform(""Windows10.1.2.3"")] +[SupportedOSPlatform(""Windows10.1.2.3"")] public class OsDependentClass { public void M2() @@ -390,7 +390,7 @@ End If End Sub End Class - + Public Class OsDependentClass Public Sub M2() End Sub @@ -424,7 +424,7 @@ void Test() Test(); } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -470,7 +470,7 @@ public void M1() action.Invoke(); } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -490,7 +490,7 @@ public class Test { public delegate void Del(); - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public event Del SampleEvent; public void M1() @@ -539,7 +539,7 @@ public class Test { public delegate void Del(); - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void DelegateMethod() { } @@ -596,7 +596,7 @@ void M1() } } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } @@ -621,7 +621,7 @@ public void M1() else M2(); } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -649,7 +649,7 @@ public void M1() else M2(); } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -674,7 +674,7 @@ public void M1() return; M2(); } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -693,7 +693,7 @@ If Not RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2 M2() End Sub - + Public Sub M2() End Sub End Class @@ -737,7 +737,7 @@ public void M1() [|M2()|]; } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -779,7 +779,7 @@ public void M1() [|M2()|]; } } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -819,7 +819,7 @@ public void M1() [|M2()|]; } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -871,7 +871,7 @@ public void M1() [|M2()|]; } } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -919,7 +919,7 @@ public void M1() [|M2()|]; } } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -960,7 +960,7 @@ void M1() } } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } @@ -983,7 +983,7 @@ ElseIf RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 13) OrEl End If End Sub - + Private Sub M2() End Sub End Class @@ -1029,7 +1029,7 @@ void M1() [|M2()|]; } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } @@ -1057,7 +1057,7 @@ void M1() M2(); } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } @@ -1076,7 +1076,7 @@ Private Sub M1() M2() End Sub - + Private Sub M2() End Sub End Class @@ -1112,7 +1112,7 @@ void M1() [|M2()|]; } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } @@ -1138,7 +1138,7 @@ void M1() } } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } @@ -1164,7 +1164,7 @@ void M1() } } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } @@ -1211,7 +1211,7 @@ void M1(bool flag1, bool flag2) } } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } @@ -1245,7 +1245,7 @@ If flag1 OrElse flag2 Then End If End Sub - + Private Sub M2() End Sub End Class @@ -1274,7 +1274,7 @@ void M1() [|M2()|]; } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index cff4ee4c28..0ed76dd17c 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -29,10 +29,10 @@ public void M1() { [|WindowsOnly()|]; [|Obsoleted()|]; - [|Removed()|]; + [|Unsupported()|]; [|ObsoletedOverload()|]; } - [MinimumOSPlatform(""Windows10.1.1.1"")] + [SupportedOSPlatform(""Windows10.1.1.1"")] public void WindowsOnly() { } @@ -44,8 +44,8 @@ public void Obsoleted() public void ObsoletedOverload() { } - [RemovedInOSPlatform(""Linux4.1"")] - public void Removed() + [UnsupportedOSPlatform(""Linux4.1"")] + public void Unsupported() { } } @@ -60,10 +60,10 @@ Public Sub M1() [|WindowsOnly()|] [|Obsoleted()|] [|ObsoletedOverload()|] - [|Removed()|] + [|Unsupported()|] End Sub - + Public Sub WindowsOnly() End Sub @@ -75,8 +75,8 @@ End Sub Public Sub ObsoletedOverload() End Sub - - Public Sub Removed() + + Public Sub Unsupported() End Sub End Class " + MockAttributesVbSource; @@ -97,21 +97,21 @@ public void M1() Windows1_2_3_4_5(); [|ObsoletedLinuxDash4_1()|]; [|ObsoletedLinuxStar4_1()|]; - [|RemovedLinu4_1()|]; + [|UnsupportedLinu4_1()|]; ObsoletedWithNullString(); - RemovedWithEmptyString(); + UnsupportedWithEmptyString(); [|WindowsOnly()|]; } - [MinimumOSPlatform(""Windows"")] + [SupportedOSPlatform(""Windows"")] public void WindowsOnly() { } - [MinimumOSPlatform(""Windows10"")] + [SupportedOSPlatform(""Windows10"")] public void Windows10() { } - [MinimumOSPlatform(""Windows1.2.3.4.5"")] + [SupportedOSPlatform(""Windows1.2.3.4.5"")] public void Windows1_2_3_4_5() { } @@ -127,12 +127,12 @@ public void ObsoletedLinuxStar4_1() public void ObsoletedWithNullString() { } - [RemovedInOSPlatform(""Linu4.1"")] - public void RemovedLinu4_1() + [UnsupportedOSPlatform(""Linu4.1"")] + public void UnsupportedLinu4_1() { } - [RemovedInOSPlatform("""")] - public void RemovedWithEmptyString() + [UnsupportedOSPlatform("""")] + public void UnsupportedWithEmptyString() { } } @@ -148,12 +148,12 @@ public async Task OsDependentPropertyCalledWarns() public class Test { - [MinimumOSPlatform(""Windows10.1.1"")] + [SupportedOSPlatform(""Windows10.1.1"")] public string WindowsStringProperty { get; set; } [ObsoletedInOSPlatform(""ios4.1"")] public int ObsoleteIntProperty { get; set; } - [RemovedInOSPlatform(""Linux4.1"")] - public byte RemovedProperty { get; } + [UnsupportedOSPlatform(""Linux4.1"")] + public byte UnsupportedProperty { get; } public void M1() { [|WindowsStringProperty|] = ""Hello""; @@ -161,7 +161,7 @@ public void M1() M2([|WindowsStringProperty|]); [|ObsoleteIntProperty|] = 5; M3([|ObsoleteIntProperty|]); - M3([|RemovedProperty|]); + M3([|UnsupportedProperty|]); } public string M2(string option) { @@ -206,9 +206,9 @@ public object M2(object option) public static IEnumerable Create_AtrrbiuteProperty_WithCondtions() { - yield return new object[] { "MinimumOSPlatform", "string StringProperty", " == [|StringProperty|]", @"StringProperty|] = ""Hello""", "StringProperty" }; + yield return new object[] { "SupportedOSPlatform", "string StringProperty", " == [|StringProperty|]", @"StringProperty|] = ""Hello""", "StringProperty" }; yield return new object[] { "ObsoletedInOSPlatform", "int IntProperty", " > [|IntProperty|]", "IntProperty|] = 5", "IntProperty" }; - yield return new object[] { "RemovedInOSPlatform", "int RemovedProperty", " <= [|RemovedProperty|]", "RemovedProperty|] = 3", "RemovedProperty" }; + yield return new object[] { "UnsupportedOSPlatform", "int UnsupportedProperty", " <= [|UnsupportedProperty|]", "UnsupportedProperty|] = 3", "UnsupportedProperty" }; } [Fact] @@ -234,9 +234,9 @@ public PlatformEnum M2(PlatformEnum option) public enum PlatformEnum { - [MinimumOSPlatform(""windows10.0"")] + [SupportedOSPlatform(""windows10.0"")] Windows10, - [MinimumOSPlatform(""linux4.8"")] + [SupportedOSPlatform(""linux4.8"")] Linux48, NoPlatform } @@ -269,9 +269,9 @@ public PlatformEnum M2(PlatformEnum option) public enum PlatformEnum { - [MinimumOSPlatform(""Windows10.0"")] + [SupportedOSPlatform(""Windows10.0"")] Windows10, - [MinimumOSPlatform(""Linux4.8"")] + [SupportedOSPlatform(""Linux4.8"")] Linux48, NoPlatform } @@ -295,9 +295,9 @@ End Sub End Class Public Enum PlatformEnum - + Windows10 - < MinimumOSPlatform(""Linux4.8"") > + < SupportedOSPlatform(""Linux4.8"") > Linux48 NoPlatform End Enum @@ -313,9 +313,9 @@ public async Task OsDependentFieldCalledWarns() public class Test { - [MinimumOSPlatform(""Windows10.1.1.1"")] + [SupportedOSPlatform(""Windows10.1.1.1"")] string WindowsStringField; - [MinimumOSPlatform(""Windows10.1.1.1"")] + [SupportedOSPlatform(""Windows10.1.1.1"")] public int WindowsIntField { get; set; } public void M1() { @@ -376,7 +376,7 @@ public void M1() } public class B { - [MinimumOSPlatform(""Windows10.1.1.1"")] + [SupportedOSPlatform(""Windows10.1.1.1"")] public void M2() { } @@ -405,7 +405,7 @@ namespace Ns { public class B { - [MinimumOSPlatform(""Windows10.1.1.1"")] + [SupportedOSPlatform(""Windows10.1.1.1"")] public void M2() { } @@ -428,7 +428,7 @@ End Class Namespace Ns Public Class B - + Public Sub M2() End Sub End Class @@ -454,7 +454,7 @@ public void M1() public class C { - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public C() { } @@ -480,7 +480,7 @@ public void M1() [|odc.M2()|]; } } -[MinimumOSPlatform(""Windows10.1.2.3"")] +[SupportedOSPlatform(""Windows10.1.2.3"")] public class OsDependentClass { public void M2() @@ -500,7 +500,7 @@ Public Sub M1() End Sub End Class - + Public Class OsDependentClass Public Sub M2() End Sub @@ -526,7 +526,7 @@ void Test() Test(); } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -543,7 +543,7 @@ public async Task LocalGuardedFunctionCallsOsDependentMemberNotWarns() public class Test { - [MinimumOSPlatform(""Windows10.2"")] + [SupportedOSPlatform(""Windows10.2"")] public void M1() { void Test() @@ -553,7 +553,7 @@ void Test() Test(); } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -582,7 +582,7 @@ public void M1() }; } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -600,7 +600,7 @@ public async Task AttributedLambdaCallsOsDependentMemberNotWarn() public class C { - [MinimumOSPlatform(""Windows10.13"")] + [SupportedOSPlatform(""Windows10.13"")] public void M1() { void Test() => M2(); @@ -612,7 +612,7 @@ public void M1() }; } - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { } @@ -631,7 +631,7 @@ public class Test { public delegate void Del(); - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public event Del SampleEvent; public void M1() @@ -657,7 +657,7 @@ Imports System.Runtime.Versioning Public Class Test Public Delegate Sub Del() - + Public Event SampleEvent As Del Public Sub M1() @@ -686,7 +686,7 @@ public class Test { public delegate void Del(); // The attribute not supported on delegates, so no tets for that - [MinimumOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] public void DelegateMethod() { } @@ -714,7 +714,7 @@ public void M1() odc.M2(); } } - [assembly:MinimumOSPlatform(""Windows10.1.2.3"")] + [assembly:SupportedOSPlatform(""Windows10.1.2.3"")] namespace ns { public class OsDependentClass @@ -729,7 +729,7 @@ await VerifyCS.VerifyAnalyzerAsync(source, VerifyCS.Diagnostic(RuntimePlatformCheckAnalyzer2.Rule).WithSpan(10, 21, 10, 29).WithArguments("M2", "Windows", "10.1.2.3")); }*/ - public static IEnumerable MinimumOsAttributeTestData() + public static IEnumerable SupportedOsAttributeTestData() { yield return new object[] { "Windows10.1.2.3", "Windows10.1.2.3", false }; yield return new object[] { "Windows10.1.2.3", "Windows10.1.3.3", false }; @@ -749,15 +749,15 @@ public static IEnumerable MinimumOsAttributeTestData() } [Theory] - [MemberData(nameof(MinimumOsAttributeTestData))] - public async Task MethodOfOsDependentClassSuppressedWithMinimumOsAttribute(string dependentVersion, string suppressingVersion, bool warn) + [MemberData(nameof(SupportedOsAttributeTestData))] + public async Task MethodOfOsDependentClassSuppressedWithSupportedOsAttribute(string dependentVersion, string suppressingVersion, bool warn) { var source = @" using System.Runtime.Versioning; public class Test { - [MinimumOSPlatform(""" + suppressingVersion + @""")] + [SupportedOSPlatform(""" + suppressingVersion + @""")] public void M1() { OsDependentClass odc = new OsDependentClass(); @@ -765,7 +765,7 @@ public void M1() } } -[MinimumOSPlatform(""" + dependentVersion + @""")] +[SupportedOSPlatform(""" + dependentVersion + @""")] public class OsDependentClass { public void M2() @@ -777,8 +777,8 @@ public void M2() if (warn) { await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(9, 32, 9, 54).WithArguments(".ctor", "Windows", "10.1.2.3"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithSpan(9, 32, 9, 54).WithArguments(".ctor", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); } else { @@ -787,13 +787,13 @@ await VerifyAnalyzerAsyncCs(source, } [Theory] - [MemberData(nameof(ObsoletedRemovedAttributeTestData))] + [MemberData(nameof(ObsoletedUnsupportedAttributeTestData))] public async Task MethodOfOsDependentClassSuppressedWithObsoleteAttribute(string dependentVersion, string suppressingVersion, bool warn) { var source = @" using System.Runtime.Versioning; -[MinimumOSPlatform(""Windows"")] +[SupportedOSPlatform(""Windows"")] public class Test { [ObsoletedInOSPlatform(""" + suppressingVersion + @""")] @@ -804,7 +804,7 @@ public void M1() } } -[MinimumOSPlatform(""Windows"")] +[SupportedOSPlatform(""Windows"")] [ObsoletedInOSPlatform(""" + dependentVersion + @""")] public class OsDependentClass { @@ -817,8 +817,8 @@ public void M2() if (warn) { await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteRule).WithLocation(10, 32).WithArguments(".ctor", "Windows", "10.1.2.3"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteRule).WithLocation(11, 9).WithArguments("M2", "Windows", "10.1.2.3")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(10, 32).WithArguments(".ctor", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(11, 9).WithArguments("M2", "Windows", "10.1.2.3")); } else { @@ -826,7 +826,7 @@ await VerifyAnalyzerAsyncCs(source, } } - public static IEnumerable ObsoletedRemovedAttributeTestData() + public static IEnumerable ObsoletedUnsupportedAttributeTestData() { yield return new object[] { "Windows10.1.2.3", "Windows10.1.2.3", false }; yield return new object[] { "Windows10.1.2.3", "MacOs10.1.3.3", true }; @@ -842,15 +842,15 @@ public static IEnumerable ObsoletedRemovedAttributeTestData() } [Theory] - [MemberData(nameof(ObsoletedRemovedAttributeTestData))] - public async Task MethodOfOsDependentClassSuppressedWithRemovedAttribute(string dependentVersion, string suppressingVersion, bool warn) + [MemberData(nameof(ObsoletedUnsupportedAttributeTestData))] + public async Task MethodOfOsDependentClassSuppressedWithUnsupportedAttribute(string dependentVersion, string suppressingVersion, bool warn) { var source = @" using System.Runtime.Versioning; public class Test { - [RemovedInOSPlatform(""" + suppressingVersion + @""")] + [UnsupportedOSPlatform(""" + suppressingVersion + @""")] public void M1() { OsDependentClass odc = new OsDependentClass(); @@ -858,7 +858,7 @@ public void M1() } } -[RemovedInOSPlatform(""" + dependentVersion + @""")] +[UnsupportedOSPlatform(""" + dependentVersion + @""")] public class OsDependentClass { public void M2() @@ -870,8 +870,8 @@ public void M2() if (warn) { await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.MinimumOsRule).WithSpan(9, 32, 9, 54).WithArguments(".ctor", "Windows", "10.1.2.3"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RemovedRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithSpan(9, 32, 9, 54).WithArguments(".ctor", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); } else { @@ -949,9 +949,9 @@ public TargetPlatformAttribute(string platformName) : base(platformName) AttributeTargets.Field | AttributeTargets.Struct, AllowMultiple = true, Inherited = false)] - public sealed class MinimumOSPlatformAttribute : OSPlatformAttribute + public sealed class SupportedOSPlatformAttribute : OSPlatformAttribute { - public MinimumOSPlatformAttribute(string platformName) : base(platformName) + public SupportedOSPlatformAttribute(string platformName) : base(platformName) { } } @@ -965,9 +965,9 @@ public MinimumOSPlatformAttribute(string platformName) : base(platformName) AttributeTargets.Field | AttributeTargets.Struct, AllowMultiple = true, Inherited = false)] - public sealed class RemovedInOSPlatformAttribute : OSPlatformAttribute + public sealed class UnsupportedOSPlatformAttribute : OSPlatformAttribute { - public RemovedInOSPlatformAttribute(string platformName) : base(platformName) + public UnsupportedOSPlatformAttribute(string platformName) : base(platformName) { } } @@ -1017,7 +1017,7 @@ End Sub End Class - Public NotInheritable Class MinimumOSPlatformAttribute + Public NotInheritable Class SupportedOSPlatformAttribute Inherits OSPlatformAttribute Public Sub New(ByVal platformName As String) @@ -1026,7 +1026,7 @@ End Sub End Class - Public NotInheritable Class RemovedInOSPlatformAttribute + Public NotInheritable Class UnsupportedOSPlatformAttribute Inherits OSPlatformAttribute Public Sub New(ByVal platformName As String) From 3103ec7d28ed920d0f9bfb48d7bf53a864ffcb61 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 10 Aug 2020 00:48:48 -0700 Subject: [PATCH 17/48] Update attributes handling, add/remove new/old guard methods support and update tests accordingly --- .../PlatformCompatabilityAnalyzer.Data.cs | 26 +- ...mCompatabilityAnalyzer.OperationVisitor.cs | 2 +- .../PlatformCompatabilityAnalyzer.Value.cs | 110 ++- .../PlatformCompatabilityAnalyzer.cs | 630 ++++++++++-------- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 425 ++++++------ .../PlatformCompatabilityAnalyzerTests.cs | 10 +- src/Utilities/Compiler/WellKnownTypeNames.cs | 1 + 7 files changed, 701 insertions(+), 503 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index ae6c245129..bc2331e9a6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -2,10 +2,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Analyzer.Utilities; -using Analyzer.Utilities.PooledObjects; -using Microsoft.CodeAnalysis; namespace Microsoft.NetCore.Analyzers.InteropServices { @@ -18,22 +14,14 @@ private enum PlatformAttributeType UnsupportedOSPlatformAttribute, } -#pragma warning disable CA1815 // Override equals and operator equals on value types - private readonly struct PlatformAttributes -#pragma warning restore CA1815 // Override equals and operator equals on value types + private class PlatformAttributes { - public PlatformAttributes(SmallDictionary> supportedPlatforms, - SmallDictionary> unsupportedPlatforms, - SmallDictionary obsoletedPlatforms) - { - SupportedPlatforms = supportedPlatforms; - UnsupportedPlatforms = unsupportedPlatforms; - ObsoletedPlatforms = obsoletedPlatforms; - } - public bool HasAttribute => SupportedPlatforms.Any() || UnsupportedPlatforms.Any() || ObsoletedPlatforms.Any(); - public SmallDictionary> SupportedPlatforms { get; } - public SmallDictionary> UnsupportedPlatforms { get; } - public SmallDictionary ObsoletedPlatforms { get; } + public Version? Obsoleted { get; set; } + 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 || Obsoleted != null; } private static bool TryParsePlatformNameAndVersion(string osString, out string osPlatformName, [NotNullWhen(true)] out Version? version) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs index 79546e6e2a..b46d0fe006 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs @@ -34,7 +34,7 @@ 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 RuntimeMethodValue.TryDecode(method, visitedArguments, DataFlowAnalysisContext.ValueContentAnalysisResultOpt, _osPlatformType, out var platformInfo) ? new GlobalFlowStateAnalysisValueSet(platformInfo) : diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs index 54107d0345..74caff1d8b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Immutable; -using System.Diagnostics; using Analyzer.Utilities.PooledObjects; using System.Linq; using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; @@ -19,6 +18,16 @@ namespace Microsoft.NetCore.Analyzers.InteropServices public sealed partial class PlatformCompatabilityAnalyzer { + private const string Browser = nameof(Browser); + private const string Linux = nameof(Linux); + private const string FreeBSD = nameof(FreeBSD); + private const string Android = nameof(Android); + private const string IOS = nameof(IOS); + private const string MacOS = nameof(MacOS); + private const string TvOS = nameof(TvOS); + private const string WatchOS = nameof(WatchOS); + private const string Windows = nameof(Windows); + private readonly struct RuntimeMethodValue : IAbstractAnalysisValue, IEquatable { private RuntimeMethodValue(string invokedPlatformCheckMethodName, string platformPropertyName, Version version, bool negated) @@ -44,39 +53,86 @@ public static bool TryDecode( INamedTypeSymbol osPlatformType, [NotNullWhen(returnValue: true)] out RuntimeMethodValue? info) { - Debug.Assert(!arguments.IsEmpty); - - if (arguments[0].Value is ILiteralOperation literal && literal.Type?.SpecialType == SpecialType.System_String) + if (arguments.IsEmpty) { - if (literal.ConstantValue.HasValue && - TryParsePlatformNameAndVersion(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) + var platformName = SwitchPlatformName(invokedPlatformCheckMethod.Name); + if (platformName != null) { - info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); + info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, new Version(0, 0), negated: false); return true; } } - - if (!TryDecodeOSPlatform(arguments, osPlatformType, out var osPlatformName) || - !TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var osVersion)) + else { - // Bail out - info = default; - return false; - } + // RuntimeInformation.IsOSPlatform(OSPlatform) + if (TryDecodeRuntimeInformationIsOSPlatform(arguments[0].Value, osPlatformType, out string? osPlatformName)) + { + info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, osPlatformName, new Version(0, 0), negated: false); + return true; + } - info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, osPlatformName, osVersion, negated: false); - return true; - } + if (arguments[0].Value is ILiteralOperation literal && + 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 RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); + return true; + } - private static bool TryDecodeOSPlatform( - ImmutableArray arguments, - INamedTypeSymbol osPlatformType, - [NotNullWhen(returnValue: true)] out string? osPlatformName) - { - return TryDecodeOSPlatform(arguments[0].Value, osPlatformType, out osPlatformName); + // OperatingSystem.IsOSPlatformVersionAtLeast(string platform, int major, int minor = 0, int build = 0, int revision = 0) + if (TryDecodeOSVersion(arguments, valueContentAnalysisResult, out version, true)) + { + info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, literal.ConstantValue.Value.ToString(), version, negated: false); + return true; + } + } + else if (arguments[0].Value is ILiteralOperation intLiteral && + intLiteral.Type?.SpecialType == SpecialType.System_Int32) + { + var platformName = SwitchVersionedPlatformName(invokedPlatformCheckMethod.Name); + if (platformName != null && TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var version)) + { + info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); + return true; + } + } + } + info = default; + return false; } - private static bool TryDecodeOSPlatform( + private static string? SwitchVersionedPlatformName(string name) + => name switch + { + IsWindowsVersionAtLeast => Windows, + IsMacOSVersionAtLeast => MacOS, + IsIOSVersionAtLeast => IOS, + IsAndroidVersionAtLeast => Android, + IsFreeBSDVersionAtLeast => FreeBSD, + IsTvOSVersionAtLeast => TvOS, + IsWatchOSVersionAtLeast => WatchOS, + _ => null + }; + + private static string? SwitchPlatformName(string name) + => name switch + { + IsWindows => Windows, + IsLinux => Linux, + IsMacOS => MacOS, + IsIOS => IOS, + IsBrowser => Browser, + IsAndroid => Android, + IsFreeBSD => FreeBSD, + IsTvOS => TvOS, + IsWatchOS => WatchOS, + _ => null + }; + + private static bool TryDecodeRuntimeInformationIsOSPlatform( IOperation argumentValue, INamedTypeSymbol osPlatformType, [NotNullWhen(returnValue: true)] out string? osPlatformName) @@ -95,11 +151,13 @@ private static bool TryDecodeOSPlatform( private static bool TryDecodeOSVersion( ImmutableArray arguments, ValueContentAnalysisResult? valueContentAnalysisResult, - [NotNullWhen(returnValue: true)] out Version? osVersion) + [NotNullWhen(returnValue: true)] out Version? osVersion, + bool skipFirst = false) { using var versionBuilder = ArrayBuilder.GetInstance(4, fillWithValue: 0); var index = 0; - foreach (var argument in arguments.Skip(1)) + + foreach (var argument in skipFirst ? arguments.Skip(1) : arguments) { if (!TryDecodeOSVersionPart(argument, valueContentAnalysisResult, out var osVersionPart)) { diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 66844d6ec3..cbb720e195 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; @@ -18,7 +19,8 @@ namespace Microsoft.NetCore.Analyzers.InteropServices public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA1416"; - private static readonly ImmutableArray s_platformCheckMethodNames = ImmutableArray.Create(IsOSPlatformOrLater, IsOSPlatformEarlierThan); + private static readonly ImmutableArray s_platformCheckMethodNames = ImmutableArray.Create(IsOSPlatformVersionAtLeast, IsOSPlatform, IsBrowser, IsLinux, IsFreeBSD, IsFreeBSDVersionAtLeast, + IsAndroid, IsAndroidVersionAtLeast, IsIOS, IsIOSVersionAtLeast, IsMacOS, IsMacOSVersionAtLeast, IsTvOS, IsTvOSVersionAtLeast, IsWatchOS, IsWatchOSVersionAtLeast, IsWindows, IsWindowsVersionAtLeast); private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(SupportedOSPlatformAttribute, ObsoletedInOSPlatformAttribute, UnsupportedOSPlatformAttribute); private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); @@ -34,8 +36,24 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private const string UnsupportedOSPlatformAttribute = nameof(UnsupportedOSPlatformAttribute); // Platform guard method names - private const string IsOSPlatformOrLater = nameof(IsOSPlatformOrLater); - private const string IsOSPlatformEarlierThan = nameof(IsOSPlatformEarlierThan); + private const string IsOSPlatformVersionAtLeast = nameof(IsOSPlatformVersionAtLeast); + private const string IsOSPlatform = nameof(IsOSPlatform); + private const string IsBrowser = nameof(IsBrowser); + private const string IsLinux = nameof(IsLinux); + private const string IsFreeBSD = nameof(IsFreeBSD); + private const string IsFreeBSDVersionAtLeast = nameof(IsFreeBSDVersionAtLeast); + private const string IsAndroid = nameof(IsAndroid); + private const string IsAndroidVersionAtLeast = nameof(IsAndroidVersionAtLeast); + private const string IsIOS = nameof(IsIOS); + private const string IsIOSVersionAtLeast = nameof(IsIOSVersionAtLeast); + private const string IsMacOS = nameof(IsMacOS); + private const string IsMacOSVersionAtLeast = nameof(IsMacOSVersionAtLeast); + private const string IsTvOS = nameof(IsTvOS); + private const string IsTvOSVersionAtLeast = nameof(IsTvOSVersionAtLeast); + private const string IsWatchOS = nameof(IsWatchOS); + private const string IsWatchOSVersionAtLeast = nameof(IsWatchOSVersionAtLeast); + private const string IsWindows = nameof(IsWindows); + private const string IsWindowsVersionAtLeast = nameof(IsWindowsVersionAtLeast); internal static DiagnosticDescriptor SupportedOsRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, @@ -71,36 +89,35 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { - var typeName = WellKnownTypeNames.SystemRuntimeInteropServicesRuntimeInformation; + var typeName = WellKnownTypeNames.SystemOperatingSystem; - if (!context.Compilation.TryGetOrCreateTypeByMetadataName(typeName + "Helper", out var runtimeInformationType)) + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(typeName + "Helper", out var operatingSystemType)) { - // TODO: remove 'typeName + "Helper"' load after tests able to use - runtimeInformationType = context.Compilation.GetOrCreateTypeByMetadataName(typeName); + // 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)) + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesOSPlatform, out var osPlatformType) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesRuntimeInformation, out var runtimeInformationType)) { return; } - var guardMethods = GetRuntimePlatformGuardMethods(runtimeInformationType!); - - //context.RegisterSymbolAction(AnalyzeSymbolAction, SymbolKind.Field, SymbolKind.Property, SymbolKind.Event, SymbolKind.Assembly, SymbolKind.Method, SymbolKind.NamedType); + var guardMethods = GetRuntimePlatformGuardMethods(runtimeInformationType, operatingSystemType!); context.RegisterOperationBlockStartAction(context => AnalyzeOperationBlock(context, guardMethods, osPlatformType)); }); - static ImmutableArray GetRuntimePlatformGuardMethods(INamedTypeSymbol runtimeInformationType) + static ImmutableArray GetRuntimePlatformGuardMethods(INamedTypeSymbol runtimeInformationType, INamedTypeSymbol operatingSystemType) { - return runtimeInformationType.GetMembers().OfType().Where(m => - s_platformCheckMethodNames.Contains(m.Name) && !m.Parameters.IsEmpty).ToImmutableArray(); + return operatingSystemType.GetMembers().OfType().Where(m => s_platformCheckMethodNames.Contains(m.Name)).ToImmutableArray(). + Add(runtimeInformationType.GetMembers().OfType().Where(m => IsOSPlatform == m.Name).FirstOrDefault()); } } private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, ImmutableArray guardMethods, INamedTypeSymbol osPlatformType) { - var platformSpecificOperations = PooledConcurrentDictionary.GetInstance(); - var platformSpecificMembers = PooledConcurrentDictionary.GetInstance(); + var platformSpecificOperations = PooledConcurrentDictionary>.GetInstance(); + var platformSpecificMembers = PooledConcurrentDictionary>.GetInstance(); context.RegisterOperationAction(context => { @@ -142,7 +159,7 @@ private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, I foreach (var platformSpecificOperation in platformSpecificOperations) { var value = analysisResult[platformSpecificOperation.Key.Kind, platformSpecificOperation.Key.Syntax]; - PlatformAttributes attribute = platformSpecificOperation.Value; + var attribute = platformSpecificOperation.Value; if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown) { @@ -184,57 +201,79 @@ private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, I }); } - private static bool IsKnownValueGuarded(PlatformAttributes attribute, GlobalFlowStateAnalysisValueSet value) + private static bool IsKnownValueGuarded(SmallDictionary attributes, GlobalFlowStateAnalysisValueSet value) { foreach (var analysisValue in value.AnalysisValues) { if (analysisValue is RuntimeMethodValue info) { - if (!info.Negated) + if (attributes.TryGetValue(info.PlatformName, out var attribute)) { - if (attribute.SupportedPlatforms.TryGetValue(info.PlatformName, out var versions)) + if (!info.Negated) { - if (info.InvokedMethodName == IsOSPlatformOrLater) + if (attribute.SupportedFirst != null) { - foreach (var version in versions) + if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, attribute.SupportedFirst, info.Version)) { - if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, version, info.Version)) - { - return true; - } + attribute.SupportedFirst = null; + continue; } } - } - if (attribute.UnsupportedPlatforms.TryGetValue(info.PlatformName, out versions)) - { - if (info.InvokedMethodName == IsOSPlatformEarlierThan) + + if (attribute.SupportedSecond != null) { - foreach (var version in versions) + if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, attribute.SupportedSecond, info.Version)) { - if (AttributeVersionsMatch(PlatformAttributeType.UnsupportedOSPlatformAttribute, version, info.Version)) - { - return true; - } + attribute.SupportedSecond = null; + continue; } } } - if (attribute.ObsoletedPlatforms.TryGetValue(info.PlatformName, out var obsoletedVersion)) + else { - if (info.InvokedMethodName == IsOSPlatformEarlierThan) + if (attribute.UnsupportedFirst != null) + { + if (AttributeVersionsMatch(PlatformAttributeType.UnsupportedOSPlatformAttribute, attribute.UnsupportedFirst, info.Version)) + { + attribute.UnsupportedFirst = null; + continue; + } + } + + if (attribute.Obsoleted != null) { - if (AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, obsoletedVersion, info.Version)) + if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, attribute.Obsoleted, info.Version)) { - return true; + attribute.Obsoleted = null; + continue; + } + } + + if (attribute.UnsupportedSecond != null) + { + if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, attribute.UnsupportedSecond, info.Version)) + { + attribute.UnsupportedSecond = null; + continue; } } } } } } - return false; + + foreach (var attribute in attributes) + { + if (attribute.Value.HasAttribute()) + { + return false; + } + } + + return true; } - private static void ReportDiagnosticsForAll(PooledConcurrentDictionary platformSpecificOperations, OperationBlockAnalysisContext context) + private static void ReportDiagnosticsForAll(PooledConcurrentDictionary> platformSpecificOperations, OperationBlockAnalysisContext context) { foreach (var platformSpecificOperation in platformSpecificOperations) { @@ -242,38 +281,38 @@ private static void ReportDiagnosticsForAll(PooledConcurrentDictionary attributes, OperationBlockAnalysisContext context) { - var operationName = GetOperationSymbol(operation)?.Name; + var operationName = GetOperationSymbol(operation)?.Name ?? string.Empty; - if (attributes.SupportedPlatforms.Any()) + foreach (var platformName in attributes.Keys) { - foreach (var attr in attributes.SupportedPlatforms) + var attribute = attributes[platformName]; + + if (attribute.SupportedFirst != null) { - foreach (var version in attr.Value) - { - context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.SupportedOSPlatformAttribute), - operationName ?? string.Empty, attr.Key, version.ToString())); - } + context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.SupportedOSPlatformAttribute), + operationName, platformName, attribute.SupportedFirst)); } - } - if (attributes.UnsupportedPlatforms.Any()) - { - foreach (var attr in attributes.UnsupportedPlatforms) + if (attribute.SupportedSecond != null) { - foreach (var version in attr.Value) - { - context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.UnsupportedOSPlatformAttribute), - operationName ?? string.Empty, attr.Key, version.ToString())); - } + context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.SupportedOSPlatformAttribute), + operationName, platformName, attribute.SupportedSecond)); } - } - if (attributes.ObsoletedPlatforms.Any()) - { - foreach (var attr in attributes.ObsoletedPlatforms) + if (attribute.UnsupportedFirst != null) { - context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.SupportedOSPlatformAttribute), - operationName ?? string.Empty, attr.Key, attr.Value.ToString())); + context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.UnsupportedOSPlatformAttribute), + operationName, platformName, attribute.UnsupportedFirst)); + } + if (attribute.UnsupportedSecond != null) + { + context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.UnsupportedOSPlatformAttribute), + operationName, platformName, attribute.UnsupportedSecond)); + } + if (attribute.Obsoleted != null) + { + context.ReportDiagnostic(operation.CreateDiagnostic(SelectRule(PlatformAttributeType.ObsoletedInOSPlatformAttribute), + operationName, platformName, attribute.Obsoleted)); } } } @@ -289,8 +328,8 @@ private static void ReportDiagnostics(IOperation operation, PlatformAttributes a }; private static void AnalyzeOperation(IOperation operation, OperationAnalysisContext context, - PooledConcurrentDictionary platformSpecificOperations, - PooledConcurrentDictionary platformSpecificMembers) + PooledConcurrentDictionary> platformSpecificOperations, + PooledConcurrentDictionary> platformSpecificMembers) { var symbol = GetOperationSymbol(operation); @@ -301,279 +340,278 @@ private static void AnalyzeOperation(IOperation operation, OperationAnalysisCont if (!platformSpecificMembers.TryGetValue(symbol.OriginalDefinition, out var operationAttributes)) { - if (FindPlatformAttributesApplied(symbol.GetAttributes(), symbol.OriginalDefinition.ContainingSymbol, out operationAttributes)) + if (TryFindPlatformAttributesApplied(symbol.GetAttributes(), symbol.OriginalDefinition.ContainingSymbol, out operationAttributes)) { platformSpecificMembers.TryAdd(symbol.OriginalDefinition, operationAttributes); } } - if (operationAttributes.HasAttribute) + if (operationAttributes != null && operationAttributes.Any()) { - if (!platformSpecificMembers.TryGetValue(context.ContainingSymbol, out var callSiteAttribute)) + if (!platformSpecificMembers.TryGetValue(context.ContainingSymbol.OriginalDefinition, out var callSiteAttribute)) { - if (FindContainingSymbolPlatformAttributes(context.ContainingSymbol, out callSiteAttribute)) + if (TryFindContainingSymbolPlatformAttributes(context.ContainingSymbol, out callSiteAttribute)) { - platformSpecificMembers.TryAdd(context.ContainingSymbol, callSiteAttribute); + platformSpecificMembers.TryAdd(context.ContainingSymbol.OriginalDefinition, callSiteAttribute); } } - if (callSiteAttribute.HasAttribute) + if (callSiteAttribute != null && callSiteAttribute.Any()) { - if (!IsSuppressedByCallSite(operationAttributes, callSiteAttribute, out PlatformAttributes notSuppressedAttributes)) + if (IsNotSuppressedByCallSite(operationAttributes, callSiteAttribute, out var notSuppressedAttributes)) { platformSpecificOperations.TryAdd(operation, notSuppressedAttributes); } } else { - platformSpecificOperations.TryAdd(operation, operationAttributes); + var copy = CopyOperationAttributes(operationAttributes); + platformSpecificOperations.TryAdd(operation, copy); } } } + private static SmallDictionary CopyOperationAttributes(SmallDictionary attributes) + { + var copy = new SmallDictionary(); + foreach (var attribute in attributes) + { + copy.Add(attribute.Key, CopyAllAttributes(new PlatformAttributes(), attribute.Value)); + } + return copy; + } + /// /// If a member has any SupportedPlatforms attribute /// /// Platform specific attributes applied to the invoked member - /// Platform specific attributes applied to the call site where the member invoked + /// Platform specific attributes applied to the call site where the member invoked /// - private static bool IsSuppressedByCallSite(PlatformAttributes operationAttributes, PlatformAttributes callSiteAttribute, out PlatformAttributes notSuppressedAttributes) + private static bool IsNotSuppressedByCallSite(SmallDictionary operationAttributes, SmallDictionary callSiteAttributes, out SmallDictionary notSuppressedAttributes) { - Debug.Assert(operationAttributes.HasAttribute && callSiteAttribute.HasAttribute); - notSuppressedAttributes = new PlatformAttributes(new SmallDictionary>(StringComparer.InvariantCultureIgnoreCase), - new SmallDictionary>(StringComparer.InvariantCultureIgnoreCase), - new SmallDictionary(StringComparer.InvariantCultureIgnoreCase)); - if (operationAttributes.SupportedPlatforms.Any()) + notSuppressedAttributes = new SmallDictionary(StringComparer.OrdinalIgnoreCase); + bool? supportedOnlyList = null; + foreach (string key in operationAttributes.Keys) { - bool? mandatoryList = null; - foreach (string key in operationAttributes.SupportedPlatforms.Keys) - { - var supportedVersions = operationAttributes.SupportedPlatforms[key]; + var attribute = operationAttributes[key]; + var diagnositcAttribute = new PlatformAttributes(); - if (operationAttributes.UnsupportedPlatforms.TryGetValue(key, out PooledSortedSet? unsupportedVersion)) + if (attribute.SupportedFirst != null) + { + if (attribute.UnsupportedFirst == null || attribute.UnsupportedFirst > attribute.SupportedFirst) // only for current platform { - if (supportedVersions.Min < unsupportedVersion.Min) // only for current platform + if (supportedOnlyList.HasValue && !supportedOnlyList.Value) + { + // report inconsistent list diagnostic + return true; // do not need to add this API to the list + } + else + { + supportedOnlyList = true; + } + + if (callSiteAttributes.TryGetValue(key, out var callSiteAttribute)) { - if (mandatoryList.HasValue && !mandatoryList.Value) + if (attribute.SupportedSecond != null) { - // report inconsistent list diagnostic - return false; + if (!MandatoryOsVersionsSuppressed(callSiteAttribute, attribute.SupportedSecond)) + { + diagnositcAttribute.SupportedSecond = (Version)attribute.SupportedSecond.Clone(); + } } else { - mandatoryList = true; + if (!MandatoryOsVersionsSuppressed(callSiteAttribute, attribute.SupportedFirst)) + { + diagnositcAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone(); + } } - if (!MandatoryOsVersionsSuppressed(callSiteAttribute.SupportedPlatforms, key, supportedVersions, notSuppressedAttributes.SupportedPlatforms)) + if (attribute.UnsupportedFirst != null) { - return false; + if (!SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst)) + { + diagnositcAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone(); + } + } + + if (attribute.Obsoleted != null) + { + if (attribute.SupportedSecond != null && attribute.SupportedSecond > attribute.Obsoleted || attribute.SupportedFirst > attribute.Obsoleted) + { + // Can supported version be greater than obsoleted? Do we want to report diagnostic about wrong version here? + } + else if (!ObsoletedSuppressed(callSiteAttribute.Obsoleted, attribute.Obsoleted)) + { + diagnositcAttribute.Obsoleted = (Version)attribute.Obsoleted.Clone(); + } } } - else if (supportedVersions.Min == unsupportedVersion.Min) + else + { + CopyAllAttributes(diagnositcAttribute, attribute); + } + } + else if (attribute.UnsupportedFirst != null) // also means Unsupported < Supported, optional list + { + if (supportedOnlyList.HasValue && supportedOnlyList.Value) { // report inconsistent list diagnostic - return false; + return true; // do not need to add this API to the list + } + else + { + supportedOnlyList = false; } - else // supported for all platforms + + if (callSiteAttributes.TryGetValue(key, out var callSiteAttribute)) { - if (mandatoryList.HasValue && mandatoryList.Value) + if (!OptionalOsVersionsSuppressed(callSiteAttribute, attribute)) { - // report Inconsistent list diagnostic + diagnositcAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone(); } - else + + if (!UnsupportedFirstSuppressed(attribute, callSiteAttribute)) { - mandatoryList = false; + diagnositcAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone(); + continue; } - if (!OptionalOsVersionsSuppressed(callSiteAttribute.SupportedPlatforms, key, supportedVersions, notSuppressedAttributes.SupportedPlatforms)) + if (attribute.UnsupportedSecond != null && !UnsupportedSecondSuppressed(attribute, callSiteAttribute)) { - return false; + diagnositcAttribute.UnsupportedSecond = (Version)attribute.UnsupportedSecond.Clone(); } } - } - else - { - if (!MandatoryOsVersionsSuppressed(callSiteAttribute.SupportedPlatforms, key, supportedVersions, notSuppressedAttributes.SupportedPlatforms)) + else { - return false; + CopyAllAttributes(diagnositcAttribute, attribute); } } } - } - - if (operationAttributes.UnsupportedPlatforms.Any()) - { - foreach (string key in operationAttributes.UnsupportedPlatforms.Keys) + else { - var unsupportedVersions = operationAttributes.UnsupportedPlatforms[key]; - - if (callSiteAttribute.SupportedPlatforms.TryGetValue(key, out PooledSortedSet? callSiteSupportedVersions)) + if (supportedOnlyList.HasValue && supportedOnlyList.Value) { - if (!SuppressedBySupported(operationAttributes.SupportedPlatforms, key, callSiteSupportedVersions)) - { - if (callSiteAttribute.UnsupportedPlatforms.TryGetValue(key, out PooledSortedSet? callSiteUnsupportedVersions)) - { - foreach (var unsupportedVersion in unsupportedVersions) - { - if (!callSiteUnsupportedVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, unsupportedVersion, v))) //unsupportedVersion < v)) - { - AddToDiagnostics(key, notSuppressedAttributes.UnsupportedPlatforms, unsupportedVersion); - return false; - } - } - } - } + // report Inconsistent list diagnostic } else { - // No any supported OS specified means for all OS - if (!operationAttributes.SupportedPlatforms.Any()) - { - if (callSiteAttribute.UnsupportedPlatforms.TryGetValue(key, out PooledSortedSet? callSiteUnsupportedVersions)) - { - foreach (var unsupportedVersion in unsupportedVersions) - { - if (!callSiteUnsupportedVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, unsupportedVersion, v))) //unsupportedVersion < v)) - { - AddToDiagnostics(key, notSuppressedAttributes.UnsupportedPlatforms, unsupportedVersion); - return false; - } - } - } - else - { - foreach (var unsupportedVersion in unsupportedVersions) - { - AddToDiagnostics(key, notSuppressedAttributes.UnsupportedPlatforms, unsupportedVersion); - } - return false; - } - } + supportedOnlyList = false; } - } - } - if (operationAttributes.ObsoletedPlatforms.Any()) - { - foreach (string key in operationAttributes.ObsoletedPlatforms.Keys) - { - var obsoletedVersion = operationAttributes.ObsoletedPlatforms[key]; - if (operationAttributes.SupportedPlatforms.TryGetValue(key, out PooledSortedSet? supportedVersion)) + if (attribute.UnsupportedFirst != null) // Unsupported for this but supported all other { - if (supportedVersion.Max < obsoletedVersion) + if (callSiteAttributes.TryGetValue(key, out var callSiteAttribute)) { - if (!callSiteAttribute.ObsoletedPlatforms.TryGetValue(key, out Version? suppressingVersion) || - !AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, obsoletedVersion, suppressingVersion)) //obsoletedVersion >= suppressingVersion) + if (!SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst)) { - notSuppressedAttributes.ObsoletedPlatforms[key] = obsoletedVersion; - return false; + diagnositcAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone(); + } + + if (attribute.UnsupportedSecond != null && !SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedSecond)) + { + diagnositcAttribute.UnsupportedSecond = (Version)attribute.UnsupportedSecond.Clone(); } } else { - // Can supported version be greater than obsoleted? Do we want to report diagnostic about wrong version? + diagnositcAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone(); + diagnositcAttribute.UnsupportedSecond = (Version?)attribute.UnsupportedSecond?.Clone(); } } - else + + if (attribute.Obsoleted != null) { - if (!operationAttributes.SupportedPlatforms.Any()) - { - // No any os specified means for all OS - if (!callSiteAttribute.ObsoletedPlatforms.TryGetValue(key, out Version? suppressingVersion) || - !AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, obsoletedVersion, suppressingVersion)) //obsoletedVersion >= suppressingVersion) - { - notSuppressedAttributes.ObsoletedPlatforms[key] = obsoletedVersion; - return false; - } - } + // When no supported attribute exist, obsoleted not expected, reoport diagnostic } } + + if (diagnositcAttribute.HasAttribute()) + { + notSuppressedAttributes[key] = diagnositcAttribute; + } } - return true; + return notSuppressedAttributes.Any(); + } + + 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(); + copyTo.Obsoleted = (Version?)copyFrom.Obsoleted?.Clone(); + return copyTo; } - private static bool SuppressedBySupported(SmallDictionary> supportedPlatforms, string key, PooledSortedSet callSiteSupportedVersions) + private static bool SuppressedByCallSiteUnsupported(PlatformAttributes callSiteAttribute, Version unsupporteAttribute) { - if (supportedPlatforms.TryGetValue(key, out PooledSortedSet? supportedVersions)) + if (callSiteAttribute.UnsupportedFirst != null && AttributeVersionsMatch(PlatformAttributeType.UnsupportedOSPlatformAttribute, unsupporteAttribute, callSiteAttribute.UnsupportedFirst) || + callSiteAttribute.UnsupportedSecond != null && AttributeVersionsMatch(PlatformAttributeType.UnsupportedOSPlatformAttribute, unsupporteAttribute, callSiteAttribute.UnsupportedSecond)) { - foreach (var calledVersion in callSiteSupportedVersions) - { - if (supportedVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, calledVersion, v))) //v <= calledVersion)) - { - return true; - } - } + return true; } - return false; } - private static bool OptionalOsVersionsSuppressed(SmallDictionary> callSitePlatforms, string key, - PooledSortedSet supportedVersions, SmallDictionary> notSuppressedVersions) + private static bool ObsoletedSuppressed(Version? callSiteObsoleted, Version checkingObsoleted) => callSiteObsoleted != null + && AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, checkingObsoleted, callSiteObsoleted); + + private static bool UnsupportedSecondSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) { - if (callSitePlatforms.TryGetValue(key, out PooledSortedSet? suppressingVersions)) + if (callSiteAttribute.SupportedFirst != null) { - foreach (var supportedVersion in supportedVersions) + if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, callSiteAttribute.SupportedFirst, attribute.SupportedFirst!) || + attribute.SupportedSecond != null && AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, callSiteAttribute.SupportedFirst, attribute.SupportedSecond)) { - if (!suppressingVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, supportedVersion, v)))//supportedVersion <= v)) - { - AddToDiagnostics(key, notSuppressedVersions, supportedVersion); - - return false; - } + return true; } } - return true; - } - private static void AddToDiagnostics(string key, SmallDictionary> versions, Version version) - { - if (versions.TryGetValue(key, out var existing)) - { - existing.Add(version); - } - else - { - var set = PooledSortedSet.GetInstance(); - set.Add(version); - versions.Add(key, set); - } + return SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedSecond!); } - private static bool MandatoryOsVersionsSuppressed(SmallDictionary> callSitePlatforms, - string key, PooledSortedSet checkingVersions, SmallDictionary> notSuppressedVersions) + private static bool UnsupportedFirstSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) { - if (callSitePlatforms.TryGetValue(key, out PooledSortedSet? suppressingVersions)) + if (callSiteAttribute.SupportedFirst != null) { - foreach (var checkingVersion in checkingVersions) + if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, callSiteAttribute.SupportedFirst, attribute.SupportedFirst!) || + attribute.SupportedSecond != null && AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, callSiteAttribute.SupportedFirst, attribute.SupportedSecond)) { - if (!suppressingVersions.Any(v => AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, checkingVersion, v))) //checkingVersion <= v)) - { - AddToDiagnostics(key, notSuppressedVersions, checkingVersion); - - return false; - } + return true; } } - else + + return SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst!); + } + + private static bool OptionalOsVersionsSuppressed(PlatformAttributes callSiteAttribute, PlatformAttributes attribute) + { + // Optianal supported attribute, if call site supports it, its versions should match + if (callSiteAttribute.SupportedFirst != null && + !(attribute.SupportedFirst <= callSiteAttribute.SupportedFirst || + (callSiteAttribute.SupportedSecond != null && attribute.SupportedFirst <= callSiteAttribute.SupportedSecond))) { - if (!notSuppressedVersions.TryGetValue(key, out var existing)) - { - existing = PooledSortedSet.GetInstance(); - notSuppressedVersions.Add(key, existing); - } - foreach (var checkingVersion in checkingVersions) - { - existing.Add(checkingVersion); - } return false; } + + // if call site not suppors it, no problem return true; } + private static bool MandatoryOsVersionsSuppressed(PlatformAttributes callSitePlatforms, Version checkingVersion) + { + if ((callSitePlatforms.SupportedFirst != null && AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, checkingVersion, callSitePlatforms.SupportedFirst)) || + (callSitePlatforms.SupportedSecond != null && AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, checkingVersion, callSitePlatforms.SupportedSecond))) + { + return true; + } + return false; + } + // Do not warn for conditional checks of platfomr specific enum value; 'if (value != FooEnum.WindowsOnlyValue)' private static bool IsWithinConditionalOperation(IFieldReferenceOperation pOperation) => pOperation.ConstantValue.HasValue && @@ -593,47 +631,44 @@ private static DiagnosticDescriptor SelectRule(PlatformAttributeType attributeTy _ => UnsupportedOsRule, }; - private static bool FindPlatformAttributesApplied(ImmutableArray immediateAttributes, ISymbol containingSymbol, out PlatformAttributes attributes) + private static bool TryFindPlatformAttributesApplied(ImmutableArray immediateAttributes, ISymbol containingSymbol, [NotNullWhen(true)] out SmallDictionary? attributes) { - attributes = new PlatformAttributes(new SmallDictionary>(StringComparer.InvariantCultureIgnoreCase), - new SmallDictionary>(StringComparer.InvariantCultureIgnoreCase), - new SmallDictionary(StringComparer.InvariantCultureIgnoreCase)); - AddPlatformAttributes(immediateAttributes, attributes); + attributes = null; + AddPlatformAttributes(immediateAttributes, ref attributes); while (containingSymbol != null) { - AddPlatformAttributes(containingSymbol.GetAttributes(), attributes); + AddPlatformAttributes(containingSymbol.GetAttributes(), ref attributes); containingSymbol = containingSymbol.ContainingSymbol; } - return attributes.HasAttribute; + return attributes != null; } - private static void AddPlatformAttributes(ImmutableArray immediateAttributes, PlatformAttributes attributes) + private static bool AddPlatformAttributes(ImmutableArray immediateAttributes, [NotNullWhen(true)] ref SmallDictionary? attributes) { foreach (AttributeData attribute in immediateAttributes) { if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name)) { - AddValidAttribute(attributes, attribute); + TryAddValidAttribute(ref attributes, attribute); } } + return attributes != null; } - private static bool FindContainingSymbolPlatformAttributes(ISymbol containingSymbol, out PlatformAttributes attributes) + private static bool TryFindContainingSymbolPlatformAttributes(ISymbol containingSymbol, [NotNullWhen(true)] out SmallDictionary? attributes) { - attributes = new PlatformAttributes(new SmallDictionary>(StringComparer.InvariantCultureIgnoreCase), - new SmallDictionary>(StringComparer.InvariantCultureIgnoreCase), - new SmallDictionary(StringComparer.InvariantCultureIgnoreCase)); + attributes = null; while (containingSymbol != null) { - AddPlatformAttributes(containingSymbol.GetAttributes(), attributes); + AddPlatformAttributes(containingSymbol.GetAttributes(), ref attributes); containingSymbol = containingSymbol.ContainingSymbol; } - return attributes.HasAttribute; + return attributes != null; } - private static PlatformAttributes AddValidAttribute(PlatformAttributes attributes, AttributeData attribute) + private static bool TryAddValidAttribute([NotNullWhen(true)] ref SmallDictionary? attributes, AttributeData attribute) { if (!attribute.ConstructorArguments.IsEmpty && attribute.ConstructorArguments[0] is { } argument && @@ -644,14 +679,27 @@ attribute.ConstructorArguments[0] is { } argument && { if (TryParsePlatformNameAndVersion(argument.Value.ToString(), out string platformName, out Version? version)) { - if (attribute.AttributeClass.Name == ObsoletedInOSPlatformAttribute) + attributes ??= new SmallDictionary(StringComparer.OrdinalIgnoreCase); + + if (!attributes.TryGetValue(platformName, out var existingAttributes)) { - attributes.ObsoletedPlatforms[platformName] = version; + existingAttributes = new PlatformAttributes(); + attributes[platformName] = existingAttributes; } - else + + switch (attribute.AttributeClass.Name) { - AddOrSetVersion(SwitchAttrributes(attribute.AttributeClass.Name, attributes), platformName, version); + case ObsoletedInOSPlatformAttribute: + AddOrUpdateObsoletedAttribute(existingAttributes, version); + break; + case SupportedOSPlatformAttribute: + AddOrUpdateSupportedAttribute(existingAttributes, version); + break; + case UnsupportedOSPlatformAttribute: + AddOrUpdateUnsupportedAttribute(existingAttributes, version); + break; } + return true; } // else report diagnostic = Diagnostic.Create(PlatformNameNullOrEmptyRule, osAttribute.ApplicationSyntaxReference.GetSyntax().GetLocation()); } @@ -660,27 +708,79 @@ attribute.ConstructorArguments[0] is { } argument && // report Diagnostic.Create(InvalidPlatformVersionRule, osAttribute.ApplicationSyntaxReference.GetSyntax().GetLocation()); } - return attributes; + return false; } - private static SmallDictionary> SwitchAttrributes(string osAttributeName, PlatformAttributes attributes) - => osAttributeName switch + private static void AddOrUpdateObsoletedAttribute(PlatformAttributes attributes, Version version) + { + if (attributes.Obsoleted != null) + { + if (attributes.Obsoleted > version) { - SupportedOSPlatformAttribute => attributes.SupportedPlatforms, - UnsupportedOSPlatformAttribute => attributes.UnsupportedPlatforms, - _ => throw new NotImplementedException(), - }; + attributes.Obsoleted = version; + } + } + else + { + attributes.Obsoleted = version; + } + } - private static void AddOrSetVersion(SmallDictionary> dictionary, string platformName, Version version) + private static void AddOrUpdateUnsupportedAttribute(PlatformAttributes attributes, Version version) { - if (dictionary.TryGetValue(platformName, out PooledSortedSet? existingVersion)) + if (attributes.UnsupportedFirst != null) { - existingVersion.Add(version); + if (attributes.UnsupportedFirst > version) + { + attributes.UnsupportedFirst = version; + } + else + { + if (attributes.UnsupportedSecond != null) + { + if (attributes.UnsupportedSecond > version) + { + attributes.UnsupportedSecond = version; + } + } + else + { + attributes.UnsupportedSecond = version; + } + } + } + else + { + attributes.UnsupportedFirst = version; + } + } + + private static void AddOrUpdateSupportedAttribute(PlatformAttributes attributes, Version version) + { + if (attributes.SupportedFirst != null) + { + if (attributes.SupportedFirst > version) + { + attributes.SupportedFirst = version; + } + else + { + if (attributes.SupportedSecond != null) + { + if (attributes.SupportedSecond < version) + { + attributes.SupportedSecond = version; + } + } + else + { + attributes.SupportedSecond = version; + } + } } else { - dictionary[platformName] = PooledSortedSet.GetInstance(); - dictionary[platformName].Add(version); + attributes.SupportedFirst = version; } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index 25c3095db4..383ec222e1 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -8,18 +8,48 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests public partial class PlatformCompatabilityAnalyzerTests { [Fact] - public async Task GuardedCalled_SimpleIf_NotWarns() + public async Task GuardedWith_IsOSPlatform_SimpleIfElse() { var source = @" using System.Runtime.Versioning; using System.Runtime.InteropServices; +class Test +{ + void M1() + { + if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + M2(); + } + else + { + [|M2()|]; + } + } + + [SupportedOSPlatform(""Windows"")] + void M2() + { + } +}" + MockAttributesCsSource + MockRuntimeApiSource; + + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task GuardedCalled_SimpleIf_NotWarns() + { + var source = @" +using System.Runtime.Versioning; +using System; + public class Test { public void M1() { [|M2()|]; - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 1, 2, 3)) M2(); } [SupportedOSPlatform(""Windows10.1.2.3"")] @@ -32,12 +62,12 @@ public void M2() var vbSource = @" Imports System.Runtime.Versioning -Imports System.Runtime.InteropServices +Imports System Public Class Test Public Sub M1() [|M2()|] - If RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3) Then M2() + If OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 1, 2, 3) Then M2() End Sub @@ -53,20 +83,20 @@ public async Task GuardedCall_MultipleSimpleIfTests() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { public void M1() { [|M2()|]; - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 1, 2, 3)) M2(); - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 10, 1, 2, 3)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 10, 1, 2, 3)) [|M2()|]; - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) M2(); - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 8, 1, 2, 3)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 8, 1, 2, 3)) [|M2()|]; } [SupportedOSPlatform(""Windows10.1.2.3"")] @@ -79,17 +109,17 @@ public void M2() } [Fact] - public async Task GuardedWith_IsOSPlatformOrLater_SimpleIfElse() + public async Task GuardedWith_IsOSPlatformVersionAtLeast_SimpleIfElse() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; class Test { void M1() { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) { M2(); } @@ -113,13 +143,14 @@ public async Task GuardedWith_IsOSPlatformEarlierThan_SimpleIfElse() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; class Test { + [SupportedOSPlatform(""Windows"")] void M1() { - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 10)) + if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(11, 0, 19222)) { [|M2()|]; M3(); @@ -131,12 +162,13 @@ void M1() } } - [SupportedOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""MacOs12.2.3"")] void M2() { } - [ObsoletedInOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Windows"")] + [ObsoletedInOSPlatform(""Windows10.0"")] void M3 () { } @@ -144,18 +176,19 @@ void M3 () await VerifyAnalyzerAsyncCs(source); } - [Fact] + /*[Fact] TODO fix VB test public async Task GuardedWith_StringOverload_SimpleIfElse() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; class Test { + [SupportedOSPlatform(""Windows"")] void M1() { - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(""Windows10.1"")) + if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10, 0, 19222)) { [|M2()|]; M3(); @@ -166,7 +199,7 @@ void M1() [|M3()|]; } - if(RuntimeInformationHelper.IsOSPlatformOrLater(""Windows10.1.3"")) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Android"",12,1)) { [|M3()|]; M2(); @@ -178,11 +211,11 @@ void M1() } } - [SupportedOSPlatform(""Windows10.1.2.3"")] + [SupportedOSPlatform(""Android10.2.3"")] void M2() { } - + [SupportedOSPlatform(""Windows"")] [ObsoletedInOSPlatform(""Windows10.1.2.3"")] void M3 () { @@ -192,11 +225,11 @@ void M3 () var vbSource = @" Imports System.Runtime.Versioning -Imports System.Runtime.InteropServices +Imports System Class Test Private Sub M1() - If RuntimeInformationHelper.IsOSPlatformEarlierThan(""Windows10.1"") Then + If OperatingSystemHelper.IsOSPlatformEarlierThan(""Windows10.1"") Then [|M2()|] M3() Else @@ -204,7 +237,7 @@ If RuntimeInformationHelper.IsOSPlatformEarlierThan(""Windows10.1"") Then [|M3()|] End If - If RuntimeInformationHelper.IsOSPlatformOrLater(""Windows10.1.3"") Then + If OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows10.1.3"") Then [|M3()|] M2() Else @@ -223,21 +256,21 @@ End Sub End Class " + MockAttributesVbSource + MockRuntimeApiSourceVb; await VerifyAnalyzerAsyncVb(vbSource); - } + }*/ [Fact] public async Task OsDependentEnumValue_GuardedCall_SimpleIfElse() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test2 { public void M1() { PlatformEnum val = [|PlatformEnum.Windows10|]; - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10)) { M2(PlatformEnum.Windows10); } @@ -271,16 +304,16 @@ public async Task OsDependentProperty_GuardedCall_SimpleIfElse() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { - [UnsupportedOSPlatform(""Linux4.1"")] + [UnsupportedOSPlatform(""Windows8.1"")] public string RemovedProperty { get; set;} public void M1() { - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 4)) + if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(8, 0, 19222)) //OperatingSystemHelper.IsOSPlatformEarlierThan { RemovedProperty = ""Hello""; string s = RemovedProperty; @@ -308,13 +341,13 @@ public async Task OsDependentConstructorOfClass_GuardedCall_SimpleIfElse() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { public void M1() { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) { C instance = new C(); instance.M2(); @@ -346,28 +379,28 @@ public async Task ConstructorAndMethodOfOsDependentClass_GuardedCall_SimpleIfEls { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { public void M1() { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) { OsDependentClass odc = new OsDependentClass(); - odc.M2(); + odc.Method2(); } else { - OsDependentClass odc = [|new OsDependentClass()|]; - [|odc.M2()|]; + OsDependentClass odc2 = [|new OsDependentClass()|]; + [|odc2.Method2()|]; } } } [SupportedOSPlatform(""Windows10.1.2.3"")] public class OsDependentClass { - public void M2() + public void Method2() { } } @@ -376,16 +409,16 @@ public void M2() var vbSource = @" Imports System.Runtime.Versioning -Imports System.Runtime.InteropServices +Imports System Public Class Test Public Sub M1() - If RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) Then + If OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) Then Dim odc As OsDependentClass = New OsDependentClass() odc.M2() Else - Dim odc As OsDependentClass = [|New OsDependentClass()|] - [|odc.M2()|] + Dim odc2 As OsDependentClass = [|New OsDependentClass()|] + [|odc2.M2()|] End If End Sub End Class @@ -404,7 +437,7 @@ public async Task LocalFunctionCallsOsDependentMember_GuardedCall_SimpleIfElse() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { @@ -412,7 +445,7 @@ public void M1() { void Test() { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2, 1)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2, 1)) { M2(); } @@ -438,14 +471,14 @@ public async Task LambdaCallsOsDependentMember_GuardedCall_SimpleIfElse() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; using System; public class Test { public void M1() { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2, 1)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2, 1)) { void Test() => M2(); Test(); @@ -458,7 +491,7 @@ public void M1() Action action = () => { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2, 1)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2, 1)) { M2(); } @@ -484,7 +517,7 @@ public async Task OsDependentEventAccessed_GuardedCall_SimpleIfElse() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { @@ -495,7 +528,7 @@ public class Test public void M1() { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) { SampleEvent += M3; } @@ -507,7 +540,7 @@ public void M1() public void M2() { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) { SampleEvent?.Invoke(); } @@ -533,7 +566,7 @@ public async Task OsDependentMethodAssignedToDelegate_GuardedCall_SimpleIfElse() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { @@ -545,7 +578,7 @@ public void DelegateMethod() } public void M1() { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) { Del handler = DelegateMethod; handler(); @@ -566,7 +599,7 @@ public async Task GuardedCall_SimpleIfElseIfElseTest() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; class Test { @@ -574,19 +607,19 @@ void M1() { [|M2()|]; - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) { M2(); } - else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 11)) + else if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(8, 0, 19222)) { [|M2()|]; } - else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + else if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222)) { [|M2()|]; } - else if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12)) + else if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 12)) { M2(); } @@ -609,14 +642,14 @@ public async Task GuardedCall_SimpleIfElseTestWithNegation() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { public void M1() { [|M2()|]; - if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + if(!OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 1, 2, 3)) [|M2()|]; else M2(); @@ -635,16 +668,16 @@ public async Task GuardedCall_SimpleIfElseIfElseTestWithNegation() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { public void M1() { [|M2()|]; - if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + if(!OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 1, 2, 3)) [|M2()|]; - else if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 1, 1)) + else if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222)) M2(); else M2(); @@ -663,14 +696,14 @@ public async Task GuardedCall_SimpleIfTestWithNegationAndReturn() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { public void M1() { [|M2()|]; - if(!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3)) + if(!OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 1, 2, 3)) return; M2(); } @@ -684,12 +717,12 @@ public void M2() var vbSource = @" Imports System.Runtime.Versioning -Imports System.Runtime.InteropServices +Imports System Public Class Test Public Sub M1() [|M2()|] - If Not RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 1, 2, 3) Then Return + If Not OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 1, 2, 3) Then Return M2() End Sub @@ -706,31 +739,31 @@ public async Task GuardedCall_SimpleIfTestWithLogicalAnd() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { public void M1() { - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && - RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) && + (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222))) { M2(); } - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) && - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12)) + if((OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222)) && + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 12)) { M2(); } - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) && - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 12)) + if((OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222)) && + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 12)) { [|M2()|]; } - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && 1 == 1) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) && 1 == 1) { M2(); } @@ -751,7 +784,7 @@ public async Task GuardedCall_SimpleIfElseTestWithLogicalAnd() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { @@ -759,8 +792,8 @@ public void M1() { [|M2()|]; - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) && - RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) && + (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222))) { M2(); } @@ -769,8 +802,8 @@ public void M1() [|M2()|]; } - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) && - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 12)) + if((OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222)) && + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 12)) { [|M2()|]; } @@ -793,26 +826,26 @@ public async Task GuardedCall_SimpleIfTestWithLogicalOr() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { public void M1() { - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || - RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) || + (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222))) { [|M2()|]; } - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + if((OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222)) || + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) { [|M2()|]; } - if(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 12) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 12) || + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) { [|M2()|]; } @@ -833,7 +866,7 @@ public async Task GuardedCall_SimpleIfElseTestWithLogicalOr() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { @@ -841,8 +874,8 @@ public void M1() { [|M2()|]; - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || - RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12)) + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) || + (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222))) { [|M2()|]; } @@ -851,8 +884,8 @@ public void M1() [|M2()|]; } - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + if((OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222)) || + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) { [|M2()|]; } @@ -861,8 +894,8 @@ public void M1() [|M2()|]; } - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) || + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) { [|M2()|]; // Even it is not meaningful check i think it is a bug, it shouldn't warn } @@ -885,7 +918,7 @@ public async Task GuardedCall_SimpleIfElseIfElseTestWithLogicalOr() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; public class Test { @@ -893,24 +926,24 @@ public void M1() { [|M2()|]; - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 5, 1)) + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) || + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 5, 1)) { [|M2()|]; } - else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 9)) + else if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 9)) { [|M2()|]; } else [|M2()|]; - if(RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Windows, 12) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222) || + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) { [|M2()|]; } - else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + else if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) { M2(); } @@ -933,7 +966,7 @@ public async Task GuardedCall_SimpleIfElseIfTestWithLogicalOrAnd() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; class Test { @@ -941,16 +974,16 @@ void M1() { [|M2()|]; - if((RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1)) && - (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 2))) + if((OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 1) || + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 1)) && + (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 12) || + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 2))) { [|M2()|]; } - else if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 13) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 3) || - RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 4)) + else if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 13) || + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 3) || + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 4)) { [|M2()|]; } @@ -970,13 +1003,13 @@ void M2() var vbSource = @" Imports System.Runtime.Versioning -Imports System.Runtime.InteropServices +Imports System Class Test Private Sub M1() - If (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 1) OrElse RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1)) AndAlso (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 12) OrElse RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 2)) Then + If (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 1) OrElse OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 1)) AndAlso (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 12) OrElse OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 2)) Then [|M2()|] - ElseIf RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 13) OrElse RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 3) OrElse RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 4) Then + ElseIf OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 13) OrElse OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 3) OrElse OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 4) Then [|M2()|] Else [|M2()|] @@ -996,21 +1029,21 @@ public async Task GuardedWith_ControlFlowAndMultipleChecks() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; class Test { void M1() { - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 8)) + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 8)) { [|M2()|]; - if (RuntimeInformationHelper.IsOSPlatformEarlierThan(OSPlatform.Linux, 2, 0)) + if (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222)) { [|M2()|]; } - else if (!RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2, 1)) + else if (!OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2, 1)) { [|M2()|]; } @@ -1044,7 +1077,7 @@ public async Task GuardedWith_DebugAssertAnalysisTest() var source = @" using System.Diagnostics; using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; class Test { @@ -1052,7 +1085,7 @@ void M1() { [|M2()|]; - Debug.Assert(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)); + Debug.Assert(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)); M2(); } @@ -1067,12 +1100,12 @@ void M2() var vbSource = @" Imports System.Diagnostics Imports System.Runtime.Versioning -Imports System.Runtime.InteropServices +Imports System Class Test Private Sub M1() [|M2()|] - Debug.Assert(RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 10, 2)) + Debug.Assert(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) M2() End Sub @@ -1089,14 +1122,14 @@ public async Task GuardedWith_ResultSavedInLocal() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; class Test { void M1() { - var x1 = RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11); - var x2 = RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Linux, 1); + var x1 = OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11); + var x2 = OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 1); if (x1) { @@ -1125,14 +1158,14 @@ public async Task GuardedWith_VersionSavedInLocal() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; class Test { void M1() { var v11 = 11; - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, v11)) + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", v11)) { M2(); } @@ -1151,14 +1184,14 @@ void M2() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; class Test { void M1() { - var platform = OSPlatform.Windows; - if (RuntimeInformationHelper.IsOSPlatformOrLater(platform, 11)) + var platform = ""Windows""; + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(platform, 11)) { [|M2()|]; } @@ -1177,7 +1210,7 @@ public async Task UnrelatedConditionCheckDoesNotInvalidateState() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; class Test { @@ -1185,7 +1218,7 @@ void M1(bool flag1, bool flag2) { [|M2()|]; - if (RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11)) + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) { M2(); @@ -1220,13 +1253,13 @@ void M2() var vbSource = @" Imports System.Runtime.Versioning -Imports System.Runtime.InteropServices +Imports System Class Test Private Sub M1(ByVal flag1 As Boolean, ByVal flag2 As Boolean) [|M2()|] - If RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows, 11) Then + If OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11) Then M2() If flag1 OrElse flag2 Then @@ -1258,7 +1291,7 @@ public async Task InterproceduralAnalysisTest() { var source = @" using System.Runtime.Versioning; -using System.Runtime.InteropServices; +using System; class Test { @@ -1281,7 +1314,7 @@ void M2() bool IsWindows11OrLater() { - return RuntimeInformationHelper.IsOSPlatformOrLater(OSPlatform.Windows,10,2,3,4); + return OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"",10,2,3,4); } }" + MockAttributesSource + MockRuntimeApiSource; @@ -1289,93 +1322,109 @@ bool IsWindows11OrLater() }*/ private readonly string MockRuntimeApiSource = @" -namespace System.Runtime.InteropServices +namespace System { - public static class RuntimeInformationHelper + public sealed class OperatingSystemHelper { #pragma warning disable CA1801, IDE0060 // Review unused parameters - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major) - { - return true; - } - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor) - { - return true; - } - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build) - { - return true; - } - public static bool IsOSPlatformOrLater(OSPlatform osPlatform, int major, int minor, int build, int revision) - { - return true; - } - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major) - { - return false; - } - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor) - { - return false; - } - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build) - { - return false; - } - public static bool IsOSPlatformEarlierThan(OSPlatform osPlatform, int major, int minor, int build, int revision) - { - return false; - } - public static bool IsOSPlatformOrLater(string platformName) => true; - public static bool IsOSPlatformEarlierThan(string platformName) => true; + public static bool IsOSPlatform(string platform) { return true; } + public static bool IsOSPlatformVersionAtLeast(string platform, int major, int minor = 0, int build = 0, int revision = 0) { return true; } + public static bool IsBrowser() { return true; } + public static bool IsLinux() { return true; } + public static bool IsFreeBSD() { return true; } + public static bool IsFreeBSDVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) { return true; } + public static bool IsAndroid() { return true; } + public static bool IsAndroidVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) { return true; } + public static bool IsIOS() { return true; } + public static bool IsIOSVersionAtLeast(int major, int minor = 0, int build = 0) { return true; } + public static bool IsMacOS() { return true; } + public static bool IsMacOSVersionAtLeast(int major, int minor = 0, int build = 0) { return true; } + public static bool IsTvOS() { return true; } + public static bool IsTvOSVersionAtLeast(int major, int minor = 0, int build = 0) { return true; } + public static bool IsWatchOS() { return true; } + public static bool IsWatchOSVersionAtLeast(int major, int minor = 0, int build = 0) { return true; } + public static bool IsWindows() { return true; } + public static bool IsWindowsVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) { return true; } #pragma warning restore CA1801, IDE0060 // Review unused parameters } }"; private readonly string MockRuntimeApiSourceVb = @" -Namespace System.Runtime.InteropServices - Module RuntimeInformationHelper - Function IsOSPlatformOrLater(ByVal osPlatform As OSPlatform, ByVal major As Integer) As Boolean +Namespace System + Public NotInheritable Class OperatingSystemHelper + Public Shared Function IsOSPlatform(ByVal platform As String) As Boolean + Return True + End Function + + Public Shared Function IsOSPlatformVersionAtLeast(ByVal platform As String, ByVal major As Integer, ByVal Optional minor As Integer = 0, ByVal Optional build As Integer = 0, ByVal Optional revision As Integer = 0) As Boolean Return True End Function - Function IsOSPlatformOrLater(ByVal osPlatform As OSPlatform, ByVal major As Integer, ByVal minor As Integer) As Boolean + Public Shared Function IsBrowser() As Boolean Return True End Function - Function IsOSPlatformOrLater(ByVal osPlatform As OSPlatform, ByVal major As Integer, ByVal minor As Integer, ByVal build As Integer) As Boolean + Public Shared Function IsLinux() As Boolean Return True End Function - Function IsOSPlatformOrLater(ByVal osPlatform As OSPlatform, ByVal major As Integer, ByVal minor As Integer, ByVal build As Integer, ByVal revision As Integer) As Boolean + Public Shared Function IsFreeBSD() As Boolean Return True End Function - Function IsOSPlatformEarlierThan(ByVal osPlatform As OSPlatform, ByVal major As Integer) As Boolean - Return False + Public Shared Function IsFreeBSDVersionAtLeast(ByVal major As Integer, ByVal Optional minor As Integer = 0, ByVal Optional build As Integer = 0, ByVal Optional revision As Integer = 0) As Boolean + Return True End Function - Function IsOSPlatformEarlierThan(ByVal osPlatform As OSPlatform, ByVal major As Integer, ByVal minor As Integer) As Boolean - Return False + Public Shared Function IsAndroid() As Boolean + Return True End Function - Function IsOSPlatformEarlierThan(ByVal osPlatform As OSPlatform, ByVal major As Integer, ByVal minor As Integer, ByVal build As Integer) As Boolean - Return False + Public Shared Function IsAndroidVersionAtLeast(ByVal major As Integer, ByVal Optional minor As Integer = 0, ByVal Optional build As Integer = 0, ByVal Optional revision As Integer = 0) As Boolean + Return True End Function - Function IsOSPlatformEarlierThan(ByVal osPlatform As OSPlatform, ByVal major As Integer, ByVal minor As Integer, ByVal build As Integer, ByVal revision As Integer) As Boolean - Return False + Public Shared Function IsIOS() As Boolean + Return True End Function - Function IsOSPlatformOrLater(ByVal platformName As String) As Boolean + Public Shared Function IsIOSVersionAtLeast(ByVal major As Integer, ByVal Optional minor As Integer = 0, ByVal Optional build As Integer = 0) As Boolean Return True End Function - Function IsOSPlatformEarlierThan(ByVal platformName As String) As Boolean + Public Shared Function IsMacOS() As Boolean Return True End Function - End Module + + Public Shared Function IsMacOSVersionAtLeast(ByVal major As Integer, ByVal Optional minor As Integer = 0, ByVal Optional build As Integer = 0) As Boolean + Return True + End Function + + Public Shared Function IsTvOS() As Boolean + Return True + End Function + + Public Shared Function IsTvOSVersionAtLeast(ByVal major As Integer, ByVal Optional minor As Integer = 0, ByVal Optional build As Integer = 0) As Boolean + Return True + End Function + + Public Shared Function IsWatchOS() As Boolean + Return True + End Function + + Public Shared Function IsWatchOSVersionAtLeast(ByVal major As Integer, ByVal Optional minor As Integer = 0, ByVal Optional build As Integer = 0) As Boolean + Return True + End Function + + Public Shared Function IsWindows() As Boolean + Return True + End Function + + Public Shared Function IsWindowsVersionAtLeast(ByVal major As Integer, ByVal Optional minor As Integer = 0, ByVal Optional build As Integer = 0, ByVal Optional revision As Integer = 0) As Boolean + Return True + End Function + End Class End Namespace "; } -} +} \ No newline at end of file diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index 0ed76dd17c..9b16a32122 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -1,6 +1,7 @@ // 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.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using Xunit; @@ -23,6 +24,7 @@ public async Task OsDependentMethodsCalledWarns() var csSource = @" using System.Runtime.Versioning; +[SupportedOSPlatform(""Linux"")] public class Test { public void M1() @@ -32,6 +34,10 @@ public void M1() [|Unsupported()|]; [|ObsoletedOverload()|]; } + [UnsupportedOSPlatform(""Linux4.1"")] + public void Unsupported() + { + } [SupportedOSPlatform(""Windows10.1.1.1"")] public void WindowsOnly() { @@ -44,10 +50,6 @@ public void Obsoleted() public void ObsoletedOverload() { } - [UnsupportedOSPlatform(""Linux4.1"")] - public void Unsupported() - { - } } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(csSource); diff --git a/src/Utilities/Compiler/WellKnownTypeNames.cs b/src/Utilities/Compiler/WellKnownTypeNames.cs index b1a22fa48d..18883d538b 100644 --- a/src/Utilities/Compiler/WellKnownTypeNames.cs +++ b/src/Utilities/Compiler/WellKnownTypeNames.cs @@ -206,6 +206,7 @@ internal static class WellKnownTypeNames public const string SystemNullable1 = "System.Nullable`1"; public const string SystemNumber = "System.Number"; public const string SystemObsoleteAttribute = "System.ObsoleteAttribute"; + public const string SystemOperatingSystem = "System.OperatingSystem"; public const string SystemOutOfMemoryException = "System.OutOfMemoryException"; public const string SystemRandom = "System.Random"; public const string SystemRange = "System.Range"; From 822ff2cd5375243daf9bd4d30f7956b79cf0114c Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 10 Aug 2020 18:59:50 -0700 Subject: [PATCH 18/48] Fix guard logic with && operator, more refacoring, add more tests --- .../PlatformCompatabilityAnalyzer.Value.cs | 18 +- .../PlatformCompatabilityAnalyzer.cs | 290 ++++++++---------- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 253 ++++++++++++++- 3 files changed, 379 insertions(+), 182 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs index 74caff1d8b..b094cfdb18 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -76,21 +76,21 @@ public static bool TryDecode( literal.ConstantValue.HasValue) { // OperatingSystem.IsOSPlatform(string platform) - if (invokedPlatformCheckMethod.Name == IsOSPlatform && TryParsePlatformNameAndVersion(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) + if (invokedPlatformCheckMethod.Name == IsOSPlatform && + TryParsePlatformNameAndVersion(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) { info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); return true; } // OperatingSystem.IsOSPlatformVersionAtLeast(string platform, int major, int minor = 0, int build = 0, int revision = 0) - if (TryDecodeOSVersion(arguments, valueContentAnalysisResult, out version, true)) + if (TryDecodeOSVersion(arguments, valueContentAnalysisResult, out version, 1)) { info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, literal.ConstantValue.Value.ToString(), version, negated: false); return true; } } - else if (arguments[0].Value is ILiteralOperation intLiteral && - intLiteral.Type?.SpecialType == SpecialType.System_Int32) + else if (arguments[0].Value is ILiteralOperation intLiteral && intLiteral.Type?.SpecialType == SpecialType.System_Int32) { var platformName = SwitchVersionedPlatformName(invokedPlatformCheckMethod.Name); if (platformName != null && TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var version)) @@ -152,12 +152,13 @@ private static bool TryDecodeOSVersion( ImmutableArray arguments, ValueContentAnalysisResult? valueContentAnalysisResult, [NotNullWhen(returnValue: true)] out Version? osVersion, - bool skipFirst = false) + int skip = 0) { + using var versionBuilder = ArrayBuilder.GetInstance(4, fillWithValue: 0); var index = 0; - foreach (var argument in skipFirst ? arguments.Skip(1) : arguments) + foreach (var argument in arguments.Skip(skip)) { if (!TryDecodeOSVersionPart(argument, valueContentAnalysisResult, out var osVersionPart)) { @@ -168,7 +169,10 @@ private static bool TryDecodeOSVersion( versionBuilder[index++] = osVersionPart; } - osVersion = new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2], versionBuilder[3]); + osVersion = arguments.Length - skip == 2 ? new Version(versionBuilder[0], versionBuilder[1]) : + arguments.Length - skip == 3 ? new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2]) : + new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2], versionBuilder[3]); + return true; static bool TryDecodeOSVersionPart(IArgumentOperation argument, ValueContentAnalysisResult? valueContentAnalysisResult, out int osVersionPart) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index cbb720e195..e450251cb3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Immutable; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using Analyzer.Utilities; @@ -169,22 +168,16 @@ private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, I if (localResult != null) { var localValue = localResult[platformSpecificOperation.Key.Kind, platformSpecificOperation.Key.Syntax]; - if (localValue.Kind == GlobalFlowStateAnalysisValueSetKind.Known) + if (localValue.Kind == GlobalFlowStateAnalysisValueSetKind.Known && IsKnownValueGuarded(attribute, localValue)) { - if (IsKnownValueGuarded(attribute, localValue)) - { - continue; - } + continue; } } } } - else if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Known) + else if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Known && IsKnownValueGuarded(attribute, value)) { - if (IsKnownValueGuarded(attribute, value)) - { - continue; - } + continue; } ReportDiagnostics(platformSpecificOperation.Key, attribute, context); @@ -203,60 +196,98 @@ private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, I private static bool IsKnownValueGuarded(SmallDictionary attributes, GlobalFlowStateAnalysisValueSet value) { - foreach (var analysisValue in value.AnalysisValues) + if (value.AnalysisValues.Count == 1) { - if (analysisValue is RuntimeMethodValue info) + var analysisValue = value.AnalysisValues.First(); + + if (analysisValue is RuntimeMethodValue info && + attributes.TryGetValue(info.PlatformName, out var attribute)) { - if (attributes.TryGetValue(info.PlatformName, out var attribute)) + if (info.Negated) { - if (!info.Negated) + if (attribute.UnsupportedFirst != null && IsEmptyVersion(attribute.UnsupportedFirst) && IsEmptyVersion(info.Version)) + { + attribute.UnsupportedFirst = null; + } + + if (attribute.UnsupportedSecond != null && IsEmptyVersion(attribute.UnsupportedSecond) && IsEmptyVersion(info.Version)) { - if (attribute.SupportedFirst != null) + attribute.UnsupportedSecond = null; + } + } + else + { + if (attribute.SupportedFirst != null && attribute.SupportedFirst <= info.Version) + { + attribute.SupportedFirst = null; + } + + if (attribute.SupportedSecond != null && attribute.SupportedSecond <= info.Version) + { + attribute.SupportedSecond = null; + } + } + } + } + else + { + var capturedPlatforms = PooledSortedSet.GetInstance(StringComparer.OrdinalIgnoreCase); + var capturedVersions = PooledDictionary.GetInstance(StringComparer.OrdinalIgnoreCase); + foreach (var analysisValue in value.AnalysisValues) + { + if (analysisValue is RuntimeMethodValue info && attributes.TryGetValue(info.PlatformName, out var attribute)) + { + if (info.Negated) + { + if (attribute.UnsupportedFirst != null && capturedPlatforms.Contains(info.PlatformName) && attribute.UnsupportedFirst >= info.Version) { - if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, attribute.SupportedFirst, info.Version)) - { - attribute.SupportedFirst = null; - continue; - } + attribute.UnsupportedFirst = null; } - if (attribute.SupportedSecond != null) + if (attribute.Obsoleted != null && capturedPlatforms.Contains(info.PlatformName) && attribute.Obsoleted <= info.Version) { - if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, attribute.SupportedSecond, info.Version)) - { - attribute.SupportedSecond = null; - continue; - } + attribute.Obsoleted = null; + } + + if (attribute.UnsupportedSecond != null && capturedPlatforms.Contains(info.PlatformName) && attribute.UnsupportedSecond <= info.Version) + { + attribute.UnsupportedSecond = null; + } + if (!IsEmptyVersion(info.Version)) + { + capturedVersions[info.PlatformName] = info.Version; } } else { - if (attribute.UnsupportedFirst != null) + if (IsEmptyVersion(info.Version)) { - if (AttributeVersionsMatch(PlatformAttributeType.UnsupportedOSPlatformAttribute, attribute.UnsupportedFirst, info.Version)) + capturedPlatforms.Add(info.PlatformName); + if (attribute.UnsupportedFirst != null && capturedVersions.TryGetValue(info.PlatformName, out var version) && attribute.UnsupportedFirst >= version) { attribute.UnsupportedFirst = null; - continue; } - } - if (attribute.Obsoleted != null) - { - if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, attribute.Obsoleted, info.Version)) + if (attribute.Obsoleted != null && capturedVersions.TryGetValue(info.PlatformName, out version) && attribute.Obsoleted <= version) { attribute.Obsoleted = null; - continue; } - } - if (attribute.UnsupportedSecond != null) - { - if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, attribute.UnsupportedSecond, info.Version)) + if (attribute.UnsupportedSecond != null && capturedVersions.TryGetValue(info.PlatformName, out version) && attribute.UnsupportedSecond <= version) { attribute.UnsupportedSecond = null; - continue; } } + + if (attribute.SupportedFirst != null && attribute.SupportedFirst <= info.Version) + { + attribute.SupportedFirst = null; + } + + if (attribute.SupportedSecond != null && attribute.SupportedSecond <= info.Version) + { + attribute.SupportedSecond = null; + } } } } @@ -273,7 +304,10 @@ private static bool IsKnownValueGuarded(SmallDictionary> platformSpecificOperations, OperationBlockAnalysisContext context) + 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) { @@ -291,28 +325,23 @@ private static void ReportDiagnostics(IOperation operation, SmallDictionary CopyOperationAttributes(SmallDictionary attributes) { - var copy = new SmallDictionary(); + var copy = new SmallDictionary(StringComparer.OrdinalIgnoreCase); foreach (var attribute in attributes) { copy.Add(attribute.Key, CopyAllAttributes(new PlatformAttributes(), attribute.Value)); @@ -382,11 +406,11 @@ private static SmallDictionary CopyOperationAttribut } /// - /// If a member has any SupportedPlatforms attribute + /// Summury coming /// /// 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, out SmallDictionary notSuppressedAttributes) { @@ -413,34 +437,22 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary attribute.Obsoleted || attribute.SupportedFirst > attribute.Obsoleted) { - // Can supported version be greater than obsoleted? Do we want to report diagnostic about wrong version here? + // Can supported version be greater than obsoleted? Do we want to report diagnostic here for wrong version? } else if (!ObsoletedSuppressed(callSiteAttribute.Obsoleted, attribute.Obsoleted)) { @@ -548,26 +560,23 @@ private static PlatformAttributes CopyAllAttributes(PlatformAttributes copyTo, P private static bool SuppressedByCallSiteUnsupported(PlatformAttributes callSiteAttribute, Version unsupporteAttribute) { - if (callSiteAttribute.UnsupportedFirst != null && AttributeVersionsMatch(PlatformAttributeType.UnsupportedOSPlatformAttribute, unsupporteAttribute, callSiteAttribute.UnsupportedFirst) || - callSiteAttribute.UnsupportedSecond != null && AttributeVersionsMatch(PlatformAttributeType.UnsupportedOSPlatformAttribute, unsupporteAttribute, callSiteAttribute.UnsupportedSecond)) + if (callSiteAttribute.UnsupportedFirst != null && unsupporteAttribute >= callSiteAttribute.UnsupportedFirst || + callSiteAttribute.UnsupportedSecond != null && unsupporteAttribute >= callSiteAttribute.UnsupportedSecond) { return true; } return false; } - private static bool ObsoletedSuppressed(Version? callSiteObsoleted, Version checkingObsoleted) => callSiteObsoleted != null - && AttributeVersionsMatch(PlatformAttributeType.ObsoletedInOSPlatformAttribute, checkingObsoleted, callSiteObsoleted); + private static bool ObsoletedSuppressed(Version? callSiteObsoleted, Version checkingObsoleted) => + callSiteObsoleted != null && checkingObsoleted >= callSiteObsoleted; private static bool UnsupportedSecondSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) { - if (callSiteAttribute.SupportedFirst != null) + if (callSiteAttribute.SupportedFirst != null && callSiteAttribute.SupportedFirst <= attribute.SupportedFirst! || + attribute.SupportedSecond != null && callSiteAttribute.SupportedFirst! <= attribute.SupportedSecond) { - if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, callSiteAttribute.SupportedFirst, attribute.SupportedFirst!) || - attribute.SupportedSecond != null && AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, callSiteAttribute.SupportedFirst, attribute.SupportedSecond)) - { - return true; - } + return true; } return SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedSecond!); @@ -575,13 +584,10 @@ private static bool UnsupportedSecondSuppressed(PlatformAttributes attribute, Pl private static bool UnsupportedFirstSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) { - if (callSiteAttribute.SupportedFirst != null) + if (callSiteAttribute.SupportedFirst != null && callSiteAttribute.SupportedFirst <= attribute.SupportedFirst! || + attribute.SupportedSecond != null && callSiteAttribute.SupportedFirst! <= attribute.SupportedSecond) { - if (AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, callSiteAttribute.SupportedFirst, attribute.SupportedFirst!) || - attribute.SupportedSecond != null && AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, callSiteAttribute.SupportedFirst, attribute.SupportedSecond)) - { - return true; - } + return true; } return SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst!); @@ -604,8 +610,8 @@ private static bool OptionalOsVersionsSuppressed(PlatformAttributes callSiteAttr private static bool MandatoryOsVersionsSuppressed(PlatformAttributes callSitePlatforms, Version checkingVersion) { - if ((callSitePlatforms.SupportedFirst != null && AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, checkingVersion, callSitePlatforms.SupportedFirst)) || - (callSitePlatforms.SupportedSecond != null && AttributeVersionsMatch(PlatformAttributeType.SupportedOSPlatformAttribute, checkingVersion, callSitePlatforms.SupportedSecond))) + if ((callSitePlatforms.SupportedFirst != null && checkingVersion <= callSitePlatforms.SupportedFirst) || + (callSitePlatforms.SupportedSecond != null && checkingVersion <= callSitePlatforms.SupportedSecond)) { return true; } @@ -623,15 +629,8 @@ pOperation.Parent is IBinaryOperation bo && bo.OperatorKind == BinaryOperatorKind.GreaterThanOrEqual || bo.OperatorKind == BinaryOperatorKind.LessThanOrEqual); - private static DiagnosticDescriptor SelectRule(PlatformAttributeType attributeType) - => attributeType switch - { - PlatformAttributeType.SupportedOSPlatformAttribute => SupportedOsRule, - PlatformAttributeType.ObsoletedInOSPlatformAttribute => ObsoleteOsRule, - _ => UnsupportedOsRule, - }; - - private static bool TryFindPlatformAttributesApplied(ImmutableArray immediateAttributes, ISymbol containingSymbol, [NotNullWhen(true)] out SmallDictionary? attributes) + private static bool TryFindPlatformAttributesApplied(ImmutableArray immediateAttributes, + ISymbol containingSymbol, [NotNullWhen(true)] out SmallDictionary? attributes) { attributes = null; AddPlatformAttributes(immediateAttributes, ref attributes); @@ -687,18 +686,7 @@ attribute.ConstructorArguments[0] is { } argument && attributes[platformName] = existingAttributes; } - switch (attribute.AttributeClass.Name) - { - case ObsoletedInOSPlatformAttribute: - AddOrUpdateObsoletedAttribute(existingAttributes, version); - break; - case SupportedOSPlatformAttribute: - AddOrUpdateSupportedAttribute(existingAttributes, version); - break; - case UnsupportedOSPlatformAttribute: - AddOrUpdateUnsupportedAttribute(existingAttributes, version); - break; - } + AddAttribute(attribute.AttributeClass.Name, version, existingAttributes); return true; } // else report diagnostic = Diagnostic.Create(PlatformNameNullOrEmptyRule, osAttribute.ApplicationSyntaxReference.GetSyntax().GetLocation()); @@ -711,6 +699,22 @@ attribute.ConstructorArguments[0] is { } argument && return false; } + private static void AddAttribute(string name, Version version, PlatformAttributes existingAttributes) + { + switch (name) + { + case ObsoletedInOSPlatformAttribute: + AddOrUpdateObsoletedAttribute(existingAttributes, version); + break; + case SupportedOSPlatformAttribute: + AddOrUpdateSupportedAttribute(existingAttributes, version); + break; + case UnsupportedOSPlatformAttribute: + AddOrUpdateUnsupportedAttribute(existingAttributes, version); + break; + } + } + private static void AddOrUpdateObsoletedAttribute(PlatformAttributes attributes, Version version) { if (attributes.Obsoleted != null) @@ -783,45 +787,5 @@ private static void AddOrUpdateSupportedAttribute(PlatformAttributes attributes, attributes.SupportedFirst = version; } } - - private static bool AttributeVersionsMatch(PlatformAttributeType attributeType, Version diagnosingVersion, Version suppressingVersion) - { - if (attributeType == PlatformAttributeType.SupportedOSPlatformAttribute) - { - if (diagnosingVersion.Major != suppressingVersion.Major) - { - return diagnosingVersion.Major < suppressingVersion.Major; - } - if (diagnosingVersion.Minor != suppressingVersion.Minor) - { - return diagnosingVersion.Minor < suppressingVersion.Minor; - } - if (diagnosingVersion.Build != suppressingVersion.Build) - { - return diagnosingVersion.Build < suppressingVersion.Build; - } - - return diagnosingVersion.Revision <= suppressingVersion.Revision; - } - else - { - Debug.Assert(attributeType == PlatformAttributeType.ObsoletedInOSPlatformAttribute || attributeType == PlatformAttributeType.UnsupportedOSPlatformAttribute); - - if (diagnosingVersion.Major != suppressingVersion.Major) - { - return diagnosingVersion.Major > suppressingVersion.Major; - } - if (diagnosingVersion.Minor != suppressingVersion.Minor) - { - return diagnosingVersion.Minor > suppressingVersion.Minor; - } - if (diagnosingVersion.Build != suppressingVersion.Build) - { - return diagnosingVersion.Build > suppressingVersion.Build; - } - - return diagnosingVersion.Revision >= suppressingVersion.Revision; - } - } } } \ No newline at end of file diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index 383ec222e1..5d5b707ad1 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -1,5 +1,6 @@ // 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; @@ -8,7 +9,233 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests public partial class PlatformCompatabilityAnalyzerTests { [Fact] - public async Task GuardedWith_IsOSPlatform_SimpleIfElse() + public async Task Unsupported_GuardedWith_IsOsNameMethods() + { + var source = @" +using System.Runtime.Versioning; +using System; + +class Test +{ + void M1() + { + if(!OperatingSystemHelper.IsBrowser()) + { + NotForBrowser(); + [|NotForIos12OrLater()|]; + } + else + { + [|NotForIos12OrLater()|]; + [|NotForBrowser()|]; + } + + if(OperatingSystemHelper.IsOSPlatform(""Browser"")) + { + [|NotForBrowser()|]; + } + else + { + NotForBrowser(); + } + + if(OperatingSystemHelper.IsIOS()) + { + [|NotForIos12OrLater()|]; + } + else + { + [|NotForIos12OrLater()|]; + } + + if(OperatingSystemHelper.IsIOSVersionAtLeast(12,1)) + { + [|NotForIos12OrLater()|]; + } + else + { + [|NotForIos12OrLater()|]; + } + + if(OperatingSystemHelper.IsIOS() && !OperatingSystemHelper.IsIOSVersionAtLeast(12,0)) + { + NotForIos12OrLater(); + } + else + { + [|NotForIos12OrLater()|]; + } + } + + [UnsupportedOSPlatform(""browser"")] + void NotForBrowser() + { + } + + [UnsupportedOSPlatform(""ios12.1"")] + void NotForIos12OrLater() + { + } +}" + MockAttributesCsSource + MockRuntimeApiSource; + + await VerifyAnalyzerAsyncCs(source); + } + + public static IEnumerable OperatingSystem_IsOsNameVersionAtLeast_MethodsTestData() + { + yield return new object[] { "Windows", "IsWindows", "10,1", true }; + yield return new object[] { "windows11.0", "IsWindows", "10,1,2,3", false }; + yield return new object[] { "WINDOWS10.1.2", "IsWindows", "10,1,2", true }; + yield return new object[] { "FreeBSD", "IsFreeBSD", "10", true }; + yield return new object[] { "FreeBSD12.0", "IsFreeBSD", "10,1,2", false }; + yield return new object[] { "freebsd10.1.2", "IsFreeBSD", "10,1,2", true }; + yield return new object[] { "Android", "IsAndroid", "10,1,2", true }; + yield return new object[] { "android11.0", "IsAndroid", "10,1,2", false }; + yield return new object[] { "Android10.1.2", "IsAndroid", "10,1,2", true }; + yield return new object[] { "IOS", "IsIOS", "10,1,2", true }; + yield return new object[] { "ios12.0", "IsIOS", "10,1,2", false }; + yield return new object[] { "iOS10.1.2", "IsIOS", "10,1,2", true }; + yield return new object[] { "MacOS", "IsMacOS", "10,1,2", true }; + yield return new object[] { "macOS14.0", "IsMacOS", "10,1,2", false }; + yield return new object[] { "macos10.1.2", "IsMacOS", "10,1,2", true }; + yield return new object[] { "TvOS", "IsTvOS", "10,1,2", true }; + yield return new object[] { "tvOS13.0", "IsTvOS", "10,1,2", false }; + yield return new object[] { "Tvos10.1", "IsTvOS", "10,1,2", true }; + yield return new object[] { "watchOS", "IsWatchOS", "10,1,2", true }; + yield return new object[] { "WatchOS14.0", "IsWatchOS", "10,1,2", false }; + yield return new object[] { "watchos10.0", "IsWatchOS", "10,1,2", true }; + } + + [Theory] + [MemberData(nameof(OperatingSystem_IsOsNameVersionAtLeast_MethodsTestData))] + public async Task GuardedWith_IsOsNameVersionAtLeast_impleIfElse(string osName, string isOsMethod, string version, bool versionMatch) + { + var match = versionMatch ? "OsSpecificMethod()" : "[|OsSpecificMethod()|]"; + var source = @" +using System.Runtime.Versioning; +using System; + +class Test +{ + void M1() + { + if(OperatingSystemHelper." + isOsMethod + @"VersionAtLeast(" + version + @")) + { + " + match + @"; + } + else + { + [|OsSpecificMethod()|]; + } + } + + [SupportedOSPlatform(""" + osName + @""")] + void OsSpecificMethod() + { + } +}" + MockAttributesCsSource + MockRuntimeApiSource; + + await VerifyAnalyzerAsyncCs(source); + } + + public static IEnumerable OperatingSystem_IsOsName_MethodsTestData() + { + yield return new object[] { "Windows", "IsWindows" }; + yield return new object[] { "WINDOWS", "IsWindows" }; + yield return new object[] { "windows", "IsWindows" }; + yield return new object[] { "LinuX", "IsLinux" }; + yield return new object[] { "linux", "IsLinux" }; + yield return new object[] { "Browser", "IsBrowser" }; + yield return new object[] { "browser", "IsBrowser" }; + yield return new object[] { "FreeBSD", "IsFreeBSD" }; + yield return new object[] { "freebsd", "IsFreeBSD" }; + yield return new object[] { "Android", "IsAndroid" }; + yield return new object[] { "android", "IsAndroid" }; + yield return new object[] { "IOS", "IsIOS" }; + yield return new object[] { "Ios", "IsIOS" }; + yield return new object[] { "ios", "IsIOS" }; + yield return new object[] { "MacOS", "IsMacOS" }; + yield return new object[] { "macOS", "IsMacOS" }; + yield return new object[] { "macos", "IsMacOS" }; + yield return new object[] { "TvOS", "IsTvOS" }; + yield return new object[] { "tvOS", "IsTvOS" }; + yield return new object[] { "watchOS", "IsWatchOS" }; + yield return new object[] { "WatchOS", "IsWatchOS" }; + yield return new object[] { "watchos", "IsWatchOS" }; + } + + [Theory] + [MemberData(nameof(OperatingSystem_IsOsName_MethodsTestData))] + public async Task GuardedWith_IsOsNameMethods_SimpleIfElse(string osName, string isOsMethod) + { + var source = @" +using System.Runtime.Versioning; +using System; + +class Test +{ + void M1() + { + if(OperatingSystemHelper." + isOsMethod + @"()) + { + OsSpecificMethod(); + } + else + { + [|OsSpecificMethod()|]; + } + } + + [SupportedOSPlatform(""" + osName + @""")] + void OsSpecificMethod() + { + } +}" + MockAttributesCsSource + MockRuntimeApiSource; + + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task GuardedWith_OperatingSystem_IsOSPlatform_SimpleIfElse() + { + var source = @" +using System.Runtime.Versioning; +using System; + +class Test +{ + void M1() + { + if(OperatingSystemHelper.IsOSPlatform(""Windows"")) + { + M2(); + } + else + { + [|M2()|]; + } + + if(OperatingSystemHelper.IsOSPlatform(""Windows8.0"")) + { + M2(); + } + else + { + [|M2()|]; + } + } + + [SupportedOSPlatform(""windows"")] + void M2() + { + } +}" + MockAttributesCsSource + MockRuntimeApiSource; + + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task GuardedWith_RuntimeInformation_IsOSPlatform_SimpleIfElse() { var source = @" using System.Runtime.Versioning; @@ -139,7 +366,7 @@ void M2() } [Fact] - public async Task GuardedWith_IsOSPlatformEarlierThan_SimpleIfElse() + public async Task GuardedWith_AlternativeOf_IsOSPlatformEarlierThan() { var source = @" using System.Runtime.Versioning; @@ -150,7 +377,7 @@ class Test [SupportedOSPlatform(""Windows"")] void M1() { - if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(11, 0, 19222)) + if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10, 0)) { [|M2()|]; M3(); @@ -176,8 +403,8 @@ void M3 () await VerifyAnalyzerAsyncCs(source); } - /*[Fact] TODO fix VB test - public async Task GuardedWith_StringOverload_SimpleIfElse() + [Fact] + public async Task GuardedWith_Obsoleted_SimpleIfElse() { var source = @" using System.Runtime.Versioning; @@ -188,7 +415,7 @@ class Test [SupportedOSPlatform(""Windows"")] void M1() { - if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10, 0, 19222)) + if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10, 2, 19222)) { [|M2()|]; M3(); @@ -199,7 +426,7 @@ void M1() [|M3()|]; } - if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Android"",12,1)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"",10,1,3)) { [|M3()|]; M2(); @@ -211,7 +438,7 @@ void M1() } } - [SupportedOSPlatform(""Android10.2.3"")] + [SupportedOSPlatform(""Windows10.1.2.3"")] void M2() { } @@ -228,8 +455,9 @@ Imports System.Runtime.Versioning Imports System Class Test + Private Sub M1() - If OperatingSystemHelper.IsOSPlatformEarlierThan(""Windows10.1"") Then + If OperatingSystemHelper.IsWindows() AndAlso Not OperatingSystemHelper.IsWindowsVersionAtLeast(10, 2, 19222) Then [|M2()|] M3() Else @@ -237,7 +465,7 @@ If OperatingSystemHelper.IsOSPlatformEarlierThan(""Windows10.1"") Then [|M3()|] End If - If OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows10.1.3"") Then + If OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"",10,1,3) Then [|M3()|] M2() Else @@ -250,13 +478,14 @@ End Sub Private Sub M2() End Sub + Private Sub M3() End Sub End Class " + MockAttributesVbSource + MockRuntimeApiSourceVb; await VerifyAnalyzerAsyncVb(vbSource); - }*/ + } [Fact] public async Task OsDependentEnumValue_GuardedCall_SimpleIfElse() @@ -313,7 +542,7 @@ public class Test public void M1() { - if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(8, 0, 19222)) //OperatingSystemHelper.IsOSPlatformEarlierThan + if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(8, 0, 19222)) { RemovedProperty = ""Hello""; string s = RemovedProperty; From 6cd038adbf3f2bba0cd28209289afd09bef6de50 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 11 Aug 2020 12:35:03 -0700 Subject: [PATCH 19/48] Suppress build warning, apply some review comments, add doc/comment --- .../PlatformCompatabilityAnalyzer.Data.cs | 30 +++++++++++---- ...mCompatabilityAnalyzer.OperationVisitor.cs | 12 ++---- .../PlatformCompatabilityAnalyzer.Value.cs | 13 ++++--- .../PlatformCompatabilityAnalyzer.cs | 37 ++++++++++++++++--- src/Utilities/Compiler/SmallDictionary.cs | 3 +- 5 files changed, 67 insertions(+), 28 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index 735c827fa8..6b4c6b2b98 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -7,13 +7,25 @@ namespace Microsoft.NetCore.Analyzers.InteropServices { public sealed partial class PlatformCompatabilityAnalyzer { - private enum PlatformAttributeType - { - SupportedOSPlatformAttribute, - ObsoletedInOSPlatformAttribute, - UnsupportedOSPlatformAttribute, - } - + /// + /// Class used for keeping platform information of an API, all are optional properties + /// + /// 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 + /// + /// I wouldn't expect that [ObsoletedInOSPlatform] attribute used more than once (like obsoleted once, supported back and obsoleted again), + /// so we will keep only one property for that, if any more attrbite found in the API parents chain we will keep the one with lowest versions + /// + /// 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 + /// - Obsoleted - keeps lowest version of [ObsoletedInOSPlatform] attrbite found + /// private class PlatformAttributes { public Version? Obsoleted { get; set; } @@ -21,7 +33,8 @@ private class PlatformAttributes 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 || Obsoleted != null; + public bool HasAttribute() => SupportedFirst != null || UnsupportedFirst != null || + SupportedSecond != null || UnsupportedSecond != null || Obsoleted != null; } private static bool TryParsePlatformNameAndVersion(string osString, out string osPlatformName, [NotNullWhen(true)] out Version? version) @@ -42,6 +55,7 @@ private static bool TryParsePlatformNameAndVersion(string osString, out string o return false; } } + osPlatformName = osString; version = new Version(0, 0); return true; diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs index c41953480e..cbd54dabe2 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs @@ -46,26 +46,22 @@ public override GlobalFlowStateAnalysisValueSet VisitInvocation_NonLambdaOrDeleg public override GlobalFlowStateAnalysisValueSet VisitPropertyReference(IPropertyReferenceOperation operation, object? argument) { - var value = base.VisitPropertyReference(operation, argument); - return GetValueOrDefault(value); + return GetValueOrDefault(base.VisitPropertyReference(operation, argument)); } public override GlobalFlowStateAnalysisValueSet VisitFieldReference(IFieldReferenceOperation operation, object? argument) { - var value = base.VisitFieldReference(operation, argument); - return GetValueOrDefault(value); + return GetValueOrDefault(base.VisitFieldReference(operation, argument)); } public override GlobalFlowStateAnalysisValueSet VisitObjectCreation(IObjectCreationOperation operation, object? argument) { - var value = base.VisitObjectCreation(operation, argument); - return GetValueOrDefault(value); + return GetValueOrDefault(base.VisitObjectCreation(operation, argument)); } public override GlobalFlowStateAnalysisValueSet VisitEventReference(IEventReferenceOperation operation, object? argument) { - var value = base.VisitEventReference(operation, argument); - return GetValueOrDefault(value); + return GetValueOrDefault(base.VisitEventReference(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 index b094cfdb18..f44a8bfbf1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -53,6 +53,7 @@ public static bool TryDecode( INamedTypeSymbol osPlatformType, [NotNullWhen(returnValue: true)] out RuntimeMethodValue? info) { + // Accelerators like OperatingSystem.IsPlatformName() if (arguments.IsEmpty) { var platformName = SwitchPlatformName(invokedPlatformCheckMethod.Name); @@ -64,7 +65,6 @@ public static bool TryDecode( } else { - // RuntimeInformation.IsOSPlatform(OSPlatform) if (TryDecodeRuntimeInformationIsOSPlatform(arguments[0].Value, osPlatformType, out string? osPlatformName)) { info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, osPlatformName, new Version(0, 0), negated: false); @@ -92,7 +92,9 @@ public static bool TryDecode( } else if (arguments[0].Value is ILiteralOperation intLiteral && intLiteral.Type?.SpecialType == SpecialType.System_Int32) { + // Accelerators like OperatingSystem.IsPlatformNameVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) var platformName = SwitchVersionedPlatformName(invokedPlatformCheckMethod.Name); + if (platformName != null && TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var version)) { info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); @@ -100,12 +102,13 @@ public static bool TryDecode( } } } + info = default; return false; } - private static string? SwitchVersionedPlatformName(string name) - => name switch + private static string? SwitchVersionedPlatformName(string methodName) + => methodName switch { IsWindowsVersionAtLeast => Windows, IsMacOSVersionAtLeast => MacOS, @@ -117,8 +120,8 @@ public static bool TryDecode( _ => null }; - private static string? SwitchPlatformName(string name) - => name switch + private static string? SwitchPlatformName(string methodName) + => methodName switch { IsWindows => Windows, IsLinux => Linux, diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index fa99c72754..052b64b5a2 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -11,9 +11,18 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis; using Microsoft.CodeAnalysis.Operations; +using Microsoft.NetCore.Analyzers.Performance; 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 obsoleted or unsupported or removed 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 { @@ -84,7 +93,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); context.RegisterCompilationStartAction(context => { @@ -198,6 +207,7 @@ private static bool IsKnownValueGuarded(SmallDictionary.GetInstance(StringComparer.OrdinalIgnoreCase); var capturedVersions = PooledDictionary.GetInstance(StringComparer.OrdinalIgnoreCase); + foreach (var analysisValue in value.AnalysisValues) { if (analysisValue is RuntimeMethodValue info && attributes.TryGetValue(info.PlatformName, out var attribute)) @@ -253,6 +266,7 @@ private static bool IsKnownValueGuarded(SmallDictionary IsEmptyVersion(version) ? string.Empty : version.ToString(); + private static ISymbol? GetOperationSymbol(IOperation operation) => operation switch { @@ -406,7 +423,15 @@ private static SmallDictionary CopyOperationAttribut } /// - /// Summury coming + /// 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. + /// - [ObsoletedInOSPlatform] continuous to require a version number. + /// - [ObsoletedInOSPlatform] by itself doesn't imply support. However, it doesn't make sense to apply [ObsoletedInOSPlatform] unless that platform is supported. /// /// Platform specific attributes applied to the invoked member /// Platform specific attributes applied to the call site where the member invoked diff --git a/src/Utilities/Compiler/SmallDictionary.cs b/src/Utilities/Compiler/SmallDictionary.cs index 137f28d81a..c1067ba76c 100644 --- a/src/Utilities/Compiler/SmallDictionary.cs +++ b/src/Utilities/Compiler/SmallDictionary.cs @@ -107,6 +107,7 @@ public bool ContainsKey(K key) return TryGetValue(key, out V _); } +#pragma warning disable CA1822 [Conditional("DEBUG")] internal void AssertBalanced() { @@ -114,7 +115,7 @@ internal void AssertBalanced() AvlNode.AssertBalanced(_root); #endif } - +#pragma warning restore CA1822 private abstract class Node { public readonly K Key; From 3916447c7269cdb91a7de6e84355592e04451986 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Wed, 12 Aug 2020 14:48:45 -0700 Subject: [PATCH 20/48] Add un versioned diagnostic, add more tests for attributes combination and fix bugs found --- .../PlatformCompatabilityAnalyzer.cs | 167 +++++++------- .../MicrosoftNetCoreAnalyzersResources.resx | 10 +- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 14 +- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 14 +- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 14 +- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 14 +- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 14 +- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 14 +- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 14 +- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 14 +- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 14 +- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 14 +- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 14 +- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 14 +- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 14 +- .../PlatformCompatabilityAnalyzerTests.cs | 207 +++++++++++++++++- 16 files changed, 454 insertions(+), 112 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 052b64b5a2..6d818a287f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis; using Microsoft.CodeAnalysis.Operations; -using Microsoft.NetCore.Analyzers.Performance; namespace Microsoft.NetCore.Analyzers.InteropServices { @@ -32,9 +31,11 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(SupportedOSPlatformAttribute, ObsoletedInOSPlatformAttribute, UnsupportedOSPlatformAttribute); private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); - private static readonly LocalizableString s_localizableSupportedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckSupportedMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private static readonly LocalizableString s_localizableRequiredOsMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckRequiredOsMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private static readonly LocalizableString s_localizableRequiredOsVersionMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckRequiredOsVersionMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableObsoleteMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckObsoleteMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); - private static readonly LocalizableString s_localizableUnsupportedMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckUnsupportedMessage), 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 @@ -63,9 +64,18 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private const string IsWindows = nameof(IsWindows); private const string IsWindowsVersionAtLeast = nameof(IsWindowsVersionAtLeast); - internal static DiagnosticDescriptor SupportedOsRule = DiagnosticDescriptorHelper.Create(RuleId, + internal static DiagnosticDescriptor RequiredOsVersionRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, - s_localizableSupportedMessage, + s_localizableRequiredOsVersionMessage, + DiagnosticCategory.Interoperability, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: false, + isDataflowRule: false); + + internal static DiagnosticDescriptor RequiredOsRule = DiagnosticDescriptorHelper.Create(RuleId, + s_localizableTitle, + s_localizableRequiredOsMessage, DiagnosticCategory.Interoperability, RuleLevel.BuildWarning, description: s_localizableDescription, @@ -80,15 +90,25 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer description: s_localizableDescription, isPortedFxCopRule: false, isDataflowRule: false); + internal static DiagnosticDescriptor UnsupportedOsRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, - s_localizableUnsupportedMessage, + s_localizableUnsupportedOsMessage, DiagnosticCategory.Interoperability, RuleLevel.BuildWarning, description: s_localizableDescription, isPortedFxCopRule: false, isDataflowRule: false); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(SupportedOsRule, ObsoleteOsRule, UnsupportedOsRule); + + 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(RequiredOsRule, RequiredOsVersionRule, ObsoleteOsRule, UnsupportedOsRule, UnsupportedOsVersionRule); public override void Initialize(AnalysisContext context) { @@ -156,7 +176,7 @@ private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, I var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); var analysisResult = GlobalFlowStateAnalysis.TryGetOrComputeResult( cfg, context.OwningSymbol, CreateOperationVisitor, wellKnownTypeProvider, - context.Options, SupportedOsRule, performValueContentAnalysis: true, + context.Options, RequiredOsRule, performValueContentAnalysis: true, context.CancellationToken, out var valueContentAnalysisResult); if (analysisResult == null) @@ -338,30 +358,40 @@ private static void ReportDiagnostics(IOperation operation, SmallDictionary IsEmptyVersion(version) ? string.Empty : version.ToString(); + private static void ReportSupportedDiagnostic(IOperation operation, OperationBlockAnalysisContext context, string name, string platformName, string? version = null) => + context.ReportDiagnostic(version == null ? operation.CreateDiagnostic(RequiredOsRule, name, platformName) : + operation.CreateDiagnostic(RequiredOsVersionRule, name, platformName, version)); + + private 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 @@ -448,7 +478,8 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary attribute.SupportedFirst) // only for current platform + // If only supported for current platform + if (attribute.UnsupportedFirst == null || attribute.UnsupportedFirst > attribute.SupportedFirst) { if (supportedOnlyList.HasValue && !supportedOnlyList.Value) { @@ -479,7 +510,7 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary attribute.Obsoleted || attribute.SupportedFirst > attribute.Obsoleted) + { + // Can supported version be greater than obsoleted? Do we want to report diagnostic here for wrong version? + } + else if (!SuppresedByUnsupported(callSiteAttribute, attribute.Obsoleted) && !ObsoletedSuppressed(callSiteAttribute.Obsoleted, attribute.Obsoleted)) + { + diagnositcAttribute.Obsoleted = (Version)attribute.Obsoleted.Clone(); + } + } } - else - { - CopyAllAttributes(diagnositcAttribute, attribute); - } + // else call site is not supporting this platform, and it is deny list, so no need to warn } } else @@ -551,11 +590,7 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary + callSiteAttribute.UnsupportedFirst != null && callSiteAttribute.UnsupportedFirst <= obsoleted || + callSiteAttribute.UnsupportedSecond != null && callSiteAttribute.UnsupportedSecond <= obsoleted; + private static PlatformAttributes CopyAllAttributes(PlatformAttributes copyTo, PlatformAttributes copyFrom) { copyTo.SupportedFirst = (Version?)copyFrom.SupportedFirst?.Clone(); @@ -583,65 +622,33 @@ private static PlatformAttributes CopyAllAttributes(PlatformAttributes copyTo, P return copyTo; } - private static bool SuppressedByCallSiteUnsupported(PlatformAttributes callSiteAttribute, Version unsupporteAttribute) - { - if (callSiteAttribute.UnsupportedFirst != null && unsupporteAttribute >= callSiteAttribute.UnsupportedFirst || - callSiteAttribute.UnsupportedSecond != null && unsupporteAttribute >= callSiteAttribute.UnsupportedSecond) - { - return true; - } - return false; - } + private static bool SuppressedByCallSiteUnsupported(PlatformAttributes callSiteAttribute, Version unsupporteAttribute) => + callSiteAttribute.UnsupportedFirst != null && unsupporteAttribute >= callSiteAttribute.UnsupportedFirst || + callSiteAttribute.UnsupportedSecond != null && unsupporteAttribute >= callSiteAttribute.UnsupportedSecond; private static bool ObsoletedSuppressed(Version? callSiteObsoleted, Version checkingObsoleted) => callSiteObsoleted != null && checkingObsoleted >= callSiteObsoleted; - private static bool UnsupportedSecondSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) - { - if (callSiteAttribute.SupportedFirst != null && callSiteAttribute.SupportedFirst <= attribute.SupportedFirst! || - attribute.SupportedSecond != null && callSiteAttribute.SupportedFirst! <= attribute.SupportedSecond) - { - return true; - } + private static bool UnsupportedSecondSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) => + SuppressedByCallSiteSupported(attribute, callSiteAttribute.SupportedFirst) || + SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedSecond!); - return SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedSecond!); - } + private static bool SuppressedByCallSiteSupported(PlatformAttributes attribute, Version? callSiteSupportedFirst) => + callSiteSupportedFirst != null && callSiteSupportedFirst >= attribute.SupportedFirst! && + attribute.SupportedSecond != null && callSiteSupportedFirst >= attribute.SupportedSecond; - private static bool UnsupportedFirstSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) - { - if (callSiteAttribute.SupportedFirst != null && callSiteAttribute.SupportedFirst <= attribute.SupportedFirst! || - attribute.SupportedSecond != null && callSiteAttribute.SupportedFirst! <= attribute.SupportedSecond) - { - return true; - } - - return SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst!); - } - - private static bool OptionalOsVersionsSuppressed(PlatformAttributes callSiteAttribute, PlatformAttributes attribute) - { - // Optianal supported attribute, if call site supports it, its versions should match - if (callSiteAttribute.SupportedFirst != null && - !(attribute.SupportedFirst <= callSiteAttribute.SupportedFirst || - (callSiteAttribute.SupportedSecond != null && attribute.SupportedFirst <= callSiteAttribute.SupportedSecond))) - { - - return false; - } + private static bool UnsupportedFirstSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) => + callSiteAttribute.SupportedFirst != null && callSiteAttribute.SupportedFirst >= attribute.SupportedFirst || + SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst!); - // if call site not suppors it, no problem - return true; - } + // As optianal if call site supports that platform, their versions should match + private static bool OptionalOsVersionsSuppressed(PlatformAttributes callSiteAttribute, PlatformAttributes attribute) => + (callSiteAttribute.SupportedFirst == null || attribute.SupportedFirst <= callSiteAttribute.SupportedFirst) && + (callSiteAttribute.SupportedSecond == null || attribute.SupportedFirst <= callSiteAttribute.SupportedSecond); - private static bool MandatoryOsVersionsSuppressed(PlatformAttributes callSitePlatforms, Version checkingVersion) - { - if ((callSitePlatforms.SupportedFirst != null && checkingVersion <= callSitePlatforms.SupportedFirst) || - (callSitePlatforms.SupportedSecond != null && checkingVersion <= callSitePlatforms.SupportedSecond)) - { - return true; - } - return false; - } + private static bool MandatoryOsVersionsSuppressed(PlatformAttributes callSitePlatforms, Version checkingVersion) => + callSitePlatforms.SupportedFirst != null && checkingVersion <= callSitePlatforms.SupportedFirst || + callSitePlatforms.SupportedSecond != null && checkingVersion <= callSitePlatforms.SupportedSecond; // Do not warn for conditional checks of platfomr specific enum value; 'if (value != FooEnum.WindowsOnlyValue)' private static bool IsWithinConditionalOperation(IFieldReferenceOperation pOperation) => diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 9e67594b7a..4513936084 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1431,13 +1431,13 @@ Using platform dependendent API on a component makes the code no longer work across all platforms. - + '{0}' requires {1} {2} version or later '{0}' has been deprecated since {1} {2} version - + '{0}' is not supported or has been removed since {1} {2} version @@ -1467,4 +1467,10 @@ '{0}' will throw for assemblies embedded in a single-file app + + '{0}' is not supported or has been removed from {1} + + + '{0}' requires {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 98fe2f29b4..a62d7019a8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1527,12 +1527,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 b622d3f9af..e08c4f3e3a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1527,12 +1527,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 3efe97102f..1c59ac742b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1527,12 +1527,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 b2be20a54f..25908fdbdf 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1527,12 +1527,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 edfbfdf55c..82216b59ab 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1527,12 +1527,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 f0d145e681..d9d7fe2d6c 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1527,12 +1527,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 2c78ce354a..a8806fcf56 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1527,12 +1527,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 7e759a9433..762d782cdd 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1528,12 +1528,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 57203f5f8f..0c6e98db16 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 @@ -1527,12 +1527,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 8195039a18..72fcac8268 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1527,12 +1527,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 00b4d493c3..a492390cba 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1527,12 +1527,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 be43904bfc..cf4b47e87f 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 @@ -1527,12 +1527,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later 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 8509b60627..8907949d57 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 @@ -1527,12 +1527,22 @@ Call of platform dependent API - + + '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from {1} + + + '{0}' is not supported or has been removed since {1} {2} version '{0}' is not supported or has been removed since {1} {2} version - + + '{0}' requires {1} + '{0}' requires {1} + + + '{0}' requires {1} {2} version or later '{0}' requires {1} {2} version or later diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index bb569390ca..d147d83f1f 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Runtime.InteropServices; +using System.Security.Cryptography; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using Xunit; @@ -779,8 +780,8 @@ public void M2() if (warn) { await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithSpan(9, 32, 9, 54).WithArguments(".ctor", "Windows", "10.1.2.3"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithSpan(9, 32, 9, 54).WithArguments(".ctor", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); } else { @@ -850,6 +851,7 @@ public async Task MethodOfOsDependentClassSuppressedWithUnsupportedAttribute(str var source = @" using System.Runtime.Versioning; +[SupportedOSPlatform(""Windows"")] public class Test { [UnsupportedOSPlatform(""" + suppressingVersion + @""")] @@ -872,8 +874,8 @@ public void M2() if (warn) { await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithSpan(9, 32, 9, 54).WithArguments(".ctor", "Windows", "10.1.2.3"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithSpan(10, 9, 10, 17).WithArguments("M2", "Windows", "10.1.2.3")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(10, 32).WithArguments(".ctor", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(11, 9).WithArguments("M2", "Windows", "10.1.2.3")); } else { @@ -881,6 +883,203 @@ await VerifyAnalyzerAsyncCs(source, } } + [Fact] + public async Task UnsupportedNotWarnsForUnrelatedSupportedContext() + { + var source = @" + using System.Runtime.Versioning; + +[SupportedOSPlatform(""Linux"")] +public class Test +{ + public void M1() + { + var obj = new C(); + obj.BrowserMethod(); + C.StaticClass.LinuxMethod(); + C.StaticClass.LinuxVersionedMethod(); + } +} + +public class C +{ + [UnsupportedOSPlatform(""browser"")] + public void BrowserMethod() + { + } + + [UnsupportedOSPlatform(""linux4.8"")] + internal static class StaticClass{ + public static void LinuxVersionedMethod() + { + } + [UnsupportedOSPlatform(""linux"")] + public static void LinuxMethod() + { + } + } +} +" + MockAttributesCsSource; + + await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(11, 9) + .WithMessage("'LinuxMethod' is not supported or has been removed from linux").WithArguments("LinuxMethod", "linux"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(12, 9) + .WithMessage("'LinuxVersionedMethod' is not supported or has been removed since linux 4.8 version").WithArguments("LinuxMethod", "linux")); + + } + + [Fact] + + public async Task MultipleAttrbiutesOptionallySupportedListTest() + { + var source = @" + using System.Runtime.Versioning; + +public class Test +{ + C obj = new C(); + [SupportedOSPlatform(""Linux"")] + public void DiffferentOsNotWarn() + { + obj.DoesNotWorkOnWindows(); + } + + [SupportedOSPlatform(""windows"")] + [UnsupportedOSPlatform(""windows10.0.2000"")] + public void SupporteWindows() + { + // Warns for UnsupportedFirst, Supported and Obsoleted + obj.DoesNotWorkOnWindows(); + } + + [UnsupportedOSPlatform(""windows"")] + [SupportedOSPlatform(""windows10.1"")] + [ObsoletedInOSPlatform(""windows10.0.1909"")] + [UnsupportedOSPlatform(""windows10.0.2003"")] + public void SameSupportForWindowsNotWarn() + { + obj.DoesNotWorkOnWindows(); + } + + public void AllSupportedWarnForAll() + { + obj.DoesNotWorkOnWindows(); + } + + [SupportedOSPlatform(""windows10.0.2000"")] + public void SupportedFromWindows10_0_2000() + { + // Should warn for [ObsoletedInOSPlatform] and [UnsupportedOSPlatform] + obj.DoesNotWorkOnWindows(); + } + + [SupportedOSPlatform(""windows10.0.1904"")] + [UnsupportedOSPlatform(""windows10.0.1909"")] + public void SupportedWindowsFrom10_0_1904_To10_0_1909_NotWarn() + { + // Should not warn + obj.DoesNotWorkOnWindows(); + } +} + +public class C +{ + [UnsupportedOSPlatform(""windows"")] + [SupportedOSPlatform(""windows10.0.1903"")] + [ObsoletedInOSPlatform(""windows10.0.1909"")] + [UnsupportedOSPlatform(""windows10.0.2004"")] + public void DoesNotWorkOnWindows() + { + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed from windows"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' requires windows 10.0.1903 version or later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed from windows"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' requires windows 10.0.1903 version or later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since windows 10.0.2004 version")); + } + + [Fact] + + public async Task MultipleAttrbiutesSupportedOnlyWindowsListTest() + { + var source = @" + using System.Runtime.Versioning; + +public class Test +{ + C obj = new C(); + [SupportedOSPlatform(""Linux"")] + public void DiffferentOsWarnsForAll() + { + obj.DoesNotWorkOnWindows(); + } + + [SupportedOSPlatform(""windows"")] + [UnsupportedOSPlatform(""windows10.0.2000"")] + public void SupporteWindows() + { + // Warns for Obsoleted version + obj.DoesNotWorkOnWindows(); + } + + [UnsupportedOSPlatform(""windows"")] + [SupportedOSPlatform(""windows10.1"")] + [ObsoletedInOSPlatform(""windows10.0.1909"")] + [UnsupportedOSPlatform(""windows10.0.2003"")] + public void SameSupportForWindowsNotWarn() + { + obj.DoesNotWorkOnWindows(); + } + + public void AllSupportedWarnForAll() + { + obj.DoesNotWorkOnWindows(); + } + + [SupportedOSPlatform(""windows10.0.2000"")] + public void SupportedFromWindows10_0_2000() + { + // Warns for [ObsoletedInOSPlatform] and [UnsupportedOSPlatform] + obj.DoesNotWorkOnWindows(); + } + + [SupportedOSPlatform(""windows10.0.1904"")] + [UnsupportedOSPlatform(""windows10.0.1909"")] + public void SupportedWindowsFrom10_0_1904_To10_0_1909_NotWarn() + { + // Should not warn + obj.DoesNotWorkOnWindows(); + } +} + +public class C +{ + [SupportedOSPlatform(""windows"")] + [ObsoletedInOSPlatform(""windows10.0.1909"")] + [UnsupportedOSPlatform(""windows10.0.2004"")] + public void DoesNotWorkOnWindows() + { + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' requires windows"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since windows 10.0.2004 version"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' requires windows"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since windows 10.0.2004 version"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since windows 10.0.2004 version")); + } + private static VerifyCS.Test PopulateTestCs(string sourceCode, params DiagnosticResult[] expected) { var test = new VerifyCS.Test From 21263f286cf01f73f9fc19c5de1d71decf523591 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 13 Aug 2020 00:46:55 -0700 Subject: [PATCH 21/48] More tests, review updates --- .../PlatformCompatabilityAnalyzer.Value.cs | 6 +- .../PlatformCompatabilityAnalyzer.cs | 4 +- .../MicrosoftNetCoreAnalyzersResources.resx | 4 +- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 8 +- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 8 +- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 8 +- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 8 +- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 125 +++++++++++++++++- .../PlatformCompatabilityAnalyzerTests.cs | 37 +++++- 18 files changed, 214 insertions(+), 66 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs index f44a8bfbf1..f4d37be1d7 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -172,9 +172,9 @@ private static bool TryDecodeOSVersion( versionBuilder[index++] = osVersionPart; } - osVersion = arguments.Length - skip == 2 ? new Version(versionBuilder[0], versionBuilder[1]) : - arguments.Length - skip == 3 ? new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2]) : - new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2], versionBuilder[3]); + osVersion = versionBuilder[3] == 0 ? versionBuilder[2] == 0 ? new Version(versionBuilder[0], versionBuilder[1]) : + new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2]) : + new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2], versionBuilder[3]); return true; diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 6d818a287f..45d2daf3e4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -294,9 +294,9 @@ private static bool IsKnownValueGuarded(SmallDictionary= version) { attribute.UnsupportedFirst = null; @@ -650,7 +650,7 @@ private static bool MandatoryOsVersionsSuppressed(PlatformAttributes callSitePla callSitePlatforms.SupportedFirst != null && checkingVersion <= callSitePlatforms.SupportedFirst || callSitePlatforms.SupportedSecond != null && checkingVersion <= callSitePlatforms.SupportedSecond; - // Do not warn for conditional checks of platfomr specific enum value; 'if (value != FooEnum.WindowsOnlyValue)' + // 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 && diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 4513936084..a80f33f97b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1429,7 +1429,7 @@ Call of platform dependent API - Using platform dependendent 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. '{0}' requires {1} {2} version or later @@ -1471,6 +1471,6 @@ '{0}' is not supported or has been removed from {1} - '{0}' requires {1} + '{0}' requires {1} platform \ 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 a62d7019a8..89386a4813 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1513,8 +1513,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1538,8 +1538,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform 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 e08c4f3e3a..1bfe71110d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1513,8 +1513,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1538,8 +1538,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform 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 1c59ac742b..220a4d3277 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1513,8 +1513,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1538,8 +1538,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform 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 25908fdbdf..80ed9ee4e7 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1513,8 +1513,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1538,8 +1538,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform 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 82216b59ab..869d80e4c2 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1513,8 +1513,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1538,8 +1538,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform 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 d9d7fe2d6c..4ec33417d8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1513,8 +1513,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1538,8 +1538,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform 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 a8806fcf56..f536e5f038 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1513,8 +1513,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1538,8 +1538,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform 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 762d782cdd..dadf257d6a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1514,8 +1514,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1539,8 +1539,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform 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 0c6e98db16..0675f800ec 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 @@ -1513,8 +1513,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1538,8 +1538,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform 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 72fcac8268..53ef842057 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1513,8 +1513,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1538,8 +1538,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform 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 a492390cba..b893209c77 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1513,8 +1513,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1538,8 +1538,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform 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 cf4b47e87f..5e29e6fca2 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 @@ -1513,8 +1513,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1538,8 +1538,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform 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 8907949d57..e99700cff5 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 @@ -1513,8 +1513,8 @@ - Using platform dependendent API on a component makes the code no longer work across all platforms. - Using platform dependendent 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. + Using platform dependent API on a component makes the code no longer work across all platforms. @@ -1538,8 +1538,8 @@ - '{0}' requires {1} - '{0}' requires {1} + '{0}' requires {1} platform + '{0}' requires {1} platform diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index 5d5b707ad1..dcfad385b9 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -4,10 +4,75 @@ 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 { + /*[Fact] TODO: Missing scenario + public async Task SupportedUnsupportedRange_GuardedWithOr() + { + var source = @" +using System.Runtime.Versioning; +using System; + +class Test +{ + public void Api_Usage() + { + if (OperatingSystemHelper.IsWindows() || + !OperatingSystemHelper.IsWindowsVersionAtLeast(10, 0, 19041)) + { + Api(); + } + + [|Api()|]; + } + + [UnsupportedOSPlatform(""windows"")] + [SupportedOSPlatform(""windows10.0.19041"")] + void Api() + { + } +}" + MockAttributesCsSource + MockRuntimeApiSource; + + await VerifyAnalyzerAsyncCs(source); + }*/ + + [Fact] + public async Task SupportedUnsupportedRange_GuardedWithAnd() + { + var source = @" +using System.Runtime.Versioning; +using System; + +class Test +{ + public void Api_Usage() + { + if (OperatingSystemHelper.IsIOSVersionAtLeast(12,0) && + !OperatingSystemHelper.IsIOSVersionAtLeast(14,0)) + { + Api(); + } + [|Api()|]; + } + + [SupportedOSPlatform(""ios12.0"")] + [UnsupportedOSPlatform(""ios14.0"")] + void Api() + { + } +}" + MockAttributesCsSource + MockRuntimeApiSource; + + await VerifyAnalyzerAsyncCs(source, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(14, 9) + .WithMessage("'Api' is not supported or has been removed since ios 14.0 version")); + } + [Fact] public async Task Unsupported_GuardedWith_IsOsNameMethods() { @@ -695,7 +760,61 @@ public void M2() await VerifyAnalyzerAsyncCs(source); } - /*[Fact] // TODO: Need to be fixed + [Fact] + public async Task LocalFunctionCallsPlatformDependentMember_InvokedFromDifferentContext() + { + var source = @" +using System.Runtime.Versioning; +using System; + +public class Test +{ + void M() + { + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) + { + LocalM(); // We don't need to account this platform check + } + + LocalM(); + return; + + void LocalM() + { + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) + { + WindowsOnlyMethod(); + } + else + { + [|WindowsOnlyMethod()|]; + } + if (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10,0)) + { + UnsupportedWindows10(); + } + else + { + [|UnsupportedWindows10()|]; + } + } + } + + [SupportedOSPlatform(""Windows10.1.2.3"")] + public void WindowsOnlyMethod() + { + } + + [UnsupportedOSPlatform(""Windows10.0"")] + public void UnsupportedWindows10() + { + } +} +" + MockAttributesCsSource + MockRuntimeApiSource; + await VerifyAnalyzerAsyncCs(source); + } + + /*[Fact] public async Task LambdaCallsOsDependentMember_GuardedCall_SimpleIfElse() { var source = @" @@ -790,7 +909,7 @@ public void M4() await VerifyAnalyzerAsyncCs(source); } - /*[Fact] // TODO: need to be fixed + /*[Fact] public async Task OsDependentMethodAssignedToDelegate_GuardedCall_SimpleIfElse() { var source = @" @@ -807,7 +926,7 @@ public void DelegateMethod() } public void M1() { - if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) + if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11, 0)) { Del handler = DelegateMethod; handler(); diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index d147d83f1f..c4e57e87ef 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -1,8 +1,6 @@ // 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.Runtime.InteropServices; -using System.Security.Cryptography; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using Xunit; @@ -19,6 +17,37 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests public partial class PlatformCompatabilityAnalyzerTests { + /*[Fact] TODO: Compiler error test, not sure how to report the diagnostic + public async Task TestOsPlatformAttributesWithNonStringArgument() + { + var csSource = @" +using System.Runtime.Versioning; +using System.Runtime.InteropServices; + +public class Test +{ + [[|SupportedOSPlatform(""Linux"", ""Windows"")|]] + public void MethodWithTwoArguments() + { + } + [SupportedOSPlatform([|new string[]{""Linux"", ""Windows""}|])] + public void MethodWithArrayArgument() + { + } + [ObsoletedInOSPlatform([|10|])] + public void MethodWithIntArgument() + { + } + [ObsoletedInOSPlatform([|OSPlatform.Windows|])] + public void MethodWithPropertyAccess() + { + } +} +" + MockAttributesCsSource; + + await VerifyAnalyzerAsyncCs(csSource); + }*/ + [Fact] public async Task OsDependentMethodsCalledWarns() { @@ -1069,11 +1098,11 @@ public void DoesNotWorkOnWindows() } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' requires windows"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' requires windows platform"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since windows 10.0.2004 version"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' requires windows"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' requires windows platform"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since windows 10.0.2004 version"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), From 28a7cba35522a24494a41dc66f6b12e52d173512 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 14 Aug 2020 01:00:08 -0700 Subject: [PATCH 22/48] Fix bugs 4008, 4009, 4011, apply some feedback, fix failing test --- .../Core/AnalyzerReleases.Shipped.md | 2 +- .../Core/AnalyzerReleases.Unshipped.md | 4 - .../PlatformCompatabilityAnalyzer.Value.cs | 74 +++++++----- .../PlatformCompatabilityAnalyzer.cs | 24 ++++ .../MicrosoftNetCoreAnalyzersResources.resx | 10 +- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 20 ++-- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 20 ++-- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 20 ++-- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 20 ++-- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 20 ++-- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 20 ++-- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 20 ++-- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 20 ++-- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 20 ++-- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 20 ++-- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 20 ++-- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 20 ++-- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 20 ++-- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 110 +++++++++++++++++- .../PlatformCompatabilityAnalyzerTests.cs | 66 +++++++---- .../DataFlow/DataFlowAnalysisResult.cs | 2 +- 21 files changed, 360 insertions(+), 192 deletions(-) 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/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index f4f7e97a4e..cdf4f1397e 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -1,5 +1 @@ ; Please do not edit this file manually, it should only be updated through code fix application. -### Changed Rules -Rule ID | New Category | New Severity | Old Category | Old Severity | Notes ---------|--------------|--------------|--------------|--------------|------- -CA1416 | Interoperability | Warning | Interoperability | Info | PlatformCompatabilityAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca1416) \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs index f4d37be1d7..1dda5b3beb 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis; using System.Diagnostics.CodeAnalysis; using Analyzer.Utilities; +using System.Diagnostics; namespace Microsoft.NetCore.Analyzers.InteropServices { @@ -71,34 +72,36 @@ public static bool TryDecode( return true; } - if (arguments[0].Value is ILiteralOperation literal && - literal.Type?.SpecialType == SpecialType.System_String && - literal.ConstantValue.HasValue) + if (arguments[0].Value is ILiteralOperation literal) { - // OperatingSystem.IsOSPlatform(string platform) - if (invokedPlatformCheckMethod.Name == IsOSPlatform && - TryParsePlatformNameAndVersion(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) + if (literal.Type?.SpecialType == SpecialType.System_String && + literal.ConstantValue.HasValue) { - info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); - return true; - } - - // OperatingSystem.IsOSPlatformVersionAtLeast(string platform, int major, int minor = 0, int build = 0, int revision = 0) - if (TryDecodeOSVersion(arguments, valueContentAnalysisResult, out version, 1)) - { - info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, literal.ConstantValue.Value.ToString(), version, negated: false); - return true; + // OperatingSystem.IsOSPlatform(string platform) + if (invokedPlatformCheckMethod.Name == IsOSPlatform && + TryParsePlatformNameAndVersion(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) + { + info = new RuntimeMethodValue(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 RuntimeMethodValue(invokedPlatformCheckMethod.Name, literal.ConstantValue.Value.ToString(), version, negated: false); + return true; + } } - } - else if (arguments[0].Value is ILiteralOperation intLiteral && intLiteral.Type?.SpecialType == SpecialType.System_Int32) - { - // Accelerators like OperatingSystem.IsPlatformNameVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) - var platformName = SwitchVersionedPlatformName(invokedPlatformCheckMethod.Name); - - if (platformName != null && TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var version)) + else if (literal.Type?.SpecialType == SpecialType.System_Int32) { - info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); - return true; + // Accelerators like OperatingSystem.IsPlatformNameVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) + var platformName = SwitchVersionedPlatformName(invokedPlatformCheckMethod.Name); + + if (platformName != null && TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var version)) + { + info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); + return true; + } } } } @@ -172,9 +175,7 @@ private static bool TryDecodeOSVersion( versionBuilder[index++] = osVersionPart; } - osVersion = versionBuilder[3] == 0 ? versionBuilder[2] == 0 ? new Version(versionBuilder[0], versionBuilder[1]) : - new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2]) : - new Version(versionBuilder[0], versionBuilder[1], versionBuilder[2], versionBuilder[3]); + osVersion = CreateVersion(versionBuilder); return true; @@ -202,6 +203,25 @@ static bool TryDecodeOSVersionPart(IArgumentOperation argument, ValueContentAnal 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() diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 45d2daf3e4..f7b100ff17 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -768,6 +768,18 @@ private static void AddOrUpdateUnsupportedAttribute(PlatformAttributes attribute { 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 @@ -797,6 +809,18 @@ private static void AddOrUpdateSupportedAttribute(PlatformAttributes attributes, { 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 diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index a80f33f97b..4da01e1fd4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1432,13 +1432,13 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} String parameters passed by value with the 'OutAttribute' can destabilize the runtime if the string is an interned string. @@ -1468,9 +1468,9 @@ '{0}' will throw for assemblies embedded in a single-file app - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' - '{0}' requires {1} platform + '{0}' requires '{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 89386a4813..e480ed2a83 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later 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 1bfe71110d..99e996ca4f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later 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 220a4d3277..3080fc73ab 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later 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 80ed9ee4e7..0a2aecc62d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later 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 869d80e4c2..26ff0e9170 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later 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 4ec33417d8..e6ace06328 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later 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 f536e5f038..936ddb6174 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later 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 dadf257d6a..be36aad2a1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1519,8 +1519,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1529,23 +1529,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later 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 0675f800ec..5137614818 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 @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later 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 53ef842057..edcb29e929 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later 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 b893209c77..7f15d6ec16 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later 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 5e29e6fca2..0fc0107c69 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 @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later 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 e99700cff5..6deeb50d89 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 @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since {1} {2} version - '{0}' has been deprecated since {1} {2} version + '{0}' has been deprecated since '{1}' {2} + '{0}' has been deprecated since '{1}' {2} @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from {1} - '{0}' is not supported or has been removed from {1} + '{0}' is not supported or has been removed from '{1}' + '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed since {1} {2} version - '{0}' is not supported or has been removed since {1} {2} version + '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is not supported or has been removed since '{1}' {2} - '{0}' requires {1} platform - '{0}' requires {1} platform + '{0}' requires '{1}' + '{0}' requires '{1}' - '{0}' requires {1} {2} version or later - '{0}' requires {1} {2} version or later + '{0}' requires '{1}' {2} or later + '{0}' requires '{1}' {2} or later diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index dcfad385b9..fe66fb51d5 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -70,7 +70,7 @@ void Api() await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(14, 9) - .WithMessage("'Api' is not supported or has been removed since ios 14.0 version")); + .WithMessage("'Api' is not supported or has been removed since 'ios' 14.0")); } [Fact] @@ -329,6 +329,42 @@ void M2() await VerifyAnalyzerAsyncCs(source); } + [Fact] + public async Task GuardedCalled_SimpleIfElse_VersionNotMatch_Warns() + { + var source = @" +using System.Runtime.Versioning; +using System; + +[assembly:SupportedOSPlatform(""windows7.0"")] + +static class Program +{ + public static void Main() + { + if (OperatingSystemHelper.IsWindowsVersionAtLeast(10)) + { + [|WindowsSpecificApis.WindowsOnlyMethod()|]; + } + else + { + [|WindowsSpecificApis.WindowsOnlyMethod()|]; + } + } +} + +public class WindowsSpecificApis +{ + [SupportedOSPlatform(""windows10.1.2.3"")] + public static void WindowsOnlyMethod() { } + + [UnsupportedOSPlatform(""windows10.1.2.3"")] + public static void UnsupportedWindows10() { } +} +" + MockAttributesCsSource + MockRuntimeApiSource; + await VerifyAnalyzerAsyncCs(source); + } + [Fact] public async Task GuardedCalled_SimpleIf_NotWarns() { @@ -761,7 +797,7 @@ public void M2() } [Fact] - public async Task LocalFunctionCallsPlatformDependentMember_InvokedFromDifferentContext() + public async Task LocalFunctionCallsPlatformDependentMember_InvokedFromNotGuardedDifferentContext() { var source = @" using System.Runtime.Versioning; @@ -771,9 +807,11 @@ public class Test { void M() { - if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) + LocalM(); + + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 10, 2)) { - LocalM(); // We don't need to account this platform check + LocalM(); } LocalM(); @@ -781,6 +819,8 @@ void M() void LocalM() { + [|WindowsOnlyMethod()|]; + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) { WindowsOnlyMethod(); @@ -789,6 +829,68 @@ void LocalM() { [|WindowsOnlyMethod()|]; } + + if (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10,0)) + { + UnsupportedWindows10(); + } + else + { + [|UnsupportedWindows10()|]; + } + } + } + + [SupportedOSPlatform(""Windows10.1.2.3"")] + public void WindowsOnlyMethod() + { + } + + [UnsupportedOSPlatform(""Windows10.0"")] + public void UnsupportedWindows10() + { + } +} +" + MockAttributesCsSource + MockRuntimeApiSource; + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task LocalFunctionCallsPlatformDependentMember_InvokedFromGuardedDifferentContext() + { + var source = @" +using System.Runtime.Versioning; +using System; + +public class Test +{ + void M() + { + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) + { + LocalM(); + } + + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) + { + LocalM(); + } + + return; + + void LocalM() + { + WindowsOnlyMethod(); + + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) + { + WindowsOnlyMethod(); + } + else + { + WindowsOnlyMethod(); + } + if (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10,0)) { UnsupportedWindows10(); diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index c4e57e87ef..ad2e39c926 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -17,7 +17,7 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests public partial class PlatformCompatabilityAnalyzerTests { - /*[Fact] TODO: Compiler error test, not sure how to report the diagnostic + /*[Fact] TODO: Test for Compiler error for wrong arguments, not sure how to report the diagnostic public async Task TestOsPlatformAttributesWithNonStringArgument() { var csSource = @" @@ -732,6 +732,32 @@ public void M1() await VerifyAnalyzerAsyncCs(source); } + [Fact] + public async Task UsingVersionedApiFromUnversionedAssembly() + { + var source = @" +using System.Runtime.Versioning; +[assembly: SupportedOSPlatform(""windows"")] + +static class Program +{ + public static void Main() + { + [|Some.WindowsSpecificApi()|]; + } +} + +[SupportedOSPlatform(""windows10.0"")] +static class Some +{ + public static void WindowsSpecificApi() + { + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source); + } + /*[Fact] TODO wait until assembly level APIs merged public async Task MethodOfOsDependentAssemblyCalledWithoutSuppressionWarns() { @@ -951,9 +977,9 @@ public static void LinuxMethod() " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(11, 9) - .WithMessage("'LinuxMethod' is not supported or has been removed from linux").WithArguments("LinuxMethod", "linux"), + .WithMessage("'LinuxMethod' is not supported or has been removed from 'linux'").WithArguments("LinuxMethod", "linux"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(12, 9) - .WithMessage("'LinuxVersionedMethod' is not supported or has been removed since linux 4.8 version").WithArguments("LinuxMethod", "linux")); + .WithMessage("'LinuxVersionedMethod' is not supported or has been removed since 'linux' 4.8").WithArguments("LinuxMethod", "linux")); } @@ -1023,14 +1049,14 @@ public void DoesNotWorkOnWindows() } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed from windows"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' requires windows 10.0.1903 version or later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed from windows"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' requires windows 10.0.1903 version or later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since windows 10.0.2004 version")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed from 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' requires 'windows' 10.0.1903 or later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed from 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' requires 'windows' 10.0.1903 or later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since 'windows' 10.0.2004")); } [Fact] @@ -1098,15 +1124,15 @@ public void DoesNotWorkOnWindows() } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' requires windows platform"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since windows 10.0.2004 version"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' requires windows platform"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since windows 10.0.2004 version"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since windows 10.0.1909 version"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since windows 10.0.2004 version")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' requires 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since 'windows' 10.0.2004"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' requires 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since 'windows' 10.0.2004"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since 'windows' 10.0.2004")); } private static VerifyCS.Test PopulateTestCs(string sourceCode, params DiagnosticResult[] expected) diff --git a/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs b/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs index 58fc26b06d..1525010b3c 100644 --- a/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs +++ b/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs @@ -139,7 +139,7 @@ public TAbstractAnalysisValue this[IOperation operation] { foreach (var kvp in _interproceduralResultsMap) { - if (kvp.Key is IInvocationOperation iOperation && iOperation.TargetMethod.OriginalDefinition.Equals(symbol)) + if (kvp.Key is IInvocationOperation iOperation && iOperation.TargetMethod.Equals(symbol)) { return (DataFlowAnalysisResult)kvp.Value; } From 1eba4b85b49c296650db237a417004ed37694f66 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 14 Aug 2020 16:03:57 -0700 Subject: [PATCH 23/48] Fixing 4012, 4010 and applying more feedback --- .../PlatformCompatabilityAnalyzer.cs | 71 +++++--- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 169 +++++++++++++----- .../PlatformCompatabilityAnalyzerTests.cs | 34 +++- .../DataFlow/DataFlowAnalysisResult.cs | 6 +- 4 files changed, 211 insertions(+), 69 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index f7b100ff17..d002582889 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -130,15 +130,25 @@ public override void Initialize(AnalysisContext context) return; } - var guardMethods = GetRuntimePlatformGuardMethods(runtimeInformationType, operatingSystemType!); + 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 = GetRuntimePlatformGuardMethods(runtimeIsOSPlatformMethod, operatingSystemType!); context.RegisterOperationBlockStartAction(context => AnalyzeOperationBlock(context, guardMethods, osPlatformType)); }); - static ImmutableArray GetRuntimePlatformGuardMethods(INamedTypeSymbol runtimeInformationType, INamedTypeSymbol operatingSystemType) + static ImmutableArray GetRuntimePlatformGuardMethods(IMethodSymbol runtimeIsOSPlatformMethod, INamedTypeSymbol operatingSystemType) { - return operatingSystemType.GetMembers().OfType().Where(m => s_platformCheckMethodNames.Contains(m.Name)).ToImmutableArray(). - Add(runtimeInformationType.GetMembers().OfType().Where(m => IsOSPlatform == m.Name).FirstOrDefault()); + return operatingSystemType.GetMembers().OfType().Where(m => + s_platformCheckMethodNames.Contains(m.Name) && + m.IsStatic && + m.ReturnType.SpecialType == SpecialType.System_Boolean).ToImmutableArray(). + Add(runtimeIsOSPlatformMethod); } } @@ -184,32 +194,41 @@ private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, I return; } - foreach (var platformSpecificOperation in platformSpecificOperations) + foreach (var (platformSpecificOperation, attributes) in platformSpecificOperations) { - var value = analysisResult[platformSpecificOperation.Key.Kind, platformSpecificOperation.Key.Syntax]; - var attribute = platformSpecificOperation.Value; + var value = analysisResult[platformSpecificOperation.Kind, platformSpecificOperation.Syntax]; if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown) { - if (platformSpecificOperation.Key.TryGetContainingLocalOrLambdaFunctionSymbol(out var containingSymbol)) + if (platformSpecificOperation.TryGetContainingLocalOrLambdaFunctionSymbol(out var containingSymbol)) { - var localResult = analysisResult.TryGetInterproceduralResultByDefinition(containingSymbol); - if (localResult != null) + var localResults = analysisResult.TryGetInterproceduralResultByDefinition(containingSymbol); + if (localResults != null) { - var localValue = localResult[platformSpecificOperation.Key.Kind, platformSpecificOperation.Key.Syntax]; - if (localValue.Kind == GlobalFlowStateAnalysisValueSetKind.Known && IsKnownValueGuarded(attribute, localValue)) + var hasKnownUnguardedValue = false; + foreach (var localResult in localResults) + { + var localValue = localResult[platformSpecificOperation.Kind, platformSpecificOperation.Syntax]; + if (localValue.Kind == GlobalFlowStateAnalysisValueSetKind.Known && IsKnownValueGuarded(attributes, localValue)) + { + hasKnownUnguardedValue = true; + break; + } + } + + if (hasKnownUnguardedValue) { continue; } } } } - else if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Known && IsKnownValueGuarded(attribute, value)) + else if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Known && IsKnownValueGuarded(attributes, value)) { continue; } - ReportDiagnostics(platformSpecificOperation.Key, attribute, context); + ReportDiagnostics(platformSpecificOperation, attributes, context); } } finally @@ -227,7 +246,7 @@ private static bool IsKnownValueGuarded(SmallDictionary.GetInstance(StringComparer.OrdinalIgnoreCase); - var capturedVersions = PooledDictionary.GetInstance(StringComparer.OrdinalIgnoreCase); + using var capturedPlatforms = PooledSortedSet.GetInstance(StringComparer.OrdinalIgnoreCase); + using var capturedVersions = PooledDictionary.GetInstance(StringComparer.OrdinalIgnoreCase); foreach (var analysisValue in value.AnalysisValues) { @@ -295,7 +314,8 @@ private static bool IsKnownValueGuarded(SmallDictionary= version) { @@ -352,7 +372,14 @@ private static void ReportDiagnosticsForAll(PooledConcurrentDictionary attributes, OperationBlockAnalysisContext context) { - var operationName = GetOperationSymbol(operation)?.Name ?? string.Empty; + var symbol = operation is IObjectCreationOperation creation ? creation.Constructor.ContainingType : GetOperationSymbol(operation); + + if (symbol == null) + { + return; + } + + var operationName = symbol.Name; foreach (var platformName in attributes.Keys) { @@ -499,7 +526,9 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary? TryGetInterproceduralResultByDefinition(IMethodSymbol symbol) + internal IEnumerable>? TryGetInterproceduralResultByDefinition(IMethodSymbol symbol) { foreach (var kvp in _interproceduralResultsMap) { if (kvp.Key is IInvocationOperation iOperation && iOperation.TargetMethod.Equals(symbol)) { - return (DataFlowAnalysisResult)kvp.Value; + yield return (DataFlowAnalysisResult)kvp.Value; } } - - return null; } public ControlFlowGraph ControlFlowGraph { get; } From 63b915675bb63b8bb3a7329fb6c10dbe857f557b Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Fri, 14 Aug 2020 17:50:16 -0700 Subject: [PATCH 24/48] Perf suggestions --- .../PlatformCompatabilityAnalyzer.cs | 77 ++++++++----------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index d002582889..5b0a9c4b3f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -155,7 +155,7 @@ static ImmutableArray GetRuntimePlatformGuardMethods(IMethodSymbo private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, ImmutableArray guardMethods, INamedTypeSymbol osPlatformType) { var platformSpecificOperations = PooledConcurrentDictionary>.GetInstance(); - var platformSpecificMembers = PooledConcurrentDictionary>.GetInstance(); + var platformSpecificMembers = PooledConcurrentDictionary?>.GetInstance(); context.RegisterOperationAction(context => { @@ -432,7 +432,7 @@ private static void ReportUnsupportedDiagnostic(IOperation operation, OperationB private static void AnalyzeOperation(IOperation operation, OperationAnalysisContext context, PooledConcurrentDictionary> platformSpecificOperations, - PooledConcurrentDictionary> platformSpecificMembers) + PooledConcurrentDictionary?> platformSpecificMembers) { var symbol = GetOperationSymbol(operation); @@ -441,23 +441,11 @@ private static void AnalyzeOperation(IOperation operation, OperationAnalysisCont return; } - if (!platformSpecificMembers.TryGetValue(symbol.OriginalDefinition, out var operationAttributes) && - TryFindPlatformAttributesApplied(symbol.GetAttributes(), symbol.OriginalDefinition.ContainingSymbol, out operationAttributes)) + if (TryGetOrCreatePlatformAttributes(symbol, platformSpecificMembers, out var operationAttributes)) { - platformSpecificMembers.TryAdd(symbol.OriginalDefinition, operationAttributes); - } - - if (operationAttributes != null && operationAttributes.Any()) - { - if (!platformSpecificMembers.TryGetValue(context.ContainingSymbol.OriginalDefinition, out var callSiteAttribute) && - TryFindContainingSymbolPlatformAttributes(context.ContainingSymbol, out callSiteAttribute)) + if (TryGetOrCreatePlatformAttributes(context.ContainingSymbol, platformSpecificMembers, out var callSiteAttributes)) { - platformSpecificMembers.TryAdd(context.ContainingSymbol.OriginalDefinition, callSiteAttribute); - } - - if (callSiteAttribute != null && callSiteAttribute.Any()) - { - if (IsNotSuppressedByCallSite(operationAttributes, callSiteAttribute, out var notSuppressedAttributes)) + if (IsNotSuppressedByCallSite(operationAttributes, callSiteAttributes, out var notSuppressedAttributes)) { platformSpecificOperations.TryAdd(operation, notSuppressedAttributes); } @@ -690,42 +678,45 @@ pOperation.Parent is IBinaryOperation bo && bo.OperatorKind == BinaryOperatorKind.GreaterThanOrEqual || bo.OperatorKind == BinaryOperatorKind.LessThanOrEqual); - private static bool TryFindPlatformAttributesApplied(ImmutableArray immediateAttributes, - ISymbol containingSymbol, [NotNullWhen(true)] out SmallDictionary? attributes) + private static bool TryGetOrCreatePlatformAttributes( + ISymbol symbol, + PooledConcurrentDictionary?> platformSpecificMembers, + [NotNullWhen(true)] out SmallDictionary? attributes) { - attributes = null; - AddPlatformAttributes(immediateAttributes, ref attributes); - - while (containingSymbol != null) + if (!platformSpecificMembers.TryGetValue(symbol, out attributes)) { - AddPlatformAttributes(containingSymbol.GetAttributes(), ref attributes); - containingSymbol = containingSymbol.ContainingSymbol; - } - return attributes != null; - } + var container = symbol.ContainingSymbol; - private static bool AddPlatformAttributes(ImmutableArray immediateAttributes, [NotNullWhen(true)] ref SmallDictionary? attributes) - { - foreach (AttributeData attribute in immediateAttributes) - { - if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name)) + // Namespaces do not have attributes + while (container is INamespaceSymbol) + { + container = container.ContainingSymbol; + } + + if (container != null && + TryGetOrCreatePlatformAttributes(container, platformSpecificMembers, out var containerAttributes)) { - TryAddValidAttribute(ref attributes, attribute); + attributes = CopyOperationAttributes(containerAttributes); } + + AddPlatformAttributes(symbol.GetAttributes(), ref attributes); + + attributes = platformSpecificMembers.GetOrAdd(symbol, attributes); } + return attributes != null; - } - private static bool TryFindContainingSymbolPlatformAttributes(ISymbol containingSymbol, [NotNullWhen(true)] out SmallDictionary? attributes) - { - attributes = null; - while (containingSymbol != null) + static bool AddPlatformAttributes(ImmutableArray immediateAttributes, [NotNullWhen(true)] ref SmallDictionary? attributes) { - AddPlatformAttributes(containingSymbol.GetAttributes(), ref attributes); - containingSymbol = containingSymbol.ContainingSymbol; + foreach (AttributeData attribute in immediateAttributes) + { + if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name)) + { + TryAddValidAttribute(ref attributes, attribute); + } + } + return attributes != null; } - - return attributes != null; } private static bool TryAddValidAttribute([NotNullWhen(true)] ref SmallDictionary? attributes, AttributeData attribute) From 7d25d86b0f3fc1c76890cb0eedce5a7481eeb3bd Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Sat, 15 Aug 2020 06:52:56 -0700 Subject: [PATCH 25/48] Use cache for entire compilation --- .../PlatformCompatabilityAnalyzer.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 5b0a9c4b3f..b5326e5d59 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -1,6 +1,7 @@ // 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.CodeAnalysis; using System.Linq; @@ -138,8 +139,9 @@ public override void Initialize(AnalysisContext context) m.Parameters[0].Type.Equals(osPlatformType)).FirstOrDefault(); var guardMethods = GetRuntimePlatformGuardMethods(runtimeIsOSPlatformMethod, operatingSystemType!); + var platformSpecificMembers = new ConcurrentDictionary?>(); - context.RegisterOperationBlockStartAction(context => AnalyzeOperationBlock(context, guardMethods, osPlatformType)); + context.RegisterOperationBlockStartAction(context => AnalyzeOperationBlock(context, guardMethods, osPlatformType, platformSpecificMembers)); }); static ImmutableArray GetRuntimePlatformGuardMethods(IMethodSymbol runtimeIsOSPlatformMethod, INamedTypeSymbol operatingSystemType) @@ -152,10 +154,13 @@ static ImmutableArray GetRuntimePlatformGuardMethods(IMethodSymbo } } - private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, ImmutableArray guardMethods, INamedTypeSymbol osPlatformType) + private void AnalyzeOperationBlock( + OperationBlockStartAnalysisContext context, + ImmutableArray guardMethods, + INamedTypeSymbol osPlatformType, + ConcurrentDictionary?> platformSpecificMembers) { var platformSpecificOperations = PooledConcurrentDictionary>.GetInstance(); - var platformSpecificMembers = PooledConcurrentDictionary?>.GetInstance(); context.RegisterOperationAction(context => { @@ -432,7 +437,7 @@ private static void ReportUnsupportedDiagnostic(IOperation operation, OperationB private static void AnalyzeOperation(IOperation operation, OperationAnalysisContext context, PooledConcurrentDictionary> platformSpecificOperations, - PooledConcurrentDictionary?> platformSpecificMembers) + ConcurrentDictionary?> platformSpecificMembers) { var symbol = GetOperationSymbol(operation); @@ -680,7 +685,7 @@ pOperation.Parent is IBinaryOperation bo && private static bool TryGetOrCreatePlatformAttributes( ISymbol symbol, - PooledConcurrentDictionary?> platformSpecificMembers, + ConcurrentDictionary?> platformSpecificMembers, [NotNullWhen(true)] out SmallDictionary? attributes) { if (!platformSpecificMembers.TryGetValue(symbol, out attributes)) From 5c9a8601382edbc1d98cd3af0626605e20245a76 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Sat, 15 Aug 2020 09:49:38 -0700 Subject: [PATCH 26/48] Handle || guards --- .../PlatformCompatabilityAnalyzer.cs | 115 ++++++++++-------- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 2 +- .../Compiler/PooledObjects/PooledSortedSet.cs | 17 +++ 3 files changed, 81 insertions(+), 53 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index b5326e5d59..ba76331be6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -249,56 +249,36 @@ private void AnalyzeOperationBlock( private static bool IsKnownValueGuarded(SmallDictionary attributes, GlobalFlowStateAnalysisValueSet value) { - if (value.AnalysisValues.Count == 1) + using var capturedPlatforms = PooledSortedSet.GetInstance(StringComparer.OrdinalIgnoreCase); + using var capturedVersions = PooledDictionary.GetInstance(StringComparer.OrdinalIgnoreCase); + return IsKnownValueGuarded(attributes, value, capturedPlatforms, capturedVersions); + + static bool IsKnownValueGuarded( + SmallDictionary attributes, + GlobalFlowStateAnalysisValueSet value, + PooledSortedSet capturedPlatforms, + PooledDictionary capturedVersions) { - // Singel valued result, no '&&' nor '||' operators are used, result can be consumed directly - var analysisValue = value.AnalysisValues.First(); - - if (analysisValue is RuntimeMethodValue info && - attributes.TryGetValue(info.PlatformName, out var attribute)) - { - if (info.Negated) - { - if (attribute.UnsupportedFirst != null && IsEmptyVersion(attribute.UnsupportedFirst) && IsEmptyVersion(info.Version)) - { - // the unsupported attribute is suppressed, setting it to null to not warn further, same logic for all checks below - attribute.UnsupportedFirst = null; - } - - if (attribute.UnsupportedSecond != null && IsEmptyVersion(attribute.UnsupportedSecond) && IsEmptyVersion(info.Version)) - { - attribute.UnsupportedSecond = null; - } - } - else - { - if (attribute.SupportedFirst != null && attribute.SupportedFirst <= info.Version) - { - attribute.SupportedFirst = null; - } - - if (attribute.SupportedSecond != null && attribute.SupportedSecond <= info.Version) - { - attribute.SupportedSecond = null; - } - } - } - } - else - { - // Analysis values conjuncted with &&, temporary containers keep track of previous values - using var capturedPlatforms = PooledSortedSet.GetInstance(StringComparer.OrdinalIgnoreCase); - using var capturedVersions = PooledDictionary.GetInstance(StringComparer.OrdinalIgnoreCase); - + // 'GlobalFlowStateAnalysisValueSet.AnalysisValues' represent the && of values. foreach (var analysisValue in value.AnalysisValues) { if (analysisValue is RuntimeMethodValue info && attributes.TryGetValue(info.PlatformName, out var attribute)) { if (info.Negated) { - if (attribute.UnsupportedFirst != null && capturedPlatforms.Contains(info.PlatformName) && attribute.UnsupportedFirst >= info.Version) + if (attribute.UnsupportedFirst != null) { - attribute.UnsupportedFirst = null; + if (capturedPlatforms.Contains(info.PlatformName)) + { + if (attribute.UnsupportedFirst >= info.Version) + { + attribute.UnsupportedFirst = null; + } + } + else if (IsEmptyVersion(attribute.UnsupportedFirst) && IsEmptyVersion(info.Version)) + { + attribute.UnsupportedFirst = null; + } } if (attribute.Obsoleted != null && capturedPlatforms.Contains(info.PlatformName) && attribute.Obsoleted <= info.Version) @@ -306,9 +286,19 @@ private static bool IsKnownValueGuarded(SmallDictionary.GetInstance(capturedPlatforms); + using var parentCapturedVersions = PooledDictionary.GetInstance(capturedVersions); + + if (!IsKnownValueGuarded(parentAttributes, parent, parentCapturedPlatforms, parentCapturedVersions)) + { + return false; + } + } } - } - return true; + return true; + } } private static bool IsEmptyVersion(Version version) => version.Major == 0 && version.Minor == 0; diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index b1824705f0..d1c9e23d29 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -1434,7 +1434,7 @@ public void M1() if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) || OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) { - [|M2()|]; // Even it is not meaningful check i think it is a bug, it shouldn't warn + M2(); // Even it is not meaningful check, it shouldn't warn } else { diff --git a/src/Utilities/Compiler/PooledObjects/PooledSortedSet.cs b/src/Utilities/Compiler/PooledObjects/PooledSortedSet.cs index 3f012cc2f0..c8c5ebacc8 100644 --- a/src/Utilities/Compiler/PooledObjects/PooledSortedSet.cs +++ b/src/Utilities/Compiler/PooledObjects/PooledSortedSet.cs @@ -59,5 +59,22 @@ public static PooledSortedSet GetInstance(IComparer? comparer = null) Debug.Assert(instance.Count == 0); return instance; } + + /// + /// Gets a pooled instance of a with an initializer and an optional comparer. + /// + /// Initial values for the set. + /// Comparer to use, or null for the element type's default comparer. + /// An empty . + public static PooledSortedSet GetInstance(IEnumerable initializer, IComparer? comparer = null) + { + var instance = GetInstance(comparer); + foreach (var value in initializer) + { + instance.Add(value); + } + + return instance; + } } } From 70badce80b2298ea62da554c0083d74045865a08 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Sat, 15 Aug 2020 12:32:55 -0700 Subject: [PATCH 27/48] Fix interprocedural analysis and also restrict when ValueContentAnalysis is needed --- .../PlatformCompatabilityAnalyzer.cs | 32 ++++++++++++-- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 44 +++++++++++++++++-- .../PlatformCompatabilityAnalyzerTests.cs | 4 +- .../GlobalFlowStateAnalysis.cs | 13 ++++-- ...GlobalFlowStateDataFlowOperationVisitor.cs | 15 ++++++- 5 files changed, 94 insertions(+), 14 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index ba76331be6..faf33382a9 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -161,10 +161,11 @@ private void AnalyzeOperationBlock( ConcurrentDictionary?> platformSpecificMembers) { var platformSpecificOperations = PooledConcurrentDictionary>.GetInstance(); + bool needsValueContentAnalysis = false; context.RegisterOperationAction(context => { - AnalyzeOperation(context.Operation, context, platformSpecificOperations, platformSpecificMembers); + AnalyzeOperation(context.Operation, context, platformSpecificOperations, platformSpecificMembers, guardMethods, ref needsValueContentAnalysis); }, OperationKind.MethodReference, OperationKind.EventReference, @@ -191,7 +192,7 @@ private void AnalyzeOperationBlock( var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); var analysisResult = GlobalFlowStateAnalysis.TryGetOrComputeResult( cfg, context.OwningSymbol, CreateOperationVisitor, wellKnownTypeProvider, - context.Options, RequiredOsRule, performValueContentAnalysis: true, + context.Options, RequiredOsRule, performValueContentAnalysis: needsValueContentAnalysis, context.CancellationToken, out var valueContentAnalysisResult); if (analysisResult == null) @@ -448,7 +449,9 @@ private static void ReportUnsupportedDiagnostic(IOperation operation, OperationB private static void AnalyzeOperation(IOperation operation, OperationAnalysisContext context, PooledConcurrentDictionary> platformSpecificOperations, - ConcurrentDictionary?> platformSpecificMembers) + ConcurrentDictionary?> platformSpecificMembers, + ImmutableArray guardMethods, + ref bool needsValueContentAnalysis) { var symbol = GetOperationSymbol(operation); @@ -471,6 +474,29 @@ private static void AnalyzeOperation(IOperation operation, OperationAnalysisCont platformSpecificOperations.TryAdd(operation, CopyOperationAttributes(operationAttributes)); } } + + if (guardMethods.Contains(symbol) && + operation is IInvocationOperation invocation) + { + needsValueContentAnalysis = needsValueContentAnalysis || ComputeNeedsValueContentAnalysis(invocation); + } + + return; + + static bool ComputeNeedsValueContentAnalysis(IInvocationOperation invocation) + { + // 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 SmallDictionary CopyOperationAttributes(SmallDictionary attributes) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index d1c9e23d29..655beaba19 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -1823,7 +1823,7 @@ End Class await VerifyAnalyzerAsyncVb(vbSource); } - /*[Fact] //TODO: Not working anymore, fix this + [Fact] public async Task InterproceduralAnalysisTest() { var source = @" @@ -1853,10 +1853,46 @@ bool IsWindows11OrLater() { return OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"",10,2,3,4); } -}" + MockAttributesSource + MockRuntimeApiSource; +}" + MockAttributesCsSource + MockOperatingSystemApiSource; - await VerifyAnalyzerAsyncCs(source, @"{ ("".editorconfig"", ""dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive"") }"); - }*/ + await VerifyAnalyzerAsyncCs(source, "dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive"); + } + + [Fact(Skip = "TODO: Needs to be fixed")] + public async Task InterproceduralAnalysisTest_LogicalOr() + { + var source = @" +using System.Runtime.Versioning; +using System; + +class Test +{ + void M1() + { + [|M2()|]; + + if (IsWindows11OrLater()) + { + M2(); + } + + [|M2()|]; + } + + [SupportedOSPlatform(""Windows10.1.2.3"")] + void M2() + { + } + + bool IsWindows11OrLater() + { + return OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"",10,2,3,4) || + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"",11); + } +}" + MockAttributesCsSource + MockOperatingSystemApiSource; + + await VerifyAnalyzerAsyncCs(source, "dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive"); + } private readonly string MockOperatingSystemApiSource = @" namespace System diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index d6c012bdfe..0acfbee3c4 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -1181,10 +1181,10 @@ private static VerifyCS.Test PopulateTestCs(string sourceCode, params Diagnostic private static async Task VerifyAnalyzerAsyncCs(string sourceCode, params DiagnosticResult[] expectedDiagnostics) => await PopulateTestCs(sourceCode, expectedDiagnostics).RunAsync(); - private static async Task VerifyAnalyzerAsyncCs(string sourceCode, string additionalFiles) + private static async Task VerifyAnalyzerAsyncCs(string sourceCode, string editorconfigText) { var test = PopulateTestCs(sourceCode); - test.TestState.AdditionalFiles.Add((".editorconfig", additionalFiles)); + test.TestState.AdditionalFiles.Add((".editorconfig", editorconfigText)); await test.RunAsync(); } diff --git a/src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysis.cs b/src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysis.cs index 9ffb6f5030..f58b98081f 100644 --- a/src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysis.cs +++ b/src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysis.cs @@ -42,8 +42,10 @@ private GlobalFlowStateAnalysis(GlobalFlowStateAnalysisDomain analysisDomain, Gl { var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create( analyzerOptions, rule, owningSymbol, wellKnownTypeProvider.Compilation, interproceduralAnalysisKind, cancellationToken); + var pointsToAnalysisKind = analyzerOptions.GetPointsToAnalysisKindOption(rule, owningSymbol, wellKnownTypeProvider.Compilation, + defaultValue: PointsToAnalysisKind.PartialWithoutTrackingFieldsAndProperties, cancellationToken); return TryGetOrComputeResult(cfg, owningSymbol, createOperationVisitor, wellKnownTypeProvider, analyzerOptions, - interproceduralAnalysisConfig, interproceduralAnalysisPredicate, pessimisticAnalysis, + interproceduralAnalysisConfig, interproceduralAnalysisPredicate, pointsToAnalysisKind, pessimisticAnalysis, performValueContentAnalysis, out valueContentAnalysisResult); } @@ -55,6 +57,7 @@ private GlobalFlowStateAnalysis(GlobalFlowStateAnalysisDomain analysisDomain, Gl AnalyzerOptions analyzerOptions, InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate, + PointsToAnalysisKind pointsToAnalysisKind, bool pessimisticAnalysis, bool performValueContentAnalysis, out ValueContentAnalysisResult? valueContentAnalysisResult) @@ -63,16 +66,18 @@ private GlobalFlowStateAnalysis(GlobalFlowStateAnalysisDomain analysisDomain, Gl RoslynDebug.Assert(owningSymbol != null); PointsToAnalysisResult? pointsToAnalysisResult = null; - valueContentAnalysisResult = performValueContentAnalysis ? ValueContentAnalysis.ValueContentAnalysis.TryGetOrComputeResult( cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider, - PointsToAnalysisKind.PartialWithoutTrackingFieldsAndProperties, - interproceduralAnalysisConfig, out _, + pointsToAnalysisKind, interproceduralAnalysisConfig, out _, out pointsToAnalysisResult, pessimisticAnalysis, performCopyAnalysis: false, interproceduralAnalysisPredicate) : null; + pointsToAnalysisResult ??= PointsToAnalysis.PointsToAnalysis.TryGetOrComputeResult( + cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider, + pointsToAnalysisKind, interproceduralAnalysisConfig, interproceduralAnalysisPredicate); + var analysisContext = GlobalFlowStateAnalysisContext.Create( GlobalFlowStateAnalysisValueSetDomain.Instance, wellKnownTypeProvider, cfg, owningSymbol, analyzerOptions, interproceduralAnalysisConfig, pessimisticAnalysis, pointsToAnalysisResult, diff --git a/src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateDataFlowOperationVisitor.cs b/src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateDataFlowOperationVisitor.cs index 9d5b8c0fd4..3cc4344226 100644 --- a/src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateDataFlowOperationVisitor.cs +++ b/src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateDataFlowOperationVisitor.cs @@ -5,7 +5,9 @@ using System.Diagnostics; using System.Linq; using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; using Microsoft.CodeAnalysis.Operations; using static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis.GlobalFlowStateAnalysis; @@ -99,7 +101,7 @@ protected void MergeAndSetGlobalState(GlobalFlowStateAnalysisValueSet value, boo { Debug.Assert(_hasPredicatedGlobalState || !negate); - if (!value.AnalysisValues.IsEmpty) + if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Known) { var currentGlobalValue = GetAbstractValue(_globalEntity); if (currentGlobalValue.Kind != GlobalFlowStateAnalysisValueSetKind.Unknown) @@ -174,6 +176,17 @@ protected sealed override void ApplyInterproceduralAnalysisResultCore(GlobalFlow => ApplyInterproceduralAnalysisResultHelper(resultData); protected sealed override GlobalFlowStateAnalysisData GetTrimmedCurrentAnalysisData(IEnumerable withEntities) => GetTrimmedCurrentAnalysisDataHelper(withEntities, CurrentAnalysisData, SetAbstractValue); + protected override GlobalFlowStateAnalysisData GetInitialInterproceduralAnalysisData( + IMethodSymbol invokedMethod, + (AnalysisEntity? Instance, PointsToAbstractValue PointsToValue)? invocationInstance, + (AnalysisEntity Instance, PointsToAbstractValue PointsToValue)? thisOrMeInstanceForCaller, + ImmutableDictionary> argumentValuesMap, + IDictionary? pointsToValues, + IDictionary? copyValues, + IDictionary? valueContentValues, + bool isLambdaOrLocalFunction, + bool hasParameterWithDelegateType) + => GetClonedCurrentAnalysisData(); protected GlobalFlowStateAnalysisValueSet GetValueOrDefault(GlobalFlowStateAnalysisValueSet value) => value.Kind == GlobalFlowStateAnalysisValueSetKind.Known ? value : GlobalState; From 8d68a2945a8152c6bea76572ab39605828508ee9 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Sat, 15 Aug 2020 15:50:58 -0700 Subject: [PATCH 28/48] Lazilly compute needs value content analysis for improved perf --- .../PlatformCompatabilityAnalyzer.cs | 57 +++++++++---------- .../Extensions/OperationBlocksExtensions.cs | 8 ++- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index faf33382a9..de6bc4591f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -161,11 +161,10 @@ private void AnalyzeOperationBlock( ConcurrentDictionary?> platformSpecificMembers) { var platformSpecificOperations = PooledConcurrentDictionary>.GetInstance(); - bool needsValueContentAnalysis = false; context.RegisterOperationAction(context => { - AnalyzeOperation(context.Operation, context, platformSpecificOperations, platformSpecificMembers, guardMethods, ref needsValueContentAnalysis); + AnalyzeOperation(context.Operation, context, platformSpecificOperations, platformSpecificMembers); }, OperationKind.MethodReference, OperationKind.EventReference, @@ -183,16 +182,17 @@ private void AnalyzeOperationBlock( return; } - if (guardMethods.IsEmpty || !(context.OperationBlocks.GetControlFlowGraph() is { } cfg)) + 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, RequiredOsRule, performValueContentAnalysis: needsValueContentAnalysis, + context.Options, RequiredOsRule, performValueContentAnalysis, context.CancellationToken, out var valueContentAnalysisResult); if (analysisResult == null) @@ -248,6 +248,28 @@ private void AnalyzeOperationBlock( }); } + 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 capturedPlatforms = PooledSortedSet.GetInstance(StringComparer.OrdinalIgnoreCase); @@ -449,9 +471,7 @@ private static void ReportUnsupportedDiagnostic(IOperation operation, OperationB private static void AnalyzeOperation(IOperation operation, OperationAnalysisContext context, PooledConcurrentDictionary> platformSpecificOperations, - ConcurrentDictionary?> platformSpecificMembers, - ImmutableArray guardMethods, - ref bool needsValueContentAnalysis) + ConcurrentDictionary?> platformSpecificMembers) { var symbol = GetOperationSymbol(operation); @@ -474,29 +494,6 @@ private static void AnalyzeOperation(IOperation operation, OperationAnalysisCont platformSpecificOperations.TryAdd(operation, CopyOperationAttributes(operationAttributes)); } } - - if (guardMethods.Contains(symbol) && - operation is IInvocationOperation invocation) - { - needsValueContentAnalysis = needsValueContentAnalysis || ComputeNeedsValueContentAnalysis(invocation); - } - - return; - - static bool ComputeNeedsValueContentAnalysis(IInvocationOperation invocation) - { - // 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 SmallDictionary CopyOperationAttributes(SmallDictionary attributes) diff --git a/src/Utilities/FlowAnalysis/Extensions/OperationBlocksExtensions.cs b/src/Utilities/FlowAnalysis/Extensions/OperationBlocksExtensions.cs index f75c2b1353..1032006d02 100644 --- a/src/Utilities/FlowAnalysis/Extensions/OperationBlocksExtensions.cs +++ b/src/Utilities/FlowAnalysis/Extensions/OperationBlocksExtensions.cs @@ -9,18 +9,22 @@ namespace Analyzer.Utilities.Extensions { internal static partial class OperationBlocksExtensions { - public static ControlFlowGraph? GetControlFlowGraph(this ImmutableArray operationBlocks) + public static ControlFlowGraph? GetControlFlowGraph(this ImmutableArray operationBlocks, out IBlockOperation? topmostBlock) { foreach (var operationRoot in operationBlocks) { - IBlockOperation? topmostBlock = operationRoot.GetTopmostParentBlock(); + topmostBlock = operationRoot.GetTopmostParentBlock(); if (topmostBlock != null) { return topmostBlock.GetEnclosingControlFlowGraph(); } } + topmostBlock = null; return null; } + + public static ControlFlowGraph? GetControlFlowGraph(this ImmutableArray operationBlocks) + => operationBlocks.GetControlFlowGraph(out _); } } From c847d9dd8a924eb48275569209af18294031604a Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Sat, 15 Aug 2020 15:51:34 -0700 Subject: [PATCH 29/48] Free dictionary after usage remove some unneeded anymore code sections --- .../PlatformCompatabilityAnalyzer.cs | 28 ++++++++----------- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 4 +-- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index d002582889..b62e664320 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -234,6 +234,7 @@ private void AnalyzeOperationBlock(OperationBlockStartAnalysisContext context, I finally { platformSpecificOperations.Free(); + platformSpecificMembers.Free(); } return; @@ -735,26 +736,19 @@ attribute.ConstructorArguments[0] is { } argument && argument.Kind == TypedConstantKind.Primitive && argument.Type.SpecialType == SpecialType.System_String && !argument.IsNull && - !argument.Value.Equals(string.Empty)) + !argument.Value.Equals(string.Empty) && + TryParsePlatformNameAndVersion(argument.Value.ToString(), out string platformName, out Version? version)) { - if (TryParsePlatformNameAndVersion(argument.Value.ToString(), out string platformName, out Version? version)) - { - attributes ??= new SmallDictionary(StringComparer.OrdinalIgnoreCase); + attributes ??= new SmallDictionary(StringComparer.OrdinalIgnoreCase); - if (!attributes.TryGetValue(platformName, out var existingAttributes)) - { - existingAttributes = new PlatformAttributes(); - attributes[platformName] = existingAttributes; - } - - AddAttribute(attribute.AttributeClass.Name, version, existingAttributes); - return true; + if (!attributes.TryGetValue(platformName, out var existingAttributes)) + { + existingAttributes = new PlatformAttributes(); + attributes[platformName] = existingAttributes; } - // else report diagnostic = Diagnostic.Create(PlatformNameNullOrEmptyRule, osAttribute.ApplicationSyntaxReference.GetSyntax().GetLocation()); - } - else - { - // report Diagnostic.Create(InvalidPlatformVersionRule, osAttribute.ApplicationSyntaxReference.GetSyntax().GetLocation()); + + AddAttribute(attribute.AttributeClass.Name, version, existingAttributes); + return true; } return false; diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index b1824705f0..98609a529c 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -827,7 +827,7 @@ public void M2() await VerifyAnalyzerAsyncCs(source); } - /* [Fact] Failing because we cannot detect the correct local invocation being called + /* [Fact] //Failing because we cannot detect the correct local invocation being called public async Task LocalFunctionCallsPlatformDependentMember_InvokedFromDifferentContext() { var source = @" @@ -837,7 +837,7 @@ public class Test { void M() { - if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 10, 2)) + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) { LocalM(true); } From 12532fe8de679dc2a6fbd7d5f30b63c2647bc4b2 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Sat, 15 Aug 2020 15:54:26 -0700 Subject: [PATCH 30/48] Merge conflict --- .../InteropServices/PlatformCompatabilityAnalyzer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 2e7d1313a8..690a994c7d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -240,7 +240,6 @@ private void AnalyzeOperationBlock( finally { platformSpecificOperations.Free(); - platformSpecificMembers.Free(); } return; From 7b9276dde325fd059020dca3e89b9a1a2f18d8b2 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Sat, 15 Aug 2020 23:27:47 -0700 Subject: [PATCH 31/48] Fix bugs in group #2 --- .../PlatformCompatabilityAnalyzer.cs | 59 +++-- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 22 +- .../PlatformCompatabilityAnalyzerTests.cs | 201 +++++++++++++++++- 3 files changed, 245 insertions(+), 37 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 690a994c7d..d301fdae74 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -525,23 +525,26 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary(StringComparer.OrdinalIgnoreCase); bool? supportedOnlyList = null; +#pragma warning disable CA2000 + var supportedOnlyPlatforms = PooledConcurrentSet.GetInstance(StringComparer.OrdinalIgnoreCase); +#pragma warning restore CA2000 foreach (string key in operationAttributes.Keys) { var attribute = operationAttributes[key]; - var diagnositcAttribute = new PlatformAttributes(); + var diagnosticAttribute = new PlatformAttributes(); if (attribute.SupportedFirst != null) { - // If only supported for current platform if (attribute.UnsupportedFirst == null || attribute.UnsupportedFirst > attribute.SupportedFirst) { + // If only supported for current platform if (supportedOnlyList.HasValue && !supportedOnlyList.Value) { - // report inconsistent list diagnostic return true; // do not need to add this API to the list } else { + supportedOnlyPlatforms.Add(key); supportedOnlyList = true; } @@ -550,14 +553,14 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary v.SupportedFirst != null)) + { + // if call site has no any other supported attribute it means global, so need to warn + diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone(); + } } if (attribute.Obsoleted != null) @@ -655,12 +662,32 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary SupportedOsAttributeTestData() { From 60a4dffb9096329c17dce1e0b14a07f27c14ddda Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Sun, 16 Aug 2020 01:56:31 -0700 Subject: [PATCH 32/48] Fixed bug #1 --- .../PlatformCompatabilityAnalyzer.cs | 45 ++++++++++++++----- .../PlatformCompatabilityAnalyzerTests.cs | 13 +++--- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index d301fdae74..1c046d0be5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -525,6 +525,7 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary(StringComparer.OrdinalIgnoreCase); bool? supportedOnlyList = null; + bool mandatoryMatchFound = false; #pragma warning disable CA2000 var supportedOnlyPlatforms = PooledConcurrentSet.GetInstance(StringComparer.OrdinalIgnoreCase); #pragma warning restore CA2000 @@ -551,7 +552,11 @@ private static bool IsNotSuppressedByCallSite(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; + } + } + private static bool SuppresedByUnsupported(PlatformAttributes callSiteAttribute, Version obsoleted) => callSiteAttribute.UnsupportedFirst != null && callSiteAttribute.UnsupportedFirst <= obsoleted || callSiteAttribute.UnsupportedSecond != null && callSiteAttribute.UnsupportedSecond <= obsoleted; diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index 591671afc9..16f61c0844 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -54,9 +54,9 @@ public async Task OsDependentMethodsCalledWarns() var csSource = @" using System.Runtime.Versioning; -[SupportedOSPlatform(""Linux"")] public class Test { + [SupportedOSPlatform(""Linux"")] public void M1() { [|WindowsOnly()|]; @@ -64,6 +64,7 @@ public void M1() [|Unsupported()|]; [|ObsoletedOverload()|]; } + [SupportedOSPlatform(""Linux"")] [UnsupportedOSPlatform(""Linux4.1"")] public void Unsupported() { @@ -72,10 +73,12 @@ public void Unsupported() public void WindowsOnly() { } + [SupportedOSPlatform(""Linux"")] [ObsoletedInOSPlatform(""Linux4.1"")] public void Obsoleted() { } + [SupportedOSPlatform(""Linux"")] [ObsoletedInOSPlatform(""Linux4.1"", ""Obsolete message"")] public void ObsoletedOverload() { @@ -732,7 +735,7 @@ public void M1() await VerifyAnalyzerAsyncCs(source); } - /*[Fact] + [Fact] public async Task CallerSupportsSubsetOfTarget() { var source = @" @@ -747,7 +750,7 @@ public static void Test() { Target.SupportedOnWindows(); [|Target.SupportedOnBrowser()|]; - Target.SupportedOnWindowsAndBrowser(); //Reported correct diagnostic: 'SupportedOnWindowsAndBrowser' requires 'browser' + Target.SupportedOnWindowsAndBrowser(); } } @@ -758,13 +761,13 @@ public static void SupportedOnWindows() { } [SupportedOSPlatform(""browser"")] public static void SupportedOnBrowser() { } - [SupportedOSPlatform(""windows""), SupportedOSPlatform(""browser"")] + [SupportedOSPlatform(""browser""), SupportedOSPlatform(""windows"")] public static void SupportedOnWindowsAndBrowser() { } } } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source); - }*/ + } [Fact] public async Task CallerSupportsSupersetOfTarget() From 6bc123f5e6f07290b406d8f158d7e95b4d499cb0 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Sun, 16 Aug 2020 10:53:29 -0700 Subject: [PATCH 33/48] Apply workaround for https://github.com/dotnet/roslyn/issues/46859 --- .../InteropServices/PlatformCompatabilityAnalyzer.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 1c046d0be5..d9cf6083f1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -239,7 +239,12 @@ private void AnalyzeOperationBlock( } finally { - platformSpecificOperations.Free(); + // Workaround for https://github.com/dotnet/roslyn/issues/46859 + // Do not free in presence of cancellation. + if (!context.CancellationToken.IsCancellationRequested) + { + platformSpecificOperations.Free(); + } } return; From 99883854e87fce50449674b9638ece153becd082 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Sun, 16 Aug 2020 20:30:10 -0700 Subject: [PATCH 34/48] Add support override #4013, #4020 --- .../PlatformCompatabilityAnalyzer.cs | 58 +++++++++--- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 56 ++++++++++- .../PlatformCompatabilityAnalyzerTests.cs | 94 +++++++++++++++---- 3 files changed, 174 insertions(+), 34 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 1c046d0be5..1d06f8e4a2 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -354,11 +354,13 @@ static bool IsKnownValueGuarded( if (attribute.SupportedFirst != null && attribute.SupportedFirst <= info.Version) { attribute.SupportedFirst = null; + RemoveUnsupportedWithLessVersion(info.Version, attribute); } if (attribute.SupportedSecond != null && attribute.SupportedSecond <= info.Version) { attribute.SupportedSecond = null; + RemoveUnsupportedWithLessVersion(info.Version, attribute); } } } @@ -396,6 +398,14 @@ static bool IsKnownValueGuarded( return true; } + + static void RemoveUnsupportedWithLessVersion(Version supportedVersion, PlatformAttributes attribute) + { + if (attribute.UnsupportedFirst != null && attribute.UnsupportedFirst <= supportedVersion) + { + attribute.UnsupportedFirst = null; + } + } } private static bool IsEmptyVersion(Version version) => version.Major == 0 && version.Minor == 0; @@ -526,9 +536,7 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary(StringComparer.OrdinalIgnoreCase); bool? supportedOnlyList = null; bool mandatoryMatchFound = false; -#pragma warning disable CA2000 - var supportedOnlyPlatforms = PooledConcurrentSet.GetInstance(StringComparer.OrdinalIgnoreCase); -#pragma warning restore CA2000 + using var supportedOnlyPlatforms = PooledHashSet.GetInstance(StringComparer.OrdinalIgnoreCase); foreach (string key in operationAttributes.Keys) { var attribute = operationAttributes[key]; @@ -699,7 +707,6 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary(StringComparer.OrdinalIgnoreCase); - if (!attributes.TryGetValue(platformName, out var existingAttributes)) + if (!attributes.TryGetValue(platformName, out var _)) { - existingAttributes = new PlatformAttributes(); - attributes[platformName] = existingAttributes; + attributes[platformName] = new PlatformAttributes(); } - AddAttribute(attribute.AttributeClass.Name, version, existingAttributes); + AddAttribute(attribute.AttributeClass.Name, version, attributes, platformName); return true; } return false; } - private static void AddAttribute(string name, Version version, PlatformAttributes existingAttributes) + private static void AddAttribute(string name, Version version, SmallDictionary existingAttributes, string platformName) { switch (name) { case ObsoletedInOSPlatformAttribute: - AddOrUpdateObsoletedAttribute(existingAttributes, version); + AddOrUpdateObsoletedAttribute(existingAttributes[platformName], version); break; case SupportedOSPlatformAttribute: - AddOrUpdateSupportedAttribute(existingAttributes, version); + AddOrUpdateSupportedAttribute(existingAttributes[platformName], version); break; case UnsupportedOSPlatformAttribute: - AddOrUpdateUnsupportedAttribute(existingAttributes, version); + AddOrUpdateUnsupportedAttribute(platformName, existingAttributes[platformName], version, existingAttributes); break; } } @@ -866,7 +872,7 @@ private static void AddOrUpdateObsoletedAttribute(PlatformAttributes attributes, } } - private static void AddOrUpdateUnsupportedAttribute(PlatformAttributes attributes, Version version) + private static void AddOrUpdateUnsupportedAttribute(string name, PlatformAttributes attributes, Version version, SmallDictionary existingAttributes) { if (attributes.UnsupportedFirst != null) { @@ -903,10 +909,34 @@ private static void AddOrUpdateUnsupportedAttribute(PlatformAttributes attribute } else { - attributes.UnsupportedFirst = version; + 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 (!HasAnySuppoertedOnlyAttribute(name, existingAttributes)) + { + attributes.UnsupportedFirst = version; + } + } + else + { + attributes.UnsupportedFirst = version; + } } } + private static bool HasAnySuppoertedOnlyAttribute(string name, SmallDictionary existingAttributes) => + existingAttributes.Any(a => a.Key != name && a.Value.SupportedFirst != null && + (a.Value.UnsupportedFirst == null || a.Value.SupportedFirst < a.Value.UnsupportedFirst)); + private static void AddOrUpdateSupportedAttribute(PlatformAttributes attributes, Version version) { if (attributes.SupportedFirst != null) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index 7e108d37a7..f47e3da308 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -73,6 +73,51 @@ await VerifyAnalyzerAsyncCs(source, .WithMessage("'Api' is not supported or has been removed since 'ios' 14.0")); } + [Fact] + public async Task GuardsAroundUnsupported() + { + var source = @" +using System.Runtime.Versioning; +using System; + +class Caller +{ + public static void TestWithGuardMethods() + { + if (!OperatingSystemHelper.IsWindows()) + { + Target.UnsupportedInWindows(); + [|Target.UnsupportedInWindows10()|]; // not windows doesn't mean it is windows 10 or before + } + if (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10)) + { + [|Target.UnsupportedInWindows()|]; + Target.UnsupportedInWindows10(); + } + if (OperatingSystemHelper.IsBrowser()) + { + [|Target.UnsupportedInWindows()|]; // It is browser doesn't mean it is not windows + [|Target.UnsupportedInWindows10()|]; // The same + } + } +} + +class Target +{ + [UnsupportedOSPlatform(""windows"")] + public static void UnsupportedInWindows() { } + + [UnsupportedOSPlatform(""windows10.0"")] + public static void UnsupportedInWindows10() { } +} +" + MockAttributesCsSource + MockOperatingSystemApiSource; + + await VerifyAnalyzerAsyncCs(source); + /*await VerifyAnalyzerAsyncCs(source, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(14, 9) + .WithMessage("'Api' is not supported or has been removed since 'ios' 14.0"));*/ + } + [Fact] public async Task Unsupported_GuardedWith_IsOsNameMethods() { @@ -366,7 +411,7 @@ public static void UnsupportedWindows10() { } } [Fact] - public async Task ReintroducingApiSupport_Guraded_NotWarn() + public async Task ReintroducingApiSupport_Guarded_NotWarn() { var source = @" using System; @@ -381,6 +426,10 @@ public static void Main() { Some.WindowsSpecificApi(); } + else + { + [|Some.WindowsSpecificApi()|]; // should show 2 diagnostic + } } } @@ -393,7 +442,10 @@ public static void WindowsSpecificApi() } } " + MockAttributesCsSource + MockOperatingSystemApiSource; - await VerifyAnalyzerAsyncCs(source); + + await VerifyAnalyzerAsyncCs(source, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(16, 13) + .WithMessage("'WindowsSpecificApi' requires 'windows' 10.0 or later")); } [Fact] diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index 16f61c0844..eaf6fbfb8c 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -719,7 +719,7 @@ public async Task OsDependentMethodAssignedToDelegateWarns() public class Test { - public delegate void Del(); // The attribute not supported on delegates, so no tets for that + public delegate void Del(); // The attribute not supported on delegates, so no tests for that [SupportedOSPlatform(""Windows10.1.2.3"")] public void DelegateMethod() @@ -782,24 +782,24 @@ class Caller [SupportedOSPlatform(""windows""), SupportedOSPlatform(""browser"")] public static void TestWithWindowsAndBrowserSupported() { - [|Target.SupportedOnWindows()|]; // FAIL: no diagnostic; should have a diagnostic for browser - [|Target.SupportedOnBrowser()|]; // FAIL: no diagnostic; should have a diagnostic for browser - Target.SupportedOnWindowsAndBrowser(); // PASS: no diagnostic + [|Target.SupportedOnWindows()|]; + [|Target.SupportedOnBrowser()|]; + Target.SupportedOnWindowsAndBrowser(); - [|Target.UnsupportedOnWindows()|]; // PASS: windows unsupported - [|Target.UnsupportedOnBrowser()|]; // PASS: browser unsupported - [|Target.UnsupportedOnWindowsAndBrowser()|]; // PASS: windows unsupported, browser unsupported + [|Target.UnsupportedOnWindows()|]; + [|Target.UnsupportedOnBrowser()|]; + [|Target.UnsupportedOnWindowsAndBrowser()|]; } [UnsupportedOSPlatform(""browser"")] public static void TestWithBrowserUnsupported() { - [|Target.SupportedOnWindows()|]; // PASS: windows supported - [|Target.SupportedOnBrowser()|]; // PASS: browser supported - [|Target.SupportedOnWindowsAndBrowser()|]; // PASS: windows supported, browser supported + [|Target.SupportedOnWindows()|]; + [|Target.SupportedOnBrowser()|]; + [|Target.SupportedOnWindowsAndBrowser()|]; - [|Target.UnsupportedOnWindows()|]; // FAIL: no diagnostic; should have a diagnostic for windows - Fixed - Target.UnsupportedOnBrowser(); // PASS: no diagnostic - [|Target.UnsupportedOnWindowsAndBrowser()|]; // FAIL: no diagnostic; should have a diagnostic for windows - Fixed + [|Target.UnsupportedOnWindows()|]; + Target.UnsupportedOnBrowser(); + [|Target.UnsupportedOnWindowsAndBrowser()|]; } } @@ -831,7 +831,7 @@ await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAna WithMessage("'SupportedOnWindowsAndBrowser' requires 'windows'").WithArguments("SupportedOnWindowsAndBrowser", "windows")); } - /*[Fact] //TODO fix it now + [Fact] public async Task UnsupportedMustSuppressSupported() { var source = @" @@ -842,7 +842,7 @@ static class Program public static void Main() { [|Some.Api1()|]; - Some.Api2(); + [|Some.Api2()|]; // TvOs is suppressed } } @@ -856,8 +856,66 @@ public static void Api1() {} public static void Api2() {} } " + MockAttributesCsSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(8, 9) + .WithMessage("'Api1' requires 'tvos' 4.0 or later").WithArguments("UnsupportedOnWindowsAndBrowser", "windows")); + } + + /*[Fact] + public async Task OverridesPlatform() + { + var source = @" +using System.Runtime.Versioning; + +namespace PlatformCompatDemo.Bugs +{ + class Caller + { + [SupportedOSPlatform(""windows"")] + public void TestSupportedOnWindows() + { + [|TargetSupportedOnWindows.FunctionUnsupportedOnWindows()|]; // 11 FAIL: should be unsupported on windows + TargetSupportedOnWindows.FunctionUnsupportedOnBrowser(); + + TargetUnsupportedOnWindows.FunctionSupportedOnWindows(); + [|TargetUnsupportedOnWindows.FunctionSupportedOnBrowser()|]; // 15 FAIL: should only be supported on browser + } // - Still should warn unsupported windows + + [UnsupportedOSPlatform(""windows"")] + public void TestUnsupportedOnWindows() + { + TargetSupportedOnWindows.FunctionUnsupportedOnWindows(); + [|TargetSupportedOnWindows.FunctionUnsupportedOnBrowser()|]; // 22 FAIL: being unsupported on browser doesn't matter + // because it should only be supported on windows + // (since an allow-list was found) - No this doesn't have any supported info means for all means browser should warn + + [|TargetUnsupportedOnWindows.FunctionSupportedOnWindows()|]; // 26 FAIL: should only be supported on windows + [|TargetUnsupportedOnWindows.FunctionSupportedOnBrowser()|]; // FAIL: should only be supported on browser } + } + + [SupportedOSPlatform(""windows"")] + class TargetSupportedOnWindows + { + [UnsupportedOSPlatform(""windows"")] + public static void FunctionUnsupportedOnWindows() { } + + [UnsupportedOSPlatform(""browser"")] + public static void FunctionUnsupportedOnBrowser() { } + } + + [UnsupportedOSPlatform(""windows"")] + class TargetUnsupportedOnWindows + { + [SupportedOSPlatform(""windows"")] + public static void FunctionSupportedOnWindows() { } + + [SupportedOSPlatform(""browser"")] + public static void FunctionSupportedOnBrowser() { } + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source); + }*/ [Fact] public async Task UnsupportedMustSuppressSupportedAssemblyAttribute() @@ -873,7 +931,7 @@ static class Program public static void Main() { [|CrossPlatformApis.DoesNotWorkOnBrowser()|]; - var nonBrowser = new [|NonBrowserApis()|]; + var nonBrowser = [|new NonBrowserApis()|]; } } @@ -914,7 +972,7 @@ public static void Api() { } } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source); - }*/ + } [Fact] public async Task UsingVersionedApiFromUnversionedAssembly() From 1cf9990cc32e2f535bf53655e35a2bd693a75810 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 17 Aug 2020 11:41:40 -0700 Subject: [PATCH 35/48] Update messages, add named arguments support --- .../PlatformCompatabilityAnalyzer.Value.cs | 7 +-- .../PlatformCompatabilityAnalyzer.cs | 23 ++++----- .../MicrosoftNetCoreAnalyzersResources.resx | 10 ++-- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 20 ++++---- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 20 ++++---- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 20 ++++---- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 20 ++++---- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 20 ++++---- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 20 ++++---- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 20 ++++---- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 20 ++++---- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 20 ++++---- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 20 ++++---- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 20 ++++---- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 20 ++++---- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 20 ++++---- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 50 ++++++++++++++++++- .../PlatformCompatabilityAnalyzerTests.cs | 44 ++++++++-------- src/Utilities/Compiler/SmallDictionary.cs | 1 + 19 files changed, 221 insertions(+), 174 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs index 1dda5b3beb..bfe253b5ca 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -12,6 +12,7 @@ using System.Diagnostics.CodeAnalysis; using Analyzer.Utilities; using System.Diagnostics; +using Analyzer.Utilities.Extensions; namespace Microsoft.NetCore.Analyzers.InteropServices { @@ -72,7 +73,7 @@ public static bool TryDecode( return true; } - if (arguments[0].Value is ILiteralOperation literal) + if (arguments.GetArgumentForParameterAtIndex(0).Value is ILiteralOperation literal) { if (literal.Type?.SpecialType == SpecialType.System_String && literal.ConstantValue.HasValue) @@ -164,9 +165,9 @@ private static bool TryDecodeOSVersion( using var versionBuilder = ArrayBuilder.GetInstance(4, fillWithValue: 0); var index = 0; - foreach (var argument in arguments.Skip(skip)) + while (index < arguments.Length - skip) { - if (!TryDecodeOSVersionPart(argument, valueContentAnalysisResult, out var osVersionPart)) + if (!TryDecodeOSVersionPart(arguments.GetArgumentForParameterAtIndex(index + skip), valueContentAnalysisResult, out var osVersionPart)) { osVersion = null; return false; diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 543023d3ae..203768e408 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -581,16 +581,12 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary attribute.Obsoleted || attribute.SupportedFirst > attribute.Obsoleted) - { - // Can supported version be greater than obsoleted? Do we want to report diagnostic here for wrong version? - } - else if (!SuppresedByUnsupported(callSiteAttribute, attribute.Obsoleted) && !ObsoletedSuppressed(callSiteAttribute.Obsoleted, attribute.Obsoleted)) - { - diagnosticAttribute.Obsoleted = (Version)attribute.Obsoleted.Clone(); - } + diagnosticAttribute.Obsoleted = (Version)attribute.Obsoleted.Clone(); } } } @@ -629,7 +625,7 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary + attribute.SupportedSecond != null && attribute.SupportedSecond < attribute.Obsoleted || attribute.SupportedFirst < attribute.Obsoleted; + private static void AddOrUpdatedDiagnostic(PlatformAttributes operationAttributes, SmallDictionary notSuppressedAttributes, string name) { if (operationAttributes.SupportedFirst != null) @@ -746,7 +745,7 @@ private static bool SuppressedByCallSiteUnsupported(PlatformAttributes callSiteA callSiteAttribute.UnsupportedFirst != null && unsupporteAttribute >= callSiteAttribute.UnsupportedFirst || callSiteAttribute.UnsupportedSecond != null && unsupporteAttribute >= callSiteAttribute.UnsupportedSecond; - private static bool ObsoletedSuppressed(Version? callSiteObsoleted, Version checkingObsoleted) => + private static bool ObsoletedSuppressedByCallSite(Version? callSiteObsoleted, Version checkingObsoleted) => callSiteObsoleted != null && checkingObsoleted >= callSiteObsoleted; private static bool UnsupportedSecondSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) => diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 4da01e1fd4..c979ac3350 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1432,13 +1432,13 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later - '{0}' is not supported or has been removed since '{1}' {2} + '{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. @@ -1468,9 +1468,9 @@ '{0}' will throw for assemblies embedded in a single-file app - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' - '{0}' requires '{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 e480ed2a83..a82ba3568d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later 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 99e996ca4f..8284301cbe 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later 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 3080fc73ab..e2036334a9 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later 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 0a2aecc62d..0871684739 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later 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 26ff0e9170..9ba4190ac5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later 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 e6ace06328..5594c16719 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later 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 936ddb6174..67800e935e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later 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 be36aad2a1..1063313c4d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1519,8 +1519,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1529,23 +1529,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later 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 5137614818..cfaf2e3d9f 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 @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later 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 edcb29e929..21e4ed2332 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later 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 7f15d6ec16..e6026c0380 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later 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 0fc0107c69..bd1343ebeb 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 @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later 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 6deeb50d89..e29ed522b7 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 @@ -1518,8 +1518,8 @@ - '{0}' has been deprecated since '{1}' {2} - '{0}' has been deprecated since '{1}' {2} + '{0}' is deprecated on '{1}' {2} and later + '{0}' is deprecated on '{1}' {2} and later @@ -1528,23 +1528,23 @@ - '{0}' is not supported or has been removed from '{1}' - '{0}' is not supported or has been removed from '{1}' + '{0}' is unsupported on '{1}' + '{0}' is unsupported on '{1}' - '{0}' is not supported or has been removed since '{1}' {2} - '{0}' is not supported or has been removed since '{1}' {2} + '{0}' is unsupported on '{1}' {2} and later + '{0}' is unsupported on '{1}' {2} and later - '{0}' requires '{1}' - '{0}' requires '{1}' + '{0}' is supported on '{1}' + '{0}' is supported on '{1}' - '{0}' requires '{1}' {2} or later - '{0}' requires '{1}' {2} or later + '{0}' is supported on '{1}' {2} and later + '{0}' is supported on '{1}' {2} and later diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index f47e3da308..d31631a6fe 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -12,6 +12,52 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests { public partial class PlatformCompatabilityAnalyzerTests { + public static IEnumerable NamedArgumentsData() + { + yield return new object[] { "minor : 2, major : 12" }; + yield return new object[] { "build : 2, major : 12, revision : 556" }; + yield return new object[] { "build : 1, minor : 1, major : 12" }; + yield return new object[] { "revision : 555, major : 12, build : 2" }; + yield return new object[] { "major : 13, build : 3" }; + } + + [Theory] + [MemberData(nameof(NamedArgumentsData))] + public async Task GuardMethodWithNamedArgumentsTest(string arguments) + { + var source = @" +using System.Runtime.Versioning; +using System; + +class Test +{ + public void Api_Usage() + { + if (OperatingSystemHelper.IsAndroidVersionAtLeast(" + arguments + @")) + { + Api(); + } + [|Api()|]; + + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(" + arguments + @", platform : ""Android"")) + { + Api(); + } + else + { + [|Api()|]; + } + } + + [SupportedOSPlatform(""Android12.0.2.521"")] + void Api() + { + } +}" + MockAttributesCsSource + MockOperatingSystemApiSource; + + await VerifyAnalyzerAsyncCs(source); + } + [Fact(Skip = "TODO: Needs to be fixed")] public async Task SupportedUnsupportedRange_GuardedWithOr() { @@ -70,7 +116,7 @@ void Api() await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(14, 9) - .WithMessage("'Api' is not supported or has been removed since 'ios' 14.0")); + .WithMessage("'Api' is unsupported on 'ios' 14.0 and later")); } [Fact] @@ -445,7 +491,7 @@ public static void WindowsSpecificApi() await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(16, 13) - .WithMessage("'WindowsSpecificApi' requires 'windows' 10.0 or later")); + .WithMessage("'WindowsSpecificApi' is supported on 'windows' 10.0 and later")); } [Fact] diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index eaf6fbfb8c..f98259b4b7 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -826,9 +826,9 @@ public static void UnsupportedOnWindowsAndBrowser() { } } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(17, 13) - .WithMessage("'UnsupportedOnWindowsAndBrowser' is not supported or has been removed from 'windows'").WithArguments("UnsupportedOnWindowsAndBrowser", "windows"), + .WithMessage("'UnsupportedOnWindowsAndBrowser' is unsupported on 'windows'").WithArguments("UnsupportedOnWindowsAndBrowser", "windows"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(24, 13). - WithMessage("'SupportedOnWindowsAndBrowser' requires 'windows'").WithArguments("SupportedOnWindowsAndBrowser", "windows")); + WithMessage("'SupportedOnWindowsAndBrowser' is supported on 'windows'").WithArguments("SupportedOnWindowsAndBrowser", "windows")); } [Fact] @@ -857,7 +857,7 @@ public static void Api2() {} } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(8, 9) - .WithMessage("'Api1' requires 'tvos' 4.0 or later").WithArguments("UnsupportedOnWindowsAndBrowser", "windows")); + .WithMessage("'Api1' is supported on 'tvos' 4.0 and later").WithArguments("UnsupportedOnWindowsAndBrowser", "windows")); } /*[Fact] @@ -1247,9 +1247,9 @@ public static void LinuxMethod() " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(11, 9) - .WithMessage("'LinuxMethod' is not supported or has been removed from 'linux'").WithArguments("LinuxMethod", "linux"), + .WithMessage("'LinuxMethod' is unsupported on 'linux'").WithArguments("LinuxMethod", "linux"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(12, 9) - .WithMessage("'LinuxVersionedMethod' is not supported or has been removed since 'linux' 4.8").WithArguments("LinuxMethod", "linux")); + .WithMessage("'LinuxVersionedMethod' is unsupported on 'linux' 4.8 and later").WithArguments("LinuxMethod", "linux")); } @@ -1319,14 +1319,14 @@ public void DoesNotWorkOnWindows() } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed from 'windows'"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' requires 'windows' 10.0.1903 or later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed from 'windows'"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' requires 'windows' 10.0.1903 or later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since 'windows' 10.0.2004")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' is supported on 'windows' 10.0.1903 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is supported on 'windows' 10.0.1903 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows' 10.0.2004 and later")); } [Fact] @@ -1394,15 +1394,15 @@ public void DoesNotWorkOnWindows() } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' requires 'windows'"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since 'windows' 10.0.2004"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' requires 'windows'"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since 'windows' 10.0.2004"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' has been deprecated since 'windows' 10.0.1909"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is not supported or has been removed since 'windows' 10.0.2004")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' is supported on 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows' 10.0.2004 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is supported on 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows' 10.0.2004 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows' 10.0.2004 and later")); } private static VerifyCS.Test PopulateTestCs(string sourceCode, params DiagnosticResult[] expected) diff --git a/src/Utilities/Compiler/SmallDictionary.cs b/src/Utilities/Compiler/SmallDictionary.cs index c1067ba76c..32b0d5aff9 100644 --- a/src/Utilities/Compiler/SmallDictionary.cs +++ b/src/Utilities/Compiler/SmallDictionary.cs @@ -9,6 +9,7 @@ namespace Analyzer.Utilities { /// + /// Copied from https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/Collections/SmallDictionary.cs /// Dictionary designed to hold small number of items. /// Compared to the regular Dictionary, average overhead per-item is roughly the same, but /// unlike regular dictionary, this one is based on an AVL tree and as such does not require From 56acd8a7753a1968b6a374c55c0374315acbffae Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 17 Aug 2020 16:00:17 -0700 Subject: [PATCH 36/48] Fix an override scenario, update named argument logic --- .../PlatformCompatabilityAnalyzer.Value.cs | 4 +- .../PlatformCompatabilityAnalyzer.cs | 10 +- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 9 +- .../PlatformCompatabilityAnalyzerTests.cs | 169 +++++++++++++++--- .../Extensions/IOperationExtensions.cs | 13 ++ 5 files changed, 167 insertions(+), 38 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs index bfe253b5ca..2c11177fea 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -165,9 +165,9 @@ private static bool TryDecodeOSVersion( using var versionBuilder = ArrayBuilder.GetInstance(4, fillWithValue: 0); var index = 0; - while (index < arguments.Length - skip) + foreach (var argument in arguments.GetArgumentsInNaturalOrder().Skip(skip)) { - if (!TryDecodeOSVersionPart(arguments.GetArgumentForParameterAtIndex(index + skip), valueContentAnalysisResult, out var osVersionPart)) + if (!TryDecodeOSVersionPart(argument, valueContentAnalysisResult, out var osVersionPart)) { osVersion = null; return false; diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 203768e408..579341248a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -631,7 +631,15 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary ca.Value.UnsupportedFirst != null && + (ca.Value.SupportedFirst == null || ca.Value.UnsupportedFirst <= ca.Value.SupportedFirst))) + { + diagnosticAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone(); + } + } } } else diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index d31631a6fe..63756fd149 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -14,11 +14,11 @@ public partial class PlatformCompatabilityAnalyzerTests { public static IEnumerable NamedArgumentsData() { - yield return new object[] { "minor : 2, major : 12" }; + //yield return new object[] { "minor : 2, major : 12" }; yield return new object[] { "build : 2, major : 12, revision : 556" }; - yield return new object[] { "build : 1, minor : 1, major : 12" }; + /*yield return new object[] { "build : 1, minor : 1, major : 12" }; yield return new object[] { "revision : 555, major : 12, build : 2" }; - yield return new object[] { "major : 13, build : 3" }; + yield return new object[] { "major : 13, build : 3" };*/ } [Theory] @@ -159,9 +159,6 @@ public static void UnsupportedInWindows10() { } " + MockAttributesCsSource + MockOperatingSystemApiSource; await VerifyAnalyzerAsyncCs(source); - /*await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(14, 9) - .WithMessage("'Api' is not supported or has been removed since 'ios' 14.0"));*/ } [Fact] diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index f98259b4b7..1af70ee969 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -769,6 +769,120 @@ public static void SupportedOnWindowsAndBrowser() { } await VerifyAnalyzerAsyncCs(source); } + [Fact] + public async Task CallerUnsupportsNonSubsetOfTargetSupport() + { + var source = @" +using System.Runtime.Versioning; + +namespace CallerUnsupportsNonSubsetOfTarget +{ + class Caller + { + [UnsupportedOSPlatform(""browser"")] + public static void TestWithBrowserUnsupported() + { + [|Target.UnsupportedOnWindowsUntilWindows11()|]; + } + } + class Target + { + [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0"")] + public static void UnsupportedOnWindowsUntilWindows11() { } + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source); + } + + [Fact(Skip = "Is this valid scenario? Check again")] + public async Task CallerUnsupportsSubsetOfTargetSupport() + { + var source = @" +using System.Runtime.Versioning; + +namespace CallerUnsupportsNonSubsetOfTarget +{ + class Caller + { + [UnsupportedOSPlatform(""windows"")] + public void TestUnsupportedOnWindows() + { + [|TargetUnsupportedOnWindows.FunctionSupportedOnWindows1()|]; // should warn supported on windows 1.0 + [|TargetUnsupportedOnWindows.FunctionSupportedOnWindowsSameVersion()|]; + } + } + [UnsupportedOSPlatform(""windows"")] + class TargetUnsupportedOnWindows + { + [SupportedOSPlatform(""windows1.0"")] + public static void FunctionSupportedOnWindows1() { } + + [SupportedOSPlatform(""windows"")] // this would cause conflict, need to have less version than unsupported + public static void FunctionSupportedOnWindowsSameVersion() { } + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task CallerSupportsSupersetOfTarget_AnotherScenario() + { + var source = @" +using System.Runtime.Versioning; + +namespace CallerSupportsSubsetOfTarget +{ + class Caller + { + [UnsupportedOSPlatform(""browser"")] + public static void TestWithBrowserUnsupported() + { + [|Target.SupportedOnWindows()|]; + [|Target.SupportedOnBrowser()|]; + [|Target.SupportedOnWindowsAndBrowser()|]; // two diagnostics expected + + [|Target.UnsupportedOnWindows()|]; + [|Target.UnsupportedOnWindows11()|]; + Target.UnsupportedOnBrowser(); + [|Target.UnsupportedOnWindowsAndBrowser()|]; + [|Target.UnsupportedOnWindowsUntilWindows11()|]; + } + } + + class Target + { + [SupportedOSPlatform(""windows"")] + public static void SupportedOnWindows() { } + + [SupportedOSPlatform(""browser"")] + public static void SupportedOnBrowser() { } + + [SupportedOSPlatform(""windows""), SupportedOSPlatform(""browser"")] + public static void SupportedOnWindowsAndBrowser() { } + + [UnsupportedOSPlatform(""windows"")] + public static void UnsupportedOnWindows() { } + + [UnsupportedOSPlatform(""windows11.0"")] + public static void UnsupportedOnWindows11() { } + + [UnsupportedOSPlatform(""browser"")] + public static void UnsupportedOnBrowser() { } + + [UnsupportedOSPlatform(""windows""), UnsupportedOSPlatform(""browser"")] + public static void UnsupportedOnWindowsAndBrowser() { } + + [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0"")] + public static void UnsupportedOnWindowsUntilWindows11() { } + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(13, 13) + .WithMessage("'SupportedOnWindowsAndBrowser' is supported on 'browser'").WithArguments("SupportedOnWindowsAndBrowser", "browser")); + } + [Fact] public async Task CallerSupportsSupersetOfTarget() { @@ -860,7 +974,7 @@ await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAna .WithMessage("'Api1' is supported on 'tvos' 4.0 and later").WithArguments("UnsupportedOnWindowsAndBrowser", "windows")); } - /*[Fact] + [Fact(Skip = "One failing, is this valid scenario?")] public async Task OverridesPlatform() { var source = @" @@ -873,23 +987,20 @@ class Caller [SupportedOSPlatform(""windows"")] public void TestSupportedOnWindows() { - [|TargetSupportedOnWindows.FunctionUnsupportedOnWindows()|]; // 11 FAIL: should be unsupported on windows + [|TargetSupportedOnWindows.FunctionUnsupportedOnWindows()|]; // should warn for unsupported windows TargetSupportedOnWindows.FunctionUnsupportedOnBrowser(); TargetUnsupportedOnWindows.FunctionSupportedOnWindows(); - [|TargetUnsupportedOnWindows.FunctionSupportedOnBrowser()|]; // 15 FAIL: should only be supported on browser - } // - Still should warn unsupported windows + [|TargetUnsupportedOnWindows.FunctionSupportedOnBrowser()|]; // should warn for unsupported windows + } [UnsupportedOSPlatform(""windows"")] public void TestUnsupportedOnWindows() { TargetSupportedOnWindows.FunctionUnsupportedOnWindows(); - [|TargetSupportedOnWindows.FunctionUnsupportedOnBrowser()|]; // 22 FAIL: being unsupported on browser doesn't matter - // because it should only be supported on windows - // (since an allow-list was found) - No this doesn't have any supported info means for all means browser should warn + [|TargetSupportedOnWindows.FunctionUnsupportedOnBrowser()|]; // should warn supported on windows and unsupported on browser as this is deny list - [|TargetUnsupportedOnWindows.FunctionSupportedOnWindows()|]; // 26 FAIL: should only be supported on windows - [|TargetUnsupportedOnWindows.FunctionSupportedOnBrowser()|]; // FAIL: should only be supported on browser + [|TargetUnsupportedOnWindows.FunctionSupportedOnBrowser();|] // Fail: should warn supported on browser, but is this valid scenario? } } @@ -914,8 +1025,9 @@ public static void FunctionSupportedOnBrowser() { } } } " + MockAttributesCsSource; - await VerifyAnalyzerAsyncCs(source); - }*/ + await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(22, 13) + .WithMessage("FunctionUnsupportedOnBrowser' is supported on 'windows'").WithArguments("FunctionUnsupportedOnBrowser", "windows")); + } [Fact] public async Task UnsupportedMustSuppressSupportedAssemblyAttribute() @@ -1342,7 +1454,7 @@ public class Test [SupportedOSPlatform(""Linux"")] public void DiffferentOsWarnsForAll() { - obj.DoesNotWorkOnWindows(); + obj.WindowsOnlyMethod(); } [SupportedOSPlatform(""windows"")] @@ -1350,28 +1462,27 @@ public void DiffferentOsWarnsForAll() public void SupporteWindows() { // Warns for Obsoleted version - obj.DoesNotWorkOnWindows(); + obj.WindowsOnlyMethod(); } - [UnsupportedOSPlatform(""windows"")] - [SupportedOSPlatform(""windows10.1"")] + [SupportedOSPlatform(""windows"")] [ObsoletedInOSPlatform(""windows10.0.1909"")] [UnsupportedOSPlatform(""windows10.0.2003"")] public void SameSupportForWindowsNotWarn() { - obj.DoesNotWorkOnWindows(); + obj.WindowsOnlyMethod(); } public void AllSupportedWarnForAll() { - obj.DoesNotWorkOnWindows(); + obj.WindowsOnlyMethod(); } [SupportedOSPlatform(""windows10.0.2000"")] public void SupportedFromWindows10_0_2000() { // Warns for [ObsoletedInOSPlatform] and [UnsupportedOSPlatform] - obj.DoesNotWorkOnWindows(); + obj.WindowsOnlyMethod(); } [SupportedOSPlatform(""windows10.0.1904"")] @@ -1379,7 +1490,7 @@ public void SupportedFromWindows10_0_2000() public void SupportedWindowsFrom10_0_1904_To10_0_1909_NotWarn() { // Should not warn - obj.DoesNotWorkOnWindows(); + obj.WindowsOnlyMethod(); } } @@ -1388,21 +1499,21 @@ public class C [SupportedOSPlatform(""windows"")] [ObsoletedInOSPlatform(""windows10.0.1909"")] [UnsupportedOSPlatform(""windows10.0.2004"")] - public void DoesNotWorkOnWindows() + public void WindowsOnlyMethod() { } } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' is supported on 'windows'"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows' 10.0.2004 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is supported on 'windows'"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows' 10.0.2004 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows' 10.0.2004 and later")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(10, 9).WithMessage("'WindowsOnlyMethod' is supported on 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'WindowsOnlyMethod' is deprecated on 'windows' 10.0.1909 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'WindowsOnlyMethod' is unsupported on 'windows' 10.0.2004 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'WindowsOnlyMethod' is deprecated on 'windows' 10.0.1909 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(31, 9).WithMessage("'WindowsOnlyMethod' is supported on 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(31, 9).WithMessage("'WindowsOnlyMethod' is deprecated on 'windows' 10.0.1909 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(31, 9).WithMessage("'WindowsOnlyMethod' is unsupported on 'windows' 10.0.2004 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(38, 9).WithMessage("'WindowsOnlyMethod' is deprecated on 'windows' 10.0.1909 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(38, 9).WithMessage("'WindowsOnlyMethod' is unsupported on 'windows' 10.0.2004 and later")); } private static VerifyCS.Test PopulateTestCs(string sourceCode, params DiagnosticResult[] expected) diff --git a/src/Utilities/Compiler/Extensions/IOperationExtensions.cs b/src/Utilities/Compiler/Extensions/IOperationExtensions.cs index bf21db4124..2b3426e1f8 100644 --- a/src/Utilities/Compiler/Extensions/IOperationExtensions.cs +++ b/src/Utilities/Compiler/Extensions/IOperationExtensions.cs @@ -803,6 +803,19 @@ public static IArgumentOperation GetArgumentForParameterAtIndex( throw new InvalidOperationException(); } + + public static ImmutableArray GetArgumentsInNaturalOrder( + this ImmutableArray arguments) + { + using var naturalOderedArray = ArrayBuilder.GetInstance(arguments.Length, null!); + + foreach (var argument in arguments) + { + naturalOderedArray[argument.Parameter.Ordinal] = argument; + } + + return naturalOderedArray.ToImmutableArray(); + } } } From 7b388a18fc59a041202d29904a8be88eb328e29a Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 17 Aug 2020 16:30:02 -0700 Subject: [PATCH 37/48] Apply feedback --- .../PlatformCompatabilityAnalyzer.Value.cs | 2 +- ...CompatabilityAnalyzerTests.GuardedCallsTests.cs | 6 +++--- .../Compiler/Extensions/IOperationExtensions.cs | 14 ++++++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs index 2c11177fea..5b2d38eaa1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -165,7 +165,7 @@ private static bool TryDecodeOSVersion( using var versionBuilder = ArrayBuilder.GetInstance(4, fillWithValue: 0); var index = 0; - foreach (var argument in arguments.GetArgumentsInNaturalOrder().Skip(skip)) + foreach (var argument in arguments.GetArgumentsInParameterOrder().Skip(skip)) { if (!TryDecodeOSVersionPart(argument, valueContentAnalysisResult, out var osVersionPart)) { diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index 63756fd149..cce8be1ee9 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -14,11 +14,11 @@ public partial class PlatformCompatabilityAnalyzerTests { public static IEnumerable NamedArgumentsData() { - //yield return new object[] { "minor : 2, major : 12" }; + yield return new object[] { "minor : 2, major : 12" }; yield return new object[] { "build : 2, major : 12, revision : 556" }; - /*yield return new object[] { "build : 1, minor : 1, major : 12" }; + yield return new object[] { "build : 1, minor : 1, major : 12" }; yield return new object[] { "revision : 555, major : 12, build : 2" }; - yield return new object[] { "major : 13, build : 3" };*/ + yield return new object[] { "major : 13, build : 3" }; } [Theory] diff --git a/src/Utilities/Compiler/Extensions/IOperationExtensions.cs b/src/Utilities/Compiler/Extensions/IOperationExtensions.cs index 2b3426e1f8..2dd94d4288 100644 --- a/src/Utilities/Compiler/Extensions/IOperationExtensions.cs +++ b/src/Utilities/Compiler/Extensions/IOperationExtensions.cs @@ -804,17 +804,23 @@ public static IArgumentOperation GetArgumentForParameterAtIndex( throw new InvalidOperationException(); } - public static ImmutableArray GetArgumentsInNaturalOrder( + /// + /// Useful when named arguments used for a method call and you need them in the original parameter order. + /// + /// Arguments of the method + /// Returns the arguments in parameter order + public static ImmutableArray GetArgumentsInParameterOrder( this ImmutableArray arguments) { - using var naturalOderedArray = ArrayBuilder.GetInstance(arguments.Length, null!); + using var parameterOrderedArguments = ArrayBuilder.GetInstance(arguments.Length, null!); foreach (var argument in arguments) { - naturalOderedArray[argument.Parameter.Ordinal] = argument; + Debug.Assert(parameterOrderedArguments[argument.Parameter.Ordinal] == null); + parameterOrderedArguments[argument.Parameter.Ordinal] = argument; } - return naturalOderedArray.ToImmutableArray(); + return parameterOrderedArguments.ToImmutableArray(); } } } From 04b6a9536b16fb2aa1c613a6067bdf7ec7d660c4 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 17 Aug 2020 19:28:00 -0700 Subject: [PATCH 38/48] Remove ObsoletedOsPlatform attribute --- .../PlatformCompatabilityAnalyzer.Data.cs | 3 +- .../PlatformCompatabilityAnalyzer.cs | 96 +------- .../MicrosoftNetCoreAnalyzersResources.resx | 7 +- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 9 +- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 9 +- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 9 +- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 9 +- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 9 +- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 9 +- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 9 +- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 9 +- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 9 +- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 9 +- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 9 +- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 9 +- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 9 +- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 15 +- .../PlatformCompatabilityAnalyzerTests.cs | 220 +++++------------- 18 files changed, 103 insertions(+), 355 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index 6b4c6b2b98..86eb780ebc 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -28,13 +28,12 @@ public sealed partial class PlatformCompatabilityAnalyzer /// private class PlatformAttributes { - public Version? Obsoleted { get; set; } 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 || Obsoleted != null; + SupportedSecond != null || UnsupportedSecond != null; } private static bool TryParsePlatformNameAndVersion(string osString, out string osPlatformName, [NotNullWhen(true)] out Version? version) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 579341248a..12ac0f502b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -18,7 +18,7 @@ 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 obsoleted or unsupported or removed APIs and diagnoses if the + /// 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. /// @@ -29,12 +29,11 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer internal const string RuleId = "CA1416"; private static readonly ImmutableArray s_platformCheckMethodNames = ImmutableArray.Create(IsOSPlatformVersionAtLeast, IsOSPlatform, IsBrowser, IsLinux, IsFreeBSD, IsFreeBSDVersionAtLeast, IsAndroid, IsAndroidVersionAtLeast, IsIOS, IsIOSVersionAtLeast, IsMacOS, IsMacOSVersionAtLeast, IsTvOS, IsTvOSVersionAtLeast, IsWatchOS, IsWatchOSVersionAtLeast, IsWindows, IsWindowsVersionAtLeast); - private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(SupportedOSPlatformAttribute, ObsoletedInOSPlatformAttribute, UnsupportedOSPlatformAttribute); + 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_localizableRequiredOsMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckRequiredOsMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); - private static readonly LocalizableString s_localizableRequiredOsVersionMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatibilityCheckRequiredOsVersionMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); - private static readonly LocalizableString s_localizableObsoleteMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.PlatformCompatabilityCheckObsoleteMessage), 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)); @@ -42,7 +41,6 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer // 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 ObsoletedInOSPlatformAttribute = nameof(ObsoletedInOSPlatformAttribute); private const string UnsupportedOSPlatformAttribute = nameof(UnsupportedOSPlatformAttribute); // Platform guard method names @@ -67,7 +65,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer internal static DiagnosticDescriptor RequiredOsVersionRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, - s_localizableRequiredOsVersionMessage, + s_localizableSupportedOsVersionMessage, DiagnosticCategory.Interoperability, RuleLevel.BuildWarning, description: s_localizableDescription, @@ -76,16 +74,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer internal static DiagnosticDescriptor RequiredOsRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, - s_localizableRequiredOsMessage, - DiagnosticCategory.Interoperability, - RuleLevel.BuildWarning, - description: s_localizableDescription, - isPortedFxCopRule: false, - isDataflowRule: false); - - internal static DiagnosticDescriptor ObsoleteOsRule = DiagnosticDescriptorHelper.Create(RuleId, - s_localizableTitle, - s_localizableObsoleteMessage, + s_localizableSupportedOsMessage, DiagnosticCategory.Interoperability, RuleLevel.BuildWarning, description: s_localizableDescription, @@ -109,7 +98,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer description: s_localizableDescription, isPortedFxCopRule: false, isDataflowRule: false); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(RequiredOsRule, RequiredOsVersionRule, ObsoleteOsRule, UnsupportedOsRule, UnsupportedOsVersionRule); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(RequiredOsRule, RequiredOsVersionRule, UnsupportedOsRule, UnsupportedOsVersionRule); public override void Initialize(AnalysisContext context) { @@ -309,11 +298,6 @@ static bool IsKnownValueGuarded( } } - if (attribute.Obsoleted != null && capturedPlatforms.Contains(info.PlatformName) && attribute.Obsoleted <= info.Version) - { - attribute.Obsoleted = null; - } - if (attribute.UnsupportedSecond != null) { if (capturedPlatforms.Contains(info.PlatformName)) @@ -345,11 +329,6 @@ static bool IsKnownValueGuarded( attribute.UnsupportedFirst = null; } - if (attribute.Obsoleted != null && capturedVersions.TryGetValue(info.PlatformName, out version) && attribute.Obsoleted <= version) - { - attribute.Obsoleted = null; - } - if (attribute.UnsupportedSecond != null && capturedVersions.TryGetValue(info.PlatformName, out version) && attribute.UnsupportedSecond <= version) { attribute.UnsupportedSecond = null; @@ -456,11 +435,6 @@ private static void ReportDiagnostics(IOperation operation, SmallDictionary CopyOperationAttribut /// - 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. - /// - [ObsoletedInOSPlatform] continuous to require a version number. - /// - [ObsoletedInOSPlatform] by itself doesn't imply support. However, it doesn't make sense to apply [ObsoletedInOSPlatform] unless that platform is supported. /// /// Platform specific attributes applied to the invoked member /// Platform specific attributes applied to the call site where the member invoked @@ -580,14 +552,6 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary attribute.Obsoleted || attribute.SupportedFirst > attribute.Obsoleted) - { - // Can supported version be greater than obsoleted? Do we want to report diagnostic here for wrong version? - } - else if (!SuppresedByUnsupported(callSiteAttribute, attribute.Obsoleted) && !ObsoletedSuppressedByCallSite(callSiteAttribute.Obsoleted, attribute.Obsoleted)) - { - diagnosticAttribute.Obsoleted = (Version)attribute.Obsoleted.Clone(); - } - } } else { @@ -673,11 +625,6 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary - attribute.SupportedSecond != null && attribute.SupportedSecond < attribute.Obsoleted || attribute.SupportedFirst < attribute.Obsoleted; - private static void AddOrUpdatedDiagnostic(PlatformAttributes operationAttributes, SmallDictionary notSuppressedAttributes, string name) { if (operationAttributes.SupportedFirst != null) @@ -735,17 +679,12 @@ private static void AddOrUpdatedDiagnostic(PlatformAttributes operationAttribute } } - private static bool SuppresedByUnsupported(PlatformAttributes callSiteAttribute, Version obsoleted) => - callSiteAttribute.UnsupportedFirst != null && callSiteAttribute.UnsupportedFirst <= obsoleted || - callSiteAttribute.UnsupportedSecond != null && callSiteAttribute.UnsupportedSecond <= obsoleted; - 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(); - copyTo.Obsoleted = (Version?)copyFrom.Obsoleted?.Clone(); return copyTo; } @@ -753,9 +692,6 @@ private static bool SuppressedByCallSiteUnsupported(PlatformAttributes callSiteA callSiteAttribute.UnsupportedFirst != null && unsupporteAttribute >= callSiteAttribute.UnsupportedFirst || callSiteAttribute.UnsupportedSecond != null && unsupporteAttribute >= callSiteAttribute.UnsupportedSecond; - private static bool ObsoletedSuppressedByCallSite(Version? callSiteObsoleted, Version checkingObsoleted) => - callSiteObsoleted != null && checkingObsoleted >= callSiteObsoleted; - private static bool UnsupportedSecondSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) => SuppressedByCallSiteSupported(attribute, callSiteAttribute.SupportedFirst) || SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedSecond!); @@ -857,9 +793,6 @@ private static void AddAttribute(string name, Version version, SmallDictionary version) - { - attributes.Obsoleted = version; - } - } - else - { - attributes.Obsoleted = version; - } - } - private static void AddOrUpdateUnsupportedAttribute(string name, PlatformAttributes attributes, Version version, SmallDictionary existingAttributes) { if (attributes.UnsupportedFirst != null) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index c979ac3350..a6efd42724 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1431,12 +1431,9 @@ 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 deprecated on '{1}' {2} and later - '{0}' is unsupported on '{1}' {2} and later @@ -1470,7 +1467,7 @@ '{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 a82ba3568d..56fb34d8b3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1517,11 +1517,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1537,12 +1532,12 @@ '{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 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 8284301cbe..c464217234 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1517,11 +1517,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1537,12 +1532,12 @@ '{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 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 e2036334a9..08df5ed93a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1517,11 +1517,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1537,12 +1532,12 @@ '{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 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 0871684739..777d6ebdd3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1517,11 +1517,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1537,12 +1532,12 @@ '{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 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 9ba4190ac5..8c59353755 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1517,11 +1517,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1537,12 +1532,12 @@ '{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 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 5594c16719..324bb0ee21 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1517,11 +1517,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1537,12 +1532,12 @@ '{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 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 67800e935e..f9776e88fe 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1517,11 +1517,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1537,12 +1532,12 @@ '{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 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 1063313c4d..19d4a7ed08 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1518,11 +1518,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1538,12 +1533,12 @@ '{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 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 cfaf2e3d9f..05b35361c7 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 @@ -1517,11 +1517,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1537,12 +1532,12 @@ '{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 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 21e4ed2332..e19f6f224d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1517,11 +1517,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1537,12 +1532,12 @@ '{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 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 e6026c0380..97ff45a283 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1517,11 +1517,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1537,12 +1532,12 @@ '{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 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 bd1343ebeb..6c048d7699 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 @@ -1517,11 +1517,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1537,12 +1532,12 @@ '{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 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 e29ed522b7..f551288d71 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 @@ -1517,11 +1517,6 @@ Using platform dependent API on a component makes the code no longer work across all platforms. - - '{0}' is deprecated on '{1}' {2} and later - '{0}' is deprecated on '{1}' {2} and later - - Call of platform dependent API Call of platform dependent API @@ -1537,12 +1532,12 @@ '{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 diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index cce8be1ee9..7113fd72ce 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -621,8 +621,7 @@ void M2() { } - [SupportedOSPlatform(""Windows"")] - [ObsoletedInOSPlatform(""Windows10.0"")] + [UnsupportedOSPlatform(""Windows10.0"")] void M3 () { } @@ -631,7 +630,7 @@ void M3 () } [Fact] - public async Task GuardedWith_Obsoleted_SimpleIfElse() + public async Task GuardedWith_Unsupported_SimpleIfElse() { var source = @" using System.Runtime.Versioning; @@ -642,7 +641,7 @@ class Test [SupportedOSPlatform(""Windows"")] void M1() { - if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10, 2, 19222)) + if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10, 1, 2, 3)) { [|M2()|]; M3(); @@ -669,8 +668,7 @@ void M1() void M2() { } - [SupportedOSPlatform(""Windows"")] - [ObsoletedInOSPlatform(""Windows10.1.2.3"")] + [UnsupportedOSPlatform(""Windows10.1.2.3"")] void M3 () { } @@ -684,7 +682,7 @@ Imports System Class Test Private Sub M1() - If OperatingSystemHelper.IsWindows() AndAlso Not OperatingSystemHelper.IsWindowsVersionAtLeast(10, 2, 19222) Then + If OperatingSystemHelper.IsWindows() AndAlso Not OperatingSystemHelper.IsWindowsVersionAtLeast(10, 1, 2, 3) Then [|M2()|] M3() Else @@ -705,8 +703,7 @@ End Sub Private Sub M2() End Sub - - + Private Sub M3() End Sub End Class diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index 1af70ee969..ca7ec25333 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -30,18 +30,10 @@ public class Test public void MethodWithTwoArguments() { } - [SupportedOSPlatform([|new string[]{""Linux"", ""Windows""}|])] + [UnsupportedOSPlatform([|new string[]{""Linux"", ""Windows""}|])] public void MethodWithArrayArgument() { } - [ObsoletedInOSPlatform([|10|])] - public void MethodWithIntArgument() - { - } - [ObsoletedInOSPlatform([|OSPlatform.Windows|])] - public void MethodWithPropertyAccess() - { - } } " + MockAttributesCsSource; @@ -60,9 +52,7 @@ public class Test public void M1() { [|WindowsOnly()|]; - [|Obsoleted()|]; [|Unsupported()|]; - [|ObsoletedOverload()|]; } [SupportedOSPlatform(""Linux"")] [UnsupportedOSPlatform(""Linux4.1"")] @@ -73,16 +63,6 @@ public void Unsupported() public void WindowsOnly() { } - [SupportedOSPlatform(""Linux"")] - [ObsoletedInOSPlatform(""Linux4.1"")] - public void Obsoleted() - { - } - [SupportedOSPlatform(""Linux"")] - [ObsoletedInOSPlatform(""Linux4.1"", ""Obsolete message"")] - public void ObsoletedOverload() - { - } } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(csSource); @@ -93,8 +73,6 @@ Imports System.Runtime.Versioning Public Class Test Public Sub M1() [|WindowsOnly()|] - [|Obsoleted()|] - [|ObsoletedOverload()|] [|Unsupported()|] End Sub @@ -102,14 +80,6 @@ End Sub Public Sub WindowsOnly() End Sub - - Public Sub Obsoleted() - End Sub - - - Public Sub ObsoletedOverload() - End Sub - Public Sub Unsupported() End Sub @@ -130,10 +100,10 @@ public void M1() { Windows10(); Windows1_2_3_4_5(); - [|ObsoletedLinuxDash4_1()|]; - [|ObsoletedLinuxStar4_1()|]; + [|UnsupportedOSPlatformLinuxDash4_1()|]; + [|UnsupportedOSPlatformLinuxStar4_1()|]; [|UnsupportedLinu4_1()|]; - ObsoletedWithNullString(); + UnsupportedOSPlatformWithNullString(); UnsupportedWithEmptyString(); [|WindowsOnly()|]; } @@ -150,16 +120,16 @@ public void Windows10() public void Windows1_2_3_4_5() { } - [ObsoletedInOSPlatform(""Linux-4.1"")] - public void ObsoletedLinuxDash4_1() + [UnsupportedOSPlatform(""Linux-4.1"")] + public void UnsupportedOSPlatformLinuxDash4_1() { } - [ObsoletedInOSPlatform(""Linux*4.1"")] - public void ObsoletedLinuxStar4_1() + [UnsupportedOSPlatform(""Linux*4.1"")] + public void UnsupportedOSPlatformLinuxStar4_1() { } - [ObsoletedInOSPlatform(null)] - public void ObsoletedWithNullString() + [UnsupportedOSPlatform(null)] + public void UnsupportedOSPlatformWithNullString() { } [UnsupportedOSPlatform(""Linu4.1"")] @@ -185,8 +155,6 @@ public class Test { [SupportedOSPlatform(""Windows10.1.1"")] public string WindowsStringProperty { get; set; } - [ObsoletedInOSPlatform(""ios4.1"")] - public int ObsoleteIntProperty { get; set; } [UnsupportedOSPlatform(""Linux4.1"")] public byte UnsupportedProperty { get; } public void M1() @@ -194,8 +162,6 @@ public void M1() [|WindowsStringProperty|] = ""Hello""; string s = [|WindowsStringProperty|]; M2([|WindowsStringProperty|]); - [|ObsoleteIntProperty|] = 5; - M3([|ObsoleteIntProperty|]); M3([|UnsupportedProperty|]); } public string M2(string option) @@ -242,7 +208,6 @@ public object M2(object option) public static IEnumerable Create_AtrrbiuteProperty_WithCondtions() { yield return new object[] { "SupportedOSPlatform", "string StringProperty", " == [|StringProperty|]", @"StringProperty|] = ""Hello""", "StringProperty" }; - yield return new object[] { "ObsoletedInOSPlatform", "int IntProperty", " > [|IntProperty|]", "IntProperty|] = 5", "IntProperty" }; yield return new object[] { "UnsupportedOSPlatform", "int UnsupportedProperty", " <= [|UnsupportedProperty|]", "UnsupportedProperty|] = 3", "UnsupportedProperty" }; } @@ -795,20 +760,55 @@ public static void UnsupportedOnWindowsUntilWindows11() { } await VerifyAnalyzerAsyncCs(source); } - [Fact(Skip = "Is this valid scenario? Check again")] - public async Task CallerUnsupportsSubsetOfTargetSupport() + [Fact] + public async Task CallerUnsupportsSubsetOfTargetUsupportedFirstThenSupportsNotWarn() { var source = @" using System.Runtime.Versioning; -namespace CallerUnsupportsNonSubsetOfTarget +namespace CallerUnsupportsSubsetOfTarget { class Caller { [UnsupportedOSPlatform(""windows"")] public void TestUnsupportedOnWindows() { - [|TargetUnsupportedOnWindows.FunctionSupportedOnWindows1()|]; // should warn supported on windows 1.0 + // Call site unsupporting Windows, means the call site supports all other platforms + // It is calling into code that was NOT supported only on Windows, but eventually added support, + // as it was only not supported window supporting it later doesn' matter for call site that is + // not supporting windows at all, so it shouldn't raise diagnostic + TargetUnsupportedOnWindows.FunctionSupportedOnWindows1(); // should not warn + TargetUnsupportedOnWindows.FunctionSupportedOnWindowsSameVersion(); + } + } + [UnsupportedOSPlatform(""windows"")] + class TargetUnsupportedOnWindows + { + [SupportedOSPlatform(""windows1.0"")] + public static void FunctionSupportedOnWindows1() { } + + [SupportedOSPlatform(""windows"")] + public static void FunctionSupportedOnWindowsSameVersion() { } + } +} +" + MockAttributesCsSource; + await VerifyAnalyzerAsyncCs(source); + } + + [Fact] + public async Task CallerUnsupportsNonSubsetOfTargetUnsupportedFirstSupportsWarns() + { + var source = @" +using System.Runtime.Versioning; + +namespace CallerUnsupportsNonSubsetOfTarget +{ + class Caller + { + [UnsupportedOSPlatform(""browser"")] + public void TestUnsupportedOnWindows() + { + [|TargetUnsupportedOnWindows.FunctionSupportedOnWindows1()|]; [|TargetUnsupportedOnWindows.FunctionSupportedOnWindowsSameVersion()|]; } } @@ -818,7 +818,7 @@ class TargetUnsupportedOnWindows [SupportedOSPlatform(""windows1.0"")] public static void FunctionSupportedOnWindows1() { } - [SupportedOSPlatform(""windows"")] // this would cause conflict, need to have less version than unsupported + [SupportedOSPlatform(""windows"")] public static void FunctionSupportedOnWindowsSameVersion() { } } } @@ -1226,47 +1226,7 @@ await VerifyAnalyzerAsyncCs(source, } } - [Theory] - [MemberData(nameof(ObsoletedUnsupportedAttributeTestData))] - public async Task MethodOfOsDependentClassSuppressedWithObsoleteAttribute(string dependentVersion, string suppressingVersion, bool warn) - { - var source = @" -using System.Runtime.Versioning; - -[SupportedOSPlatform(""Windows"")] -public class Test -{ - [ObsoletedInOSPlatform(""" + suppressingVersion + @""")] - public void M1() - { - OsDependentClass odc = new OsDependentClass(); - odc.M2(); - } - } - -[SupportedOSPlatform(""Windows"")] -[ObsoletedInOSPlatform(""" + dependentVersion + @""")] -public class OsDependentClass -{ - public void M2() - { - } -} -" + MockAttributesCsSource; - - if (warn) - { - await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(10, 32).WithArguments("OsDependentClass", "Windows", "10.1.2.3"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(11, 9).WithArguments("M2", "Windows", "10.1.2.3")); - } - else - { - await VerifyAnalyzerAsyncCs(source); - } - } - - public static IEnumerable ObsoletedUnsupportedAttributeTestData() + public static IEnumerable UnsupportedAttributeTestData() { yield return new object[] { "Windows10.1.2.3", "Windows10.1.2.3", false }; yield return new object[] { "Windows10.1.2.3", "MacOs10.1.3.3", true }; @@ -1282,7 +1242,7 @@ public static IEnumerable ObsoletedUnsupportedAttributeTestData() } [Theory] - [MemberData(nameof(ObsoletedUnsupportedAttributeTestData))] + [MemberData(nameof(UnsupportedAttributeTestData))] public async Task MethodOfOsDependentClassSuppressedWithUnsupportedAttribute(string dependentVersion, string suppressingVersion, bool warn) { var source = @" @@ -1385,13 +1345,12 @@ public void DiffferentOsNotWarn() [UnsupportedOSPlatform(""windows10.0.2000"")] public void SupporteWindows() { - // Warns for UnsupportedFirst, Supported and Obsoleted + // Warns for UnsupportedFirst, Supported obj.DoesNotWorkOnWindows(); } [UnsupportedOSPlatform(""windows"")] [SupportedOSPlatform(""windows10.1"")] - [ObsoletedInOSPlatform(""windows10.0.1909"")] [UnsupportedOSPlatform(""windows10.0.2003"")] public void SameSupportForWindowsNotWarn() { @@ -1406,7 +1365,7 @@ public void AllSupportedWarnForAll() [SupportedOSPlatform(""windows10.0.2000"")] public void SupportedFromWindows10_0_2000() { - // Should warn for [ObsoletedInOSPlatform] and [UnsupportedOSPlatform] + // Should warn for [UnsupportedOSPlatform] obj.DoesNotWorkOnWindows(); } @@ -1423,7 +1382,6 @@ public class C { [UnsupportedOSPlatform(""windows"")] [SupportedOSPlatform(""windows10.0.1903"")] - [ObsoletedInOSPlatform(""windows10.0.1909"")] [UnsupportedOSPlatform(""windows10.0.2004"")] public void DoesNotWorkOnWindows() { @@ -1433,12 +1391,9 @@ public void DoesNotWorkOnWindows() await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows'"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' is supported on 'windows' 10.0.1903 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows'"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is supported on 'windows' 10.0.1903 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(32, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is deprecated on 'windows' 10.0.1909 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(39, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows' 10.0.2004 and later")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(31, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(31, 9).WithMessage("'DoesNotWorkOnWindows' is supported on 'windows' 10.0.1903 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(38, 9).WithMessage("'DoesNotWorkOnWindows' is unsupported on 'windows' 10.0.2004 and later")); } [Fact] @@ -1458,15 +1413,6 @@ public void DiffferentOsWarnsForAll() } [SupportedOSPlatform(""windows"")] - [UnsupportedOSPlatform(""windows10.0.2000"")] - public void SupporteWindows() - { - // Warns for Obsoleted version - obj.WindowsOnlyMethod(); - } - - [SupportedOSPlatform(""windows"")] - [ObsoletedInOSPlatform(""windows10.0.1909"")] [UnsupportedOSPlatform(""windows10.0.2003"")] public void SameSupportForWindowsNotWarn() { @@ -1481,7 +1427,7 @@ public void AllSupportedWarnForAll() [SupportedOSPlatform(""windows10.0.2000"")] public void SupportedFromWindows10_0_2000() { - // Warns for [ObsoletedInOSPlatform] and [UnsupportedOSPlatform] + // Warns for [UnsupportedOSPlatform] obj.WindowsOnlyMethod(); } @@ -1497,7 +1443,6 @@ public void SupportedWindowsFrom10_0_1904_To10_0_1909_NotWarn() public class C { [SupportedOSPlatform(""windows"")] - [ObsoletedInOSPlatform(""windows10.0.1909"")] [UnsupportedOSPlatform(""windows10.0.2004"")] public void WindowsOnlyMethod() { @@ -1506,14 +1451,10 @@ public void WindowsOnlyMethod() " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(10, 9).WithMessage("'WindowsOnlyMethod' is supported on 'windows'"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'WindowsOnlyMethod' is deprecated on 'windows' 10.0.1909 and later"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'WindowsOnlyMethod' is unsupported on 'windows' 10.0.2004 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(18, 9).WithMessage("'WindowsOnlyMethod' is deprecated on 'windows' 10.0.1909 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(31, 9).WithMessage("'WindowsOnlyMethod' is supported on 'windows'"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.ObsoleteOsRule).WithLocation(31, 9).WithMessage("'WindowsOnlyMethod' is deprecated on 'windows' 10.0.1909 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(31, 9).WithMessage("'WindowsOnlyMethod' is unsupported on 'windows' 10.0.2004 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(38, 9).WithMessage("'WindowsOnlyMethod' is deprecated on 'windows' 10.0.1909 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(38, 9).WithMessage("'WindowsOnlyMethod' is unsupported on 'windows' 10.0.2004 and later")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(22, 9).WithMessage("'WindowsOnlyMethod' is supported on 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(22, 9).WithMessage("'WindowsOnlyMethod' is unsupported on 'windows' 10.0.2004 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(29, 9).WithMessage("'WindowsOnlyMethod' is unsupported on 'windows' 10.0.2004 and later")); } private static VerifyCS.Test PopulateTestCs(string sourceCode, params DiagnosticResult[] expected) @@ -1607,28 +1548,6 @@ public sealed class UnsupportedOSPlatformAttribute : OSPlatformAttribute public UnsupportedOSPlatformAttribute(string platformName) : base(platformName) { } } - - [AttributeUsage(AttributeTargets.Assembly | - AttributeTargets.Class | - AttributeTargets.Constructor | - AttributeTargets.Event | - AttributeTargets.Method | - AttributeTargets.Module | - AttributeTargets.Property | - AttributeTargets.Field | - AttributeTargets.Struct, - AllowMultiple = true, Inherited = false)] - public sealed class ObsoletedInOSPlatformAttribute: OSPlatformAttribute - { - public ObsoletedInOSPlatformAttribute(string platformName) : base(platformName) - { } - public ObsoletedInOSPlatformAttribute(string platformName, string message) : base(platformName) - { - Message = message; - } - public string Message { get; } - public string Url { get; set; } - } } "; @@ -1670,23 +1589,6 @@ Public Sub New(ByVal platformName As String) MyBase.New(platformName) End Sub End Class - - - Public NotInheritable Class ObsoletedInOSPlatformAttribute - Inherits OSPlatformAttribute - - Public Sub New(ByVal platformName As String) - MyBase.New(platformName) - End Sub - - Public Sub New(ByVal platformName As String, ByVal message As String) - MyBase.New(platformName) - Message = message - End Sub - - Public ReadOnly Property Message As String - Public Property Url As String - End Class End Namespace "; } From 919cf2c9747207c45e6f7cfc22c48ab2e819c8ee Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 17 Aug 2020 20:00:15 -0700 Subject: [PATCH 39/48] Use DisplayString name --- .../PlatformCompatabilityAnalyzer.cs | 2 +- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 4 +-- .../PlatformCompatabilityAnalyzerTests.cs | 36 +++++++++---------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 12ac0f502b..807b920223 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -412,7 +412,7 @@ private static void ReportDiagnostics(IOperation operation, SmallDictionary Date: Wed, 19 Aug 2020 10:39:03 -0700 Subject: [PATCH 40/48] Apply guard methods new logic fix bug --- .../PlatformCompatabilityAnalyzer.cs | 121 +++--- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 349 +++++++++++++----- .../PlatformCompatabilityAnalyzerTests.cs | 23 +- 3 files changed, 331 insertions(+), 162 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 807b920223..a6949adfc2 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -63,7 +63,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private const string IsWindows = nameof(IsWindows); private const string IsWindowsVersionAtLeast = nameof(IsWindowsVersionAtLeast); - internal static DiagnosticDescriptor RequiredOsVersionRule = DiagnosticDescriptorHelper.Create(RuleId, + internal static DiagnosticDescriptor SupportedOsVersionRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, s_localizableSupportedOsVersionMessage, DiagnosticCategory.Interoperability, @@ -72,7 +72,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer isPortedFxCopRule: false, isDataflowRule: false); - internal static DiagnosticDescriptor RequiredOsRule = DiagnosticDescriptorHelper.Create(RuleId, + internal static DiagnosticDescriptor SupportedOsRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, s_localizableSupportedOsMessage, DiagnosticCategory.Interoperability, @@ -98,7 +98,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer description: s_localizableDescription, isPortedFxCopRule: false, isDataflowRule: false); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(RequiredOsRule, RequiredOsVersionRule, UnsupportedOsRule, UnsupportedOsVersionRule); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(SupportedOsRule, SupportedOsVersionRule, UnsupportedOsRule, UnsupportedOsVersionRule); public override void Initialize(AnalysisContext context) { @@ -181,7 +181,7 @@ private void AnalyzeOperationBlock( var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); var analysisResult = GlobalFlowStateAnalysis.TryGetOrComputeResult( cfg, context.OwningSymbol, CreateOperationVisitor, wellKnownTypeProvider, - context.Options, RequiredOsRule, performValueContentAnalysis, + context.Options, SupportedOsRule, performValueContentAnalysis, context.CancellationToken, out var valueContentAnalysisResult); if (analysisResult == null) @@ -266,85 +266,85 @@ private static bool ComputeNeedsValueContentAnalysis(IBlockOperation operationBl private static bool IsKnownValueGuarded(SmallDictionary attributes, GlobalFlowStateAnalysisValueSet value) { - using var capturedPlatforms = PooledSortedSet.GetInstance(StringComparer.OrdinalIgnoreCase); using var capturedVersions = PooledDictionary.GetInstance(StringComparer.OrdinalIgnoreCase); - return IsKnownValueGuarded(attributes, value, capturedPlatforms, capturedVersions); + return IsKnownValueGuarded(attributes, value, capturedVersions); static bool IsKnownValueGuarded( SmallDictionary attributes, GlobalFlowStateAnalysisValueSet value, - PooledSortedSet capturedPlatforms, PooledDictionary capturedVersions) { // 'GlobalFlowStateAnalysisValueSet.AnalysisValues' represent the && of values. foreach (var analysisValue in value.AnalysisValues) { - if (analysisValue is RuntimeMethodValue info && attributes.TryGetValue(info.PlatformName, out var attribute)) + if (analysisValue is RuntimeMethodValue info) { - if (info.Negated) + if (attributes.TryGetValue(info.PlatformName, out var attribute)) { - if (attribute.UnsupportedFirst != null) + if (info.Negated) { - if (capturedPlatforms.Contains(info.PlatformName)) + if (attribute.UnsupportedFirst != null) { if (attribute.UnsupportedFirst >= info.Version) { + if (attribute.SupportedFirst != null && attribute.SupportedFirst >= attribute.UnsupportedFirst) + { // deny list + attribute.SupportedFirst = null; + } attribute.UnsupportedFirst = null; } } - else if (IsEmptyVersion(attribute.UnsupportedFirst) && IsEmptyVersion(info.Version)) - { - attribute.UnsupportedFirst = null; - } - } - if (attribute.UnsupportedSecond != null) - { - if (capturedPlatforms.Contains(info.PlatformName)) + if (attribute.UnsupportedSecond != null) { if (attribute.UnsupportedSecond <= info.Version) { attribute.UnsupportedSecond = null; } } - else if (IsEmptyVersion(attribute.UnsupportedSecond) && IsEmptyVersion(info.Version)) + + if (!IsEmptyVersion(info.Version)) { - attribute.UnsupportedSecond = null; + capturedVersions[info.PlatformName] = info.Version; } } - - if (!IsEmptyVersion(info.Version)) - { - capturedVersions[info.PlatformName] = info.Version; - } - } - else - { - capturedPlatforms.Add(info.PlatformName); - - if (capturedVersions.Any()) + else { - if (attribute.UnsupportedFirst != null && capturedVersions.TryGetValue(info.PlatformName, out var version) && attribute.UnsupportedFirst >= version) + if (capturedVersions.Any()) { - attribute.UnsupportedFirst = null; + 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.UnsupportedSecond != null && capturedVersions.TryGetValue(info.PlatformName, out version) && attribute.UnsupportedSecond <= version) + if (attribute.SupportedFirst != null && attribute.SupportedFirst <= info.Version) { - attribute.UnsupportedSecond = null; + attribute.SupportedFirst = null; + RemoveUnsupportedWithLessVersion(info.Version, attribute); + RemoveOtherSupportsOnDifferentPlatforms(attributes, info.PlatformName); } - } - if (attribute.SupportedFirst != null && attribute.SupportedFirst <= info.Version) - { - attribute.SupportedFirst = null; - RemoveUnsupportedWithLessVersion(info.Version, attribute); + if (attribute.SupportedSecond != null && attribute.SupportedSecond <= info.Version) + { + attribute.SupportedSecond = null; + RemoveUnsupportedWithLessVersion(info.Version, attribute); + RemoveOtherSupportsOnDifferentPlatforms(attributes, info.PlatformName); + } + RemoveUnsupportsOnDifferentPlatforms(attributes, info.PlatformName); } - - if (attribute.SupportedSecond != null && attribute.SupportedSecond <= info.Version) + } + else + { + if (!info.Negated) { - attribute.SupportedSecond = null; - RemoveUnsupportedWithLessVersion(info.Version, attribute); + // it is checking one exact platform, other unsupported should be suppressed + RemoveUnsupportsOnDifferentPlatforms(attributes, info.PlatformName); } } } @@ -370,10 +370,9 @@ static bool IsKnownValueGuarded( // 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 = CopyOperationAttributes(attributes); - using var parentCapturedPlatforms = PooledSortedSet.GetInstance(capturedPlatforms); using var parentCapturedVersions = PooledDictionary.GetInstance(capturedVersions); - if (!IsKnownValueGuarded(parentAttributes, parent, parentCapturedPlatforms, parentCapturedVersions)) + if (!IsKnownValueGuarded(parentAttributes, parent, parentCapturedVersions)) { return false; } @@ -383,6 +382,18 @@ static bool IsKnownValueGuarded( return true; } + static void RemoveUnsupportsOnDifferentPlatforms(SmallDictionary attributes, string platformName) + { + foreach (var (name, attribute) in attributes) + { + if (!name.Equals(platformName, StringComparison.OrdinalIgnoreCase)) + { + attribute.UnsupportedFirst = null; + attribute.UnsupportedSecond = null; + } + } + } + static void RemoveUnsupportedWithLessVersion(Version supportedVersion, PlatformAttributes attribute) { if (attribute.UnsupportedFirst != null && attribute.UnsupportedFirst <= supportedVersion) @@ -390,6 +401,18 @@ static void RemoveUnsupportedWithLessVersion(Version supportedVersion, PlatformA 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; @@ -439,8 +462,8 @@ private static void ReportDiagnostics(IOperation operation, SmallDictionary - context.ReportDiagnostic(version == null ? operation.CreateDiagnostic(RequiredOsRule, name, platformName) : - operation.CreateDiagnostic(RequiredOsVersionRule, name, platformName, version)); + context.ReportDiagnostic(version == null ? operation.CreateDiagnostic(SupportedOsRule, name, platformName) : + operation.CreateDiagnostic(SupportedOsVersionRule, name, platformName, version)); private static void ReportUnsupportedDiagnostic(IOperation operation, OperationBlockAnalysisContext context, string name, string platformName, string? version = null) => context.ReportDiagnostic(version == null ? operation.CreateDiagnostic(UnsupportedOsRule, name, platformName) : diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index 587da3e50d..74765bc7b8 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -58,7 +58,7 @@ void Api() await VerifyAnalyzerAsyncCs(source); } - [Fact(Skip = "TODO: Needs to be fixed")] + [Fact] public async Task SupportedUnsupportedRange_GuardedWithOr() { var source = @" @@ -75,7 +75,7 @@ public void Api_Usage() Api(); } - [|Api()|]; + [|Api()|]; // Two diagnostics expected } [UnsupportedOSPlatform(""windows"")] @@ -85,38 +85,134 @@ void Api() } }" + MockAttributesCsSource + MockOperatingSystemApiSource; + await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(15, 9) + .WithMessage("'Test.Api()' is unsupported on 'windows'")); + } + + [Fact] + public async Task GuardsAroundSupported_SimpleIfElse() + { + var source = @" +using System.Runtime.Versioning; +using System; + +namespace PlatformCompatDemo.Bugs.GuardsAroundSupported +{ + class Caller + { + public static void TestWithGuardMethods() + { + if (OperatingSystemHelper.IsWindows() || OperatingSystemHelper.IsBrowser()) + { + [|Target.SupportedOnWindows()|]; + [|Target.SupportedOnWindows10()|]; + } + } + } + + class Target + { + [SupportedOSPlatform(""windows"")] + public static void SupportedOnWindows() { } + + [SupportedOSPlatform(""windows10.0"")] + public static void SupportedOnWindows10() { } + + [SupportedOSPlatform(""windows""), SupportedOSPlatform(""browser"")] + public static void SupportedOnWindowsAndBrowser() { } + + [SupportedOSPlatform(""windows10.0""), SupportedOSPlatform(""browser"")] + public static void SupportedOnWindows10AndBrowser() { } + } +}" + MockAttributesCsSource + MockOperatingSystemApiSource; + await VerifyAnalyzerAsyncCs(source); } [Fact] - public async Task SupportedUnsupportedRange_GuardedWithAnd() + public async Task GuardsAroundSupported() { var source = @" using System.Runtime.Versioning; using System; -class Test +namespace PlatformCompatDemo.Bugs.GuardsAroundSupported { - public void Api_Usage() + class Caller { - if (OperatingSystemHelper.IsIOSVersionAtLeast(12,0) && - !OperatingSystemHelper.IsIOSVersionAtLeast(14,0)) + public static void TestWithGuardMethods() { - Api(); + if (!OperatingSystemHelper.IsWindows()) + { + [|Target.SupportedOnWindows()|]; + [|Target.SupportedOnWindows10()|]; + [|Target.SupportedOnWindowsAndBrowser()|]; // expected two diagnostics - supported on windows and browser + [|Target.SupportedOnWindows10AndBrowser()|]; // 16 expected two diagnostics - supported on windows 10 and browser + } + + if (OperatingSystemHelper.IsWindows()) + { + Target.SupportedOnWindows(); + [|Target.SupportedOnWindows10()|]; + Target.SupportedOnWindowsAndBrowser(); // no diagnostic expected - the API is supported on windows, no need to warn for other platforms support + [|Target.SupportedOnWindows10AndBrowser()|]; // 24 expected two diagnostics - supported on windows 10 and browser + } + + if (OperatingSystemHelper.IsWindowsVersionAtLeast(10)) + { + Target.SupportedOnWindows(); + Target.SupportedOnWindows10(); + Target.SupportedOnWindowsAndBrowser(); // no diagnostic expected - the API is supported on windows, no need to warn for other platforms support + Target.SupportedOnWindows10AndBrowser(); //32 The same, no diagnostic expected + } + + if (OperatingSystemHelper.IsBrowser()) + { + [|Target.SupportedOnWindows()|]; + [|Target.SupportedOnWindows10()|]; + Target.SupportedOnWindowsAndBrowser(); // 39 No diagnostic expected - the API is supported on browser, no need to warn for other platforms support + Target.SupportedOnWindows10AndBrowser(); // The same, no diagnostic expected + } + + if (OperatingSystemHelper.IsWindows() || OperatingSystemHelper.IsBrowser()) + { + [|Target.SupportedOnWindows()|]; // 45 No diagnostic expected because of it was windows + [|Target.SupportedOnWindows10()|]; + Target.SupportedOnWindowsAndBrowser(); + [|Target.SupportedOnWindows10AndBrowser()|]; // 48 two diagnostic expected windows 10 and browser + } + + if (OperatingSystemHelper.IsWindowsVersionAtLeast(10) || OperatingSystemHelper.IsBrowser()) + { + [|Target.SupportedOnWindows()|]; // 53 + [|Target.SupportedOnWindows10()|]; + Target.SupportedOnWindowsAndBrowser(); + Target.SupportedOnWindows10AndBrowser(); + } } - [|Api()|]; } - [SupportedOSPlatform(""ios12.0"")] - [UnsupportedOSPlatform(""ios14.0"")] - void Api() + class Target { + [SupportedOSPlatform(""windows"")] + public static void SupportedOnWindows() { } + + [SupportedOSPlatform(""windows10.0"")] + public static void SupportedOnWindows10() { } + + [SupportedOSPlatform(""windows""), SupportedOSPlatform(""browser"")] + public static void SupportedOnWindowsAndBrowser() { } + + [SupportedOSPlatform(""windows10.0""), SupportedOSPlatform(""browser"")] + public static void SupportedOnWindows10AndBrowser() { } } }" + MockAttributesCsSource + MockOperatingSystemApiSource; await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(14, 9) - .WithMessage("'Test.Api()' is unsupported on 'ios' 14.0 and later")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithLocation(15, 17).WithMessage("'Target.SupportedOnWindowsAndBrowser()' is supported on 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(16, 17).WithMessage("'Target.SupportedOnWindows10AndBrowser()' is supported on 'windows' 10.0 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(24, 17).WithMessage("'Target.SupportedOnWindows10AndBrowser()' is supported on 'browser'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(48, 17).WithMessage("'Target.SupportedOnWindows10AndBrowser()' is supported on 'browser'")); } [Fact] @@ -126,39 +222,111 @@ public async Task GuardsAroundUnsupported() using System.Runtime.Versioning; using System; -class Caller +namespace PlatformCompatDemo.Bugs.GuardsAroundUnsupported { - public static void TestWithGuardMethods() + class Caller { - if (!OperatingSystemHelper.IsWindows()) + public static void TestWithGuardMethods() { - Target.UnsupportedInWindows(); - [|Target.UnsupportedInWindows10()|]; // not windows doesn't mean it is windows 10 or before + if (!OperatingSystemHelper.IsWindows()) + { + Target.UnsupportedInWindows(); + Target.UnsupportedInWindows10(); + [|Target.UnsupportedOnBrowser()|]; // row 15 expected diagnostic - browser unsupported + [|Target.UnsupportedOnWindowsAndBrowser()|]; // expected diagnostic - browser unsupported + [|Target.UnsupportedOnWindows10AndBrowser()|]; // expected diagnostic - browser unsupported + } + + if (!OperatingSystemHelper.IsWindowsVersionAtLeast(10)) + { + [|Target.UnsupportedInWindows()|]; // row 22 expected diagnostic - windows unsupported + Target.UnsupportedInWindows10(); + [|Target.UnsupportedOnBrowser()|]; // expected diagnostic - browser unsupported + [|Target.UnsupportedOnWindowsAndBrowser()|]; // expected 2 diagnostics - windows and browser unsupported + [|Target.UnsupportedOnWindows10AndBrowser()|]; // expected diagnostic - browser unsupported + } + + if (OperatingSystemHelper.IsWindows()) + { + [|Target.UnsupportedInWindows()|]; // row 31 expected diagnostic - windows unsupported + [|Target.UnsupportedInWindows10()|]; // expected diagnostic - windows 10 unsupported + Target.UnsupportedOnBrowser(); + [|Target.UnsupportedOnWindowsAndBrowser()|]; // expected diagnostic - windows unsupported + [|Target.UnsupportedOnWindows10AndBrowser()|]; // expected diagnostic - windows 10 unsupported + } + + if (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10)) + { + [|Target.UnsupportedInWindows()|]; // row 40 expected diagnostic - windows unsupported + Target.UnsupportedInWindows10(); + Target.UnsupportedOnBrowser(); // FAIL: unexpected diagnostic - browser unsupported + [|Target.UnsupportedOnWindowsAndBrowser()|]; // expected diagnostic - windows unsupported + Target.UnsupportedOnWindows10AndBrowser(); // FAIL: unexpected diagnostic - browser unsupported + } + + if (OperatingSystemHelper.IsBrowser()) + { + Target.UnsupportedInWindows(); + Target.UnsupportedInWindows10(); + [|Target.UnsupportedOnBrowser()|}; // row 51 expected diagnostic - browser unsupported + [|Target.UnsupportedOnWindowsAndBrowser()|]; // expected diagnostic - browser unsupported + [|Target.UnsupportedOnWindows10AndBrowser()|]; // expected diagnostic - browser unsupported + } } - if (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10)) - { - [|Target.UnsupportedInWindows()|]; - Target.UnsupportedInWindows10(); + } + + class Target + { + [UnsupportedOSPlatform(""windows"")] + public static void UnsupportedInWindows() { } + + [UnsupportedOSPlatform(""windows10.0"")] + public static void UnsupportedInWindows10() { } + + [UnsupportedOSPlatform(""browser"")] + public static void UnsupportedOnBrowser() { } + + [UnsupportedOSPlatform(""windows""), UnsupportedOSPlatform(""browser"")] + public static void UnsupportedOnWindowsAndBrowser() { } + + [UnsupportedOSPlatform(""windows10.0""), UnsupportedOSPlatform(""browser"")] + public static void UnsupportedOnWindows10AndBrowser() { } + } +}" + MockAttributesCsSource + MockOperatingSystemApiSource; + + await VerifyAnalyzerAsyncCs(source, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(25, 17).WithMessage("'Target.UnsupportedOnWindowsAndBrowser()' is unsupported on 'browser'")); } - if (OperatingSystemHelper.IsBrowser()) + + [Fact] + public async Task SupportedUnsupportedRange_GuardedWithAnd() { - [|Target.UnsupportedInWindows()|]; // It is browser doesn't mean it is not windows - [|Target.UnsupportedInWindows10()|]; // The same - } - } -} + var source = @" +using System.Runtime.Versioning; +using System; -class Target +class Test { - [UnsupportedOSPlatform(""windows"")] - public static void UnsupportedInWindows() { } + public void Api_Usage() + { + if (OperatingSystemHelper.IsIOSVersionAtLeast(12,0) && + !OperatingSystemHelper.IsIOSVersionAtLeast(14,0)) + { + Api(); + } + [|Api()|]; // two diagnostics expected + } - [UnsupportedOSPlatform(""windows10.0"")] - public static void UnsupportedInWindows10() { } -} -" + MockAttributesCsSource + MockOperatingSystemApiSource; + [SupportedOSPlatform(""ios12.0"")] + [UnsupportedOSPlatform(""ios14.0"")] + void Api() + { + } +}" + MockAttributesCsSource + MockOperatingSystemApiSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(14, 9) + .WithMessage("'Test.Api()' is unsupported on 'ios' 14.0 and later")); } [Fact] @@ -179,7 +347,7 @@ void M1() } else { - [|NotForIos12OrLater()|]; + NotForIos12OrLater(); [|NotForBrowser()|]; } @@ -198,7 +366,7 @@ void M1() } else { - [|NotForIos12OrLater()|]; + NotForIos12OrLater(); } if(OperatingSystemHelper.IsIOSVersionAtLeast(12,1)) @@ -207,7 +375,7 @@ void M1() } else { - [|NotForIos12OrLater()|]; + NotForIos12OrLater(); } if(OperatingSystemHelper.IsIOS() && !OperatingSystemHelper.IsIOSVersionAtLeast(12,0)) @@ -988,7 +1156,7 @@ void M() { LocalM(); - if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 10, 2)) + if (!OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 10, 2)) { LocalM(); } @@ -1467,10 +1635,10 @@ public void M1() [|M2()|]; } - if((OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222)) || + if(OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222) || OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) { - [|M2()|]; + M2(); } if(OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 12) || @@ -1478,9 +1646,8 @@ public void M1() { [|M2()|]; } - - [|M2()|]; } + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { @@ -1501,38 +1668,17 @@ public class Test { public void M1() { - [|M2()|]; - - if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) || - (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222))) - { - [|M2()|]; - } - else - { - [|M2()|]; - } - - if((OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222)) || - OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) - { - [|M2()|]; - } - else - { - [|M2()|]; - } - if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) || OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) { - M2(); // Even it is not meaningful check, it shouldn't warn + M2(); } else { [|M2()|]; } } + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { @@ -1553,34 +1699,39 @@ public class Test { public void M1() { - [|M2()|]; - if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) || - OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 5, 1)) + OperatingSystemHelper.IsLinux()) { - [|M2()|]; + [|M2()|]; //12 } - else if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 9)) + else if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) { - [|M2()|]; + M2(); } else [|M2()|]; - if(OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222) || - OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) + if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2) || + (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222))) { [|M2()|]; } - else if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 11)) + else { - M2(); + [|M2()|]; + } + + if((OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(12, 0, 19222)) || + OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 10, 2)) + { + [|M2()|]; //34 } else { [|M2()|]; } } + [SupportedOSPlatform(""Windows10.1.2.3"")] public void M2() { @@ -1591,7 +1742,7 @@ public void M2() } [Fact] - public async Task GuardedCall_SimpleIfElseIfTestWithLogicalOrAnd() + public async Task GuardedCall_SimpleIfElseTestWithLogicalOrAnd() { var source = @" using System.Runtime.Versioning; @@ -1600,35 +1751,28 @@ public async Task GuardedCall_SimpleIfElseIfTestWithLogicalOrAnd() class Test { void M1() - { - [|M2()|]; - - if((OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 1) || - OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 1)) && - (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 12) || - OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 2))) + { + if ((!OperatingSystemHelper.IsWindows() || OperatingSystemHelper.IsWindowsVersionAtLeast(10, 0, 1903)) && + (OperatingSystemHelper.IsWindows() && !OperatingSystemHelper.IsWindowsVersionAtLeast(10, 0, 2004))) { - [|M2()|]; - } - else if (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 13) || - OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 3) || - OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 4)) - { - [|M2()|]; + M2(); } else { - [|M2()|]; + [|M2()|]; // Two diagnostics expected } } - [SupportedOSPlatform(""Windows10.1.2.3"")] + [UnsupportedOSPlatform(""windows"")] + [SupportedOSPlatform(""windows10.0.1903"")] + [UnsupportedOSPlatform(""windows10.0.2004"")] void M2() { } }" + MockAttributesCsSource + MockOperatingSystemApiSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithLocation(16, 13).WithMessage("'Test.M2()' is unsupported on 'windows'")); var vbSource = @" Imports System.Runtime.Versioning @@ -1636,21 +1780,22 @@ Imports System Class Test Private Sub M1() - If (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 1) OrElse OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 1)) AndAlso (OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 12) OrElse OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 2)) Then - [|M2()|] - ElseIf OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Windows"", 13) OrElse OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 3) OrElse OperatingSystemHelper.IsOSPlatformVersionAtLeast(""Linux"", 4) Then - [|M2()|] + If (Not OperatingSystemHelper.IsWindows() OrElse OperatingSystemHelper.IsWindowsVersionAtLeast(10, 0, 1903)) AndAlso (OperatingSystemHelper.IsWindows() AndAlso Not OperatingSystemHelper.IsWindowsVersionAtLeast(10, 0, 2004)) Then + M2() Else [|M2()|] End If End Sub - + + + Private Sub M2() End Sub End Class " + MockRuntimeApiSourceVb + MockAttributesVbSource; - await VerifyAnalyzerAsyncVb(vbSource); + await VerifyAnalyzerAsyncVb(vbSource, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithLocation(10, 13).WithMessage("'Test.M2()' is unsupported on 'Windows'")); } [Fact] diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index a71f9631c4..1f82ff893f 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -1001,6 +1001,8 @@ public void TestUnsupportedOnWindows() [|TargetSupportedOnWindows.FunctionUnsupportedOnBrowser()|]; // should warn supported on windows and unsupported on browser as this is deny list [|TargetUnsupportedOnWindows.FunctionSupportedOnBrowser();|] // Fail: should warn supported on browser, but is this valid scenario? + TargetUnsupportedOnWindows.FunctionSupportedOnWindows(); // It's unsupporting Windows at the call site, which means the call site supports all other platforms. + // It's calling into code that was NOT supported only on Windows but eventually added support, so it shouldn't raise diagnostic } } @@ -1166,7 +1168,7 @@ public void M2() } " + MockAttributesCsSource; await VerifyCS.VerifyAnalyzerAsync(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithSpan(10, 21, 10, 29).WithArguments("M2", "Windows", "10.1.2.3")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithSpan(10, 21, 10, 29).WithArguments("M2", "Windows", "10.1.2.3")); } public static IEnumerable SupportedOsAttributeTestData() @@ -1217,8 +1219,8 @@ public void M2() if (warn) { await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithSpan(9, 32, 9, 54).WithArguments("OsDependentClass", "Windows", "10.1.2.3"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithSpan(10, 9, 10, 17).WithArguments("OsDependentClass.M2()", "Windows", "10.1.2.3")); + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithSpan(9, 32, 9, 54).WithArguments("OsDependentClass", "Windows", "10.1.2.3"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithSpan(10, 9, 10, 17).WithArguments("OsDependentClass.M2()", "Windows", "10.1.2.3")); } else { @@ -1390,9 +1392,9 @@ public void DoesNotWorkOnWindows() " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(18, 9).WithMessage("'C.DoesNotWorkOnWindows()' is unsupported on 'windows'"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(18, 9).WithMessage("'C.DoesNotWorkOnWindows()' is supported on 'windows' 10.0.1903 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(18, 9).WithMessage("'C.DoesNotWorkOnWindows()' is supported on 'windows' 10.0.1903 and later"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(31, 9).WithMessage("'C.DoesNotWorkOnWindows()' is unsupported on 'windows'"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(31, 9).WithMessage("'C.DoesNotWorkOnWindows()' is supported on 'windows' 10.0.1903 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(31, 9).WithMessage("'C.DoesNotWorkOnWindows()' is supported on 'windows' 10.0.1903 and later"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(38, 9).WithMessage("'C.DoesNotWorkOnWindows()' is unsupported on 'windows' 10.0.2004 and later")); } @@ -1450,9 +1452,9 @@ public void WindowsOnlyMethod() } " + MockAttributesCsSource; await VerifyAnalyzerAsyncCs(source, - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(10, 9).WithMessage("'C.WindowsOnlyMethod()' is supported on 'windows'"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsVersionRule).WithLocation(10, 9).WithMessage("'C.WindowsOnlyMethod()' is unsupported on 'windows' 10.0.2004 and later"), - VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.RequiredOsRule).WithLocation(22, 9).WithMessage("'C.WindowsOnlyMethod()' is supported on 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithLocation(10, 9).WithMessage("'C.WindowsOnlyMethod()' is supported on 'windows'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(10, 9).WithMessage("'C.WindowsOnlyMethod()' is unsupported on 'windows' 10.0.2004 and later"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithLocation(22, 9).WithMessage("'C.WindowsOnlyMethod()' is supported on 'windows'"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(22, 9).WithMessage("'C.WindowsOnlyMethod()' is unsupported on 'windows' 10.0.2004 and later"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(29, 9).WithMessage("'C.WindowsOnlyMethod()' is unsupported on 'windows' 10.0.2004 and later")); } @@ -1470,8 +1472,6 @@ private static VerifyCS.Test PopulateTestCs(string sourceCode, params Diagnostic return test; } - private static async Task VerifyAnalyzerAsyncCs(string sourceCode) => await PopulateTestCs(sourceCode).RunAsync(); - private static async Task VerifyAnalyzerAsyncCs(string sourceCode, params DiagnosticResult[] expectedDiagnostics) => await PopulateTestCs(sourceCode, expectedDiagnostics).RunAsync(); @@ -1482,7 +1482,8 @@ private static async Task VerifyAnalyzerAsyncCs(string sourceCode, string editor await test.RunAsync(); } - private static async Task VerifyAnalyzerAsyncVb(string sourceCode) => await PopulateTestVb(sourceCode).RunAsync(); + private static async Task VerifyAnalyzerAsyncVb(string sourceCode, params DiagnosticResult[] expectedDiagnostics) + => await PopulateTestVb(sourceCode, expectedDiagnostics).RunAsync(); private static VerifyVB.Test PopulateTestVb(string sourceCode, params DiagnosticResult[] expected) { From d9aa6a9b0cffc43ee9268e3c9dab68b8f7ea0f2e Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 20 Aug 2020 11:32:24 -0700 Subject: [PATCH 41/48] Add more guard tests and fix failures --- .../PlatformCompatabilityAnalyzer.cs | 33 +- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 354 +++++++++++++++++- 2 files changed, 371 insertions(+), 16 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index a6949adfc2..7d9bce8a30 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -288,8 +288,11 @@ static bool IsKnownValueGuarded( if (attribute.UnsupportedFirst >= info.Version) { if (attribute.SupportedFirst != null && attribute.SupportedFirst >= attribute.UnsupportedFirst) - { // deny list + { + // deny list, no need further check attribute.SupportedFirst = null; + attribute.SupportedSecond = null; + attribute.UnsupportedSecond = null; } attribute.UnsupportedFirst = null; } @@ -312,30 +315,37 @@ static bool IsKnownValueGuarded( { if (capturedVersions.Any()) { - if (attribute.UnsupportedFirst != null && capturedVersions.TryGetValue(info.PlatformName, out var version) && attribute.UnsupportedFirst >= version) + 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) + if (attribute.UnsupportedSecond != null && + capturedVersions.TryGetValue(info.PlatformName, out version) && + attribute.UnsupportedSecond <= version) { attribute.UnsupportedSecond = null; } } - if (attribute.SupportedFirst != null && attribute.SupportedFirst <= info.Version) + 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) + if (attribute.SupportedSecond != null && + attribute.SupportedSecond <= info.Version) { attribute.SupportedSecond = null; RemoveUnsupportedWithLessVersion(info.Version, attribute); RemoveOtherSupportsOnDifferentPlatforms(attributes, info.PlatformName); } + RemoveUnsupportsOnDifferentPlatforms(attributes, info.PlatformName); } } @@ -386,10 +396,14 @@ static void RemoveUnsupportsOnDifferentPlatforms(SmallDictionary Date: Fri, 21 Aug 2020 13:40:05 -0700 Subject: [PATCH 42/48] Add Msbuild configuration support --- .../PlatformCompatabilityAnalyzer.cs | 355 +++++++++-------- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 359 +++++++++--------- .../PlatformCompatabilityAnalyzerTests.cs | 188 +++++++-- .../Options/MSBuildItemOptionNames.cs | 13 +- 4 files changed, 554 insertions(+), 361 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 7d9bce8a30..cadbd4ec00 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -3,8 +3,10 @@ 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; @@ -30,6 +32,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private static readonly ImmutableArray s_platformCheckMethodNames = ImmutableArray.Create(IsOSPlatformVersionAtLeast, IsOSPlatform, IsBrowser, IsLinux, IsFreeBSD, IsFreeBSDVersionAtLeast, IsAndroid, IsAndroidVersionAtLeast, IsIOS, IsIOSVersionAtLeast, IsMacOS, IsMacOSVersionAtLeast, IsTvOS, IsTvOSVersionAtLeast, IsWatchOS, IsWatchOSVersionAtLeast, IsWindows, IsWindowsVersionAtLeast); private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(SupportedOSPlatformAttribute, UnsupportedOSPlatformAttribute); + private static ImmutableArray s_supportedMsBuildPlatforms; 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)); @@ -120,6 +123,7 @@ public override void Initialize(AnalysisContext context) return; } + s_supportedMsBuildPlatforms = GetSupportedPlatforms(context.Options, context.Compilation, context.CancellationToken); var runtimeIsOSPlatformMethod = runtimeInformationType.GetMembers().OfType().Where(m => IsOSPlatform == m.Name && m.IsStatic && @@ -141,8 +145,10 @@ static ImmutableArray GetRuntimePlatformGuardMethods(IMethodSymbo m.ReturnType.SpecialType == SpecialType.System_Boolean).ToImmutableArray(). Add(runtimeIsOSPlatformMethod); } - } + static ImmutableArray GetSupportedPlatforms(AnalyzerOptions options, Compilation compilation, CancellationToken cancellationToken) => + options.GetMSBuildItemMetadataValues(MSBuildItemOptionNames.SupportedPlatform, compilation, cancellationToken); + } private void AnalyzeOperationBlock( OperationBlockStartAnalysisContext context, ImmutableArray guardMethods, @@ -287,7 +293,8 @@ static bool IsKnownValueGuarded( { if (attribute.UnsupportedFirst >= info.Version) { - if (attribute.SupportedFirst != null && attribute.SupportedFirst >= attribute.UnsupportedFirst) + if (attribute.SupportedFirst != null && + attribute.SupportedFirst >= attribute.UnsupportedFirst) { // deny list, no need further check attribute.SupportedFirst = null; @@ -379,7 +386,7 @@ static bool IsKnownValueGuarded( { // 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 = CopyOperationAttributes(attributes); + var parentAttributes = CopyAttributes(attributes); using var parentCapturedVersions = PooledDictionary.GetInstance(capturedVersions); if (!IsKnownValueGuarded(parentAttributes, parent, parentCapturedVersions)) @@ -397,8 +404,7 @@ static void RemoveUnsupportsOnDifferentPlatforms(SmallDictionary operationAttributes, out SmallDictionary copiedAttributes) + { + copiedAttributes = new SmallDictionary(StringComparer.OrdinalIgnoreCase); + foreach (var (platformName, attributes) in operationAttributes) + { + if (AllowList(attributes) || s_supportedMsBuildPlatforms.IndexOf(platformName, 0, StringComparer.OrdinalIgnoreCase) != -1) + { + + copiedAttributes.Add(platformName, CopyAllAttributes(new PlatformAttributes(), attributes)); } } + return copiedAttributes.Any(); } - private static SmallDictionary CopyOperationAttributes(SmallDictionary attributes) + private static SmallDictionary CopyAttributes(SmallDictionary copyAttributes) { var copy = new SmallDictionary(StringComparer.OrdinalIgnoreCase); - foreach (var attribute in attributes) + foreach (var (platformName, attributes) in copyAttributes) { - copy.Add(attribute.Key, CopyAllAttributes(new PlatformAttributes(), attribute.Value)); + copy.Add(platformName, CopyAllAttributes(new PlatformAttributes(), attributes)); } return copy; } @@ -551,9 +575,8 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary.GetInstance(StringComparer.OrdinalIgnoreCase); - foreach (string key in operationAttributes.Keys) + foreach (var (platformName, attribute) in operationAttributes) { - var attribute = operationAttributes[key]; var diagnosticAttribute = new PlatformAttributes(); if (attribute.SupportedFirst != null) @@ -567,11 +590,11 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary ca.Value.UnsupportedFirst != null && - (ca.Value.SupportedFirst == null || ca.Value.UnsupportedFirst <= ca.Value.SupportedFirst))) + // 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 (s_supportedMsBuildPlatforms.Contains(platformName) && + callSiteAttributes.Any(ca => DenyList(ca.Value))) { diagnosticAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone(); } @@ -635,7 +662,7 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary v.SupportedFirst != null)) + else if (s_supportedMsBuildPlatforms.Contains(platformName) && + !callSiteAttributes.Values.Any(v => v.SupportedFirst != null)) { - // if call site has no any other supported attribute it means global, so need to warn + // 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(); } } @@ -666,7 +698,7 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary notSuppressedAttributes, string name) - { - if (operationAttributes.SupportedFirst != null) + static void AddOrUpdatedDiagnostic(PlatformAttributes operationAttributes, SmallDictionary notSuppressedAttributes, string name) { - if (!notSuppressedAttributes.TryGetValue(name, out var diagnosticAttribute)) + if (operationAttributes.SupportedFirst != null) { - diagnosticAttribute = new PlatformAttributes(); + if (!notSuppressedAttributes.TryGetValue(name, out var diagnosticAttribute)) + { + diagnosticAttribute = new PlatformAttributes(); + } + diagnosticAttribute.SupportedFirst = (Version)operationAttributes.SupportedFirst.Clone(); + notSuppressedAttributes[name] = diagnosticAttribute; } - 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) @@ -725,31 +783,6 @@ private static PlatformAttributes CopyAllAttributes(PlatformAttributes copyTo, P return copyTo; } - private static bool SuppressedByCallSiteUnsupported(PlatformAttributes callSiteAttribute, Version unsupporteAttribute) => - callSiteAttribute.UnsupportedFirst != null && unsupporteAttribute >= callSiteAttribute.UnsupportedFirst || - callSiteAttribute.UnsupportedSecond != null && unsupporteAttribute >= callSiteAttribute.UnsupportedSecond; - - private static bool UnsupportedSecondSuppressed(PlatformAttributes attribute, PlatformAttributes callSiteAttribute) => - SuppressedByCallSiteSupported(attribute, callSiteAttribute.SupportedFirst) || - SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedSecond!); - - private static bool SuppressedByCallSiteSupported(PlatformAttributes attribute, Version? callSiteSupportedFirst) => - callSiteSupportedFirst != null && callSiteSupportedFirst >= attribute.SupportedFirst! && - attribute.SupportedSecond != null && callSiteSupportedFirst >= attribute.SupportedSecond; - - private 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 - private static bool OptionalOsVersionsSuppressed(PlatformAttributes callSiteAttribute, PlatformAttributes attribute) => - (callSiteAttribute.SupportedFirst == null || attribute.SupportedFirst <= callSiteAttribute.SupportedFirst) && - (callSiteAttribute.SupportedSecond == null || attribute.SupportedFirst <= callSiteAttribute.SupportedSecond); - - private static bool MandatoryOsVersionsSuppressed(PlatformAttributes callSitePlatforms, Version checkingVersion) => - callSitePlatforms.SupportedFirst != null && checkingVersion <= callSitePlatforms.SupportedFirst || - callSitePlatforms.SupportedSecond != null && checkingVersion <= callSitePlatforms.SupportedSecond; - // 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 && @@ -779,7 +812,7 @@ private static bool TryGetOrCreatePlatformAttributes( if (container != null && TryGetOrCreatePlatformAttributes(container, platformSpecificMembers, out var containerAttributes)) { - attributes = CopyOperationAttributes(containerAttributes); + attributes = CopyAttributes(containerAttributes); } AddPlatformAttributes(symbol.GetAttributes(), ref attributes); @@ -828,126 +861,144 @@ attribute.ConstructorArguments[0] is { } argument && private static void AddAttribute(string name, Version version, SmallDictionary existingAttributes, string platformName) { - switch (name) + if (name == SupportedOSPlatformAttribute) { - case SupportedOSPlatformAttribute: - AddOrUpdateSupportedAttribute(existingAttributes[platformName], version); - break; - case UnsupportedOSPlatformAttribute: - AddOrUpdateUnsupportedAttribute(platformName, existingAttributes[platformName], version, existingAttributes); - break; + AddOrUpdateSupportedAttribute(existingAttributes[platformName], version); + } + else + { + Debug.Assert(name == UnsupportedOSPlatformAttribute); + AddOrUpdateUnsupportedAttribute(platformName, existingAttributes[platformName], version, existingAttributes); } - } - private static void AddOrUpdateUnsupportedAttribute(string name, PlatformAttributes attributes, Version version, SmallDictionary existingAttributes) - { - if (attributes.UnsupportedFirst != null) + static void AddOrUpdateUnsupportedAttribute(string name, PlatformAttributes attributes, Version version, SmallDictionary existingAttributes) { - if (attributes.UnsupportedFirst > version) + if (attributes.UnsupportedFirst != null) { - if (attributes.UnsupportedSecond != null) + if (attributes.UnsupportedFirst > version) { - if (attributes.UnsupportedSecond > attributes.UnsupportedFirst) + if (attributes.UnsupportedSecond != null) + { + if (attributes.UnsupportedSecond > attributes.UnsupportedFirst) + { + attributes.UnsupportedSecond = attributes.UnsupportedFirst; + } + } + else { attributes.UnsupportedSecond = attributes.UnsupportedFirst; } + + attributes.UnsupportedFirst = version; } else { - attributes.UnsupportedSecond = attributes.UnsupportedFirst; + 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; + } + } } - - attributes.UnsupportedFirst = version; } else { - if (attributes.UnsupportedSecond != null) + if (attributes.SupportedFirst != null && attributes.SupportedFirst >= version) { - if (attributes.UnsupportedSecond > version) + // Override needed + if (attributes.SupportedSecond != null) { - attributes.UnsupportedSecond = version; + attributes.SupportedFirst = attributes.SupportedSecond; + attributes.SupportedSecond = null; } - } - else - { - if (attributes.SupportedFirst != null && attributes.SupportedFirst < version) + else { - // We should ignore second attribute in case like [UnsupportedOSPlatform(""windows""), - // [UnsupportedOSPlatform(""windows11.0"")] which doesn't have supported in between - attributes.UnsupportedSecond = version; + attributes.SupportedFirst = null; + } + if (!HasAnySupportedOnlyAttribute(name, existingAttributes)) + { + attributes.UnsupportedFirst = 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 (!HasAnySuppoertedOnlyAttribute(name, existingAttributes)) { attributes.UnsupportedFirst = version; } } - else - { - attributes.UnsupportedFirst = version; - } - } - } - private static bool HasAnySuppoertedOnlyAttribute(string name, SmallDictionary existingAttributes) => - existingAttributes.Any(a => a.Key != name && a.Value.SupportedFirst != null && - (a.Value.UnsupportedFirst == null || a.Value.SupportedFirst < a.Value.UnsupportedFirst)); + static bool HasAnySupportedOnlyAttribute(string name, SmallDictionary existingAttributes) => + existingAttributes.Any(a => !a.Key.Equals(name, StringComparison.OrdinalIgnoreCase) && + AllowList(a.Value)); + } - private static void AddOrUpdateSupportedAttribute(PlatformAttributes attributes, Version version) - { - if (attributes.SupportedFirst != null) + static void AddOrUpdateSupportedAttribute(PlatformAttributes attributes, Version version) { - if (attributes.SupportedFirst > version) + if (attributes.SupportedFirst != null) { - if (attributes.SupportedSecond != null) + if (attributes.SupportedFirst > version) { - if (attributes.SupportedSecond < attributes.SupportedFirst) + if (attributes.SupportedSecond != null) + { + if (attributes.SupportedSecond < attributes.SupportedFirst) + { + attributes.SupportedSecond = attributes.SupportedFirst; + } + } + else { attributes.SupportedSecond = attributes.SupportedFirst; } + + attributes.SupportedFirst = version; } else { - attributes.SupportedSecond = attributes.SupportedFirst; - } - - attributes.SupportedFirst = version; - } - else - { - if (attributes.SupportedSecond != null) - { - if (attributes.SupportedSecond < version) + if (attributes.SupportedSecond != null) + { + if (attributes.SupportedSecond < version) + { + attributes.SupportedSecond = version; + } + } + else { attributes.SupportedSecond = version; } } - else - { - attributes.SupportedSecond = version; - } } - } - else - { - attributes.SupportedFirst = 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/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index 3f4cf68113..8c9a7dc609 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -85,7 +85,7 @@ void Api() } }" + MockAttributesCsSource + MockOperatingSystemApiSource; - await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(15, 9) + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(15, 9) .WithMessage("'Test.Api()' is unsupported on 'windows'")); } @@ -222,7 +222,7 @@ public async Task MoreGuardsAroundSupported() using System.Runtime.Versioning; using System; -namespace PlatformCompatDemo.Bugs.GuardsAroundSupported +namespace PlatformCompatDemo.SupportedUnupported { class Caller { @@ -271,58 +271,9 @@ public static void UnsupportedCombinations() } } } +}" + TargetTypesForTest + MockAttributesCsSource + MockOperatingSystemApiSource; - public class TypeWithoutAttributes - { - [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0"")] - public void TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11() { } - - [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0"")] - public void TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12() { } - - [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0""), SupportedOSPlatform(""windows13.0"")] - public void TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12SupportedOnWindows13() { } - } - - [UnsupportedOSPlatform(""windows"")] - public class TypeUnsupportedOnWindows - { - [SupportedOSPlatform(""windows11.0"")] - public void TypeUnsupportedOnWindows_FunctionSupportedOnWindows11() { } - - [SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0"")] - public void TypeUnsupportedOnWindows_FunctionSupportedOnWindows11UnsupportedOnWindows12() { } - - [SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0""), SupportedOSPlatform(""windows13.0"")] - public void TypeUnsupportedOnWindows_FunctionSupportedOnWindows11UnsupportedOnWindows12SupportedOnWindows13() { } - } - - [UnsupportedOSPlatform(""browser"")] - public class TypeUnsupportedOnBrowser - { - [SupportedOSPlatform(""browser"")] - public void TypeUnsupportedOnBrowser_FunctionSupportedOnBrowser() { } - } - - [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0"")] - public class TypeUnsupportedOnWindowsSupportedOnWindows11 - { - [UnsupportedOSPlatform(""windows12.0"")] - public void TypeUnsupportedOnWindowsSupportedOnWindows11_FunctionUnsupportedOnWindows12() { } - - [UnsupportedOSPlatform(""windows12.0""), SupportedOSPlatform(""windows13.0"")] - public void TypeUnsupportedOnWindowsSupportedOnWindows11_FunctionUnsupportedOnWindows12SupportedOnWindows13() { } - } - - [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0"")] - public class TypeUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12 - { - [SupportedOSPlatform(""windows13.0"")] - public void TypeUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12_FunctionSupportedOnWindows13() { } - } -}" + MockAttributesCsSource + MockOperatingSystemApiSource; - - await VerifyAnalyzerAsyncCs(source, + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(37, 17).WithMessage("'TypeWithoutAttributes.TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11()' is unsupported on 'windows'"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(38, 17).WithMessage("'TypeWithoutAttributes.TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12()' is unsupported on 'windows'"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(39, 17).WithMessage("'TypeWithoutAttributes.TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12SupportedOnWindows13()' is unsupported on 'windows'"), @@ -339,7 +290,7 @@ public async Task MoreGuardsAroundUnSupported() using System.Runtime.Versioning; using System; -namespace PlatformCompatDemo.Bugs.GuardsAroundSupported +namespace PlatformCompatDemo.SupportedUnupported { class Caller { @@ -436,115 +387,9 @@ public static void UnsupportedCombinations() } } } +}" + TargetTypesForTest + MockAttributesCsSource + MockOperatingSystemApiSource; - public class TypeWithoutAttributes - { - [UnsupportedOSPlatform(""windows"")] - public void TypeWithoutAttributes_FunctionUnsupportedOnWindows() { } - - [UnsupportedOSPlatform(""browser"")] - public void TypeWithoutAttributes_FunctionUnsupportedOnBrowser() { } - - [UnsupportedOSPlatform(""windows10.0"")] - public void TypeWithoutAttributes_FunctionUnsupportedOnWindows10() { } - - [UnsupportedOSPlatform(""windows""), UnsupportedOSPlatform(""browser"")] - public void TypeWithoutAttributes_FunctionUnsupportedOnWindowsAndBrowser() { } - - [UnsupportedOSPlatform(""windows10.0""), UnsupportedOSPlatform(""browser"")] - public void TypeWithoutAttributes_FunctionUnsupportedOnWindows10AndBrowser() { } - - [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0"")] - public void TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11() { } - - [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0"")] - public void TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12() { } - - [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0""), SupportedOSPlatform(""windows13.0"")] - public void TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12SupportedOnWindows13() { } - - } - - [UnsupportedOSPlatform(""windows"")] - public class TypeUnsupportedOnWindows { - [UnsupportedOSPlatform(""browser"")] // more restrictive should be OK - public void TypeUnsupportedOnWindows_FunctionUnsupportedOnBrowser() { } - - [UnsupportedOSPlatform(""windows11.0"")] - public void TypeUnsupportedOnWindows_FunctionUnsupportedOnWindows11() { } - - [UnsupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""browser"")] - public void TypeUnsupportedOnWindows_FunctionUnsupportedOnWindows11AndBrowser() { } - - [SupportedOSPlatform(""windows11.0"")] - public void TypeUnsupportedOnWindows_FunctionSupportedOnWindows11() { } - - [SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0"")] - public void TypeUnsupportedOnWindows_FunctionSupportedOnWindows11UnsupportedOnWindows12() { } - - [SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0""), SupportedOSPlatform(""windows13.0"")] - public void TypeUnsupportedOnWindows_FunctionSupportedOnWindows11UnsupportedOnWindows12SupportedOnWindows13() { } - } - - [UnsupportedOSPlatform(""browser"")] - public class TypeUnsupportedOnBrowser - { - [UnsupportedOSPlatform(""windows"")] // more restrictive should be OK - public void TypeUnsupportedOnBrowser_FunctionUnsupportedOnWindows() { } - - [UnsupportedOSPlatform(""windows10.0"")] // more restrictive should be OK - public void TypeUnsupportedOnBrowser_FunctionUnsupportedOnWindows10() { } - - [SupportedOSPlatform(""browser"")] - public void TypeUnsupportedOnBrowser_FunctionSupportedOnBrowser() { } - } - - [UnsupportedOSPlatform(""windows10.0"")] - public class TypeUnsupportedOnWindows10 - { - [UnsupportedOSPlatform(""browser"")] // more restrictive should be OK - public void TypeUnsupportedOnWindows10_FunctionUnsupportedOnBrowser() { } - - [UnsupportedOSPlatform(""windows11.0"")] - public void TypeUnsupportedOnWindows10_FunctionUnsupportedOnWindows11() { } - - [UnsupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""browser"")] - public void TypeUnsupportedOnWindows10_FunctionUnsupportedOnWindows11AndBrowser() { } - } - - [UnsupportedOSPlatform(""windows""), UnsupportedOSPlatform(""browser"")] - public class TypeUnsupportedOnWindowsAndBrowser - { - [UnsupportedOSPlatform(""windows11.0"")] - public void TypeUnsupportedOnWindowsAndBrowser_FunctionUnsupportedOnWindows11() { } - } - - [UnsupportedOSPlatform(""windows10.0""), UnsupportedOSPlatform(""browser"")] - public class TypeUnsupportedOnWindows10AndBrowser - { - [UnsupportedOSPlatform(""windows11.0"")] - public void TypeUnsupportedOnWindows10AndBrowser_FunctionUnsupportedOnWindows11() { } - } - - [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0"")] - public class TypeUnsupportedOnWindowsSupportedOnWindows11 - { - [UnsupportedOSPlatform(""windows12.0"")] - public void TypeUnsupportedOnWindowsSupportedOnWindows11_FunctionUnsupportedOnWindows12() { } - - [UnsupportedOSPlatform(""windows12.0""), SupportedOSPlatform(""windows13.0"")] - public void TypeUnsupportedOnWindowsSupportedOnWindows11_FunctionUnsupportedOnWindows12SupportedOnWindows13() { } - } - - [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0"")] - public class TypeUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12 - { - [SupportedOSPlatform(""windows13.0"")] - public void TypeUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12_FunctionSupportedOnWindows13() { } - } -}" + MockAttributesCsSource + MockOperatingSystemApiSource; - - await VerifyAnalyzerAsyncCs(source, + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(17, 17).WithMessage("'TypeWithoutAttributes.TypeWithoutAttributes_FunctionUnsupportedOnWindowsAndBrowser()' is unsupported on 'browser'"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(24, 17).WithMessage("'TypeUnsupportedOnBrowser.TypeUnsupportedOnBrowser_FunctionUnsupportedOnWindows()' is unsupported on 'browser'"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(32, 54).WithMessage("'TypeUnsupportedOnWindowsAndBrowser' is unsupported on 'browser'"), @@ -630,7 +475,7 @@ public static void UnsupportedOnWindows10AndBrowser() { } } }" + MockAttributesCsSource + MockOperatingSystemApiSource; - await VerifyAnalyzerAsyncCs(source, + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(25, 17).WithMessage("'Target.UnsupportedOnWindowsAndBrowser()' is unsupported on 'browser'")); } @@ -735,7 +580,7 @@ void NotForIos12OrLater() } }" + MockAttributesCsSource + MockOperatingSystemApiSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms); } public static IEnumerable OperatingSystem_IsOsNameVersionAtLeast_MethodsTestData() @@ -1290,7 +1135,7 @@ public string M2(string option) } } " + MockAttributesCsSource + MockOperatingSystemApiSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms); } [Fact] @@ -1535,7 +1380,7 @@ public void UnsupportedWindows10() } } " + MockAttributesCsSource + MockOperatingSystemApiSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms); } [Fact] @@ -1596,7 +1441,7 @@ public void UnsupportedWindows10() } } " + MockAttributesCsSource + MockOperatingSystemApiSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms); } [Fact(Skip = "TODO: Needs to be fixed")] @@ -2107,7 +1952,7 @@ void M2() } }" + MockAttributesCsSource + MockOperatingSystemApiSource; - await VerifyAnalyzerAsyncCs(source, + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithLocation(16, 13).WithMessage("'Test.M2()' is unsupported on 'windows'")); var vbSource = @" @@ -2130,7 +1975,7 @@ Private Sub M2() End Sub End Class " + MockRuntimeApiSourceVb + MockAttributesVbSource; - await VerifyAnalyzerAsyncVb(vbSource, + await VerifyAnalyzerAsyncVb(vbSource, s_msBuildPlatforms, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithLocation(10, 13).WithMessage("'Test.M2()' is unsupported on 'Windows'")); } @@ -2467,6 +2312,182 @@ bool IsWindows11OrLater() await VerifyAnalyzerAsyncCs(source, "dotnet_code_quality.interprocedural_analysis_kind = ContextSensitive"); } + private readonly string TargetTypesForTest = @" +namespace PlatformCompatDemo.SupportedUnupported +{ + public class TypeWithoutAttributes + { + [UnsupportedOSPlatform(""windows"")] + public void TypeWithoutAttributes_FunctionUnsupportedOnWindows() { } + + [UnsupportedOSPlatform(""browser"")] + public void TypeWithoutAttributes_FunctionUnsupportedOnBrowser() { } + + [UnsupportedOSPlatform(""windows10.0"")] + public void TypeWithoutAttributes_FunctionUnsupportedOnWindows10() { } + + [UnsupportedOSPlatform(""windows""), UnsupportedOSPlatform(""browser"")] + public void TypeWithoutAttributes_FunctionUnsupportedOnWindowsAndBrowser() { } + + [UnsupportedOSPlatform(""windows10.0""), UnsupportedOSPlatform(""browser"")] + public void TypeWithoutAttributes_FunctionUnsupportedOnWindows10AndBrowser() { } + + [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0"")] + public void TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11() { } + + [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0"")] + public void TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12() { } + + [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0""), SupportedOSPlatform(""windows13.0"")] + public void TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12SupportedOnWindows13() { } + + [SupportedOSPlatform(""windows"")] + public void TypeWithoutAttributes_FunctionSupportedOnWindows() { } + + [SupportedOSPlatform(""windows10.0"")] + public void TypeWithoutAttributes_FunctionSupportedOnWindows10() { } + + [SupportedOSPlatform(""browser"")] + public void TypeWithoutAttributes_FunctionSupportedOnBrowser() { } + + [SupportedOSPlatform(""windows""), SupportedOSPlatform(""browser"")] + public void TypeWithoutAttributes_FunctionSupportedOnWindowsAndBrowser() { } + + [SupportedOSPlatform(""windows10.0""), SupportedOSPlatform(""browser"")] + public void TypeWithoutAttributes_FunctionSupportedOnWindows10AndBrowser() { } + } + + [UnsupportedOSPlatform(""windows"")] + public class TypeUnsupportedOnWindows { + [UnsupportedOSPlatform(""browser"")] // more restrictive should be OK + public void TypeUnsupportedOnWindows_FunctionUnsupportedOnBrowser() { } + + [UnsupportedOSPlatform(""windows11.0"")] + public void TypeUnsupportedOnWindows_FunctionUnsupportedOnWindows11() { } + + [UnsupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""browser"")] + public void TypeUnsupportedOnWindows_FunctionUnsupportedOnWindows11AndBrowser() { } + + [SupportedOSPlatform(""windows11.0"")] + public void TypeUnsupportedOnWindows_FunctionSupportedOnWindows11() { } + + [SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0"")] + public void TypeUnsupportedOnWindows_FunctionSupportedOnWindows11UnsupportedOnWindows12() { } + + [SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0""), SupportedOSPlatform(""windows13.0"")] + public void TypeUnsupportedOnWindows_FunctionSupportedOnWindows11UnsupportedOnWindows12SupportedOnWindows13() { } + } + + [UnsupportedOSPlatform(""browser"")] + public class TypeUnsupportedOnBrowser + { + [UnsupportedOSPlatform(""windows"")] // more restrictive should be OK + public void TypeUnsupportedOnBrowser_FunctionUnsupportedOnWindows() { } + + [UnsupportedOSPlatform(""windows10.0"")] // more restrictive should be OK + public void TypeUnsupportedOnBrowser_FunctionUnsupportedOnWindows10() { } + + [SupportedOSPlatform(""browser"")] + public void TypeUnsupportedOnBrowser_FunctionSupportedOnBrowser() { } + } + + [UnsupportedOSPlatform(""windows10.0"")] + public class TypeUnsupportedOnWindows10 + { + [UnsupportedOSPlatform(""browser"")] // more restrictive should be OK + public void TypeUnsupportedOnWindows10_FunctionUnsupportedOnBrowser() { } + + [UnsupportedOSPlatform(""windows11.0"")] + public void TypeUnsupportedOnWindows10_FunctionUnsupportedOnWindows11() { } + + [UnsupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""browser"")] + public void TypeUnsupportedOnWindows10_FunctionUnsupportedOnWindows11AndBrowser() { } + } + + [UnsupportedOSPlatform(""windows""), UnsupportedOSPlatform(""browser"")] + public class TypeUnsupportedOnWindowsAndBrowser + { + [UnsupportedOSPlatform(""windows11.0"")] + public void TypeUnsupportedOnWindowsAndBrowser_FunctionUnsupportedOnWindows11() { } + } + + [UnsupportedOSPlatform(""windows10.0""), UnsupportedOSPlatform(""browser"")] + public class TypeUnsupportedOnWindows10AndBrowser + { + [UnsupportedOSPlatform(""windows11.0"")] + public void TypeUnsupportedOnWindows10AndBrowser_FunctionUnsupportedOnWindows11() { } + } + + [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0"")] + public class TypeUnsupportedOnWindowsSupportedOnWindows11 + { + [UnsupportedOSPlatform(""windows12.0"")] + public void TypeUnsupportedOnWindowsSupportedOnWindows11_FunctionUnsupportedOnWindows12() { } + + [UnsupportedOSPlatform(""windows12.0""), SupportedOSPlatform(""windows13.0"")] + public void TypeUnsupportedOnWindowsSupportedOnWindows11_FunctionUnsupportedOnWindows12SupportedOnWindows13() { } + } + + [UnsupportedOSPlatform(""windows""), SupportedOSPlatform(""windows11.0""), UnsupportedOSPlatform(""windows12.0"")] + public class TypeUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12 + { + [SupportedOSPlatform(""windows13.0"")] + public void TypeUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12_FunctionSupportedOnWindows13() { } + } + [SupportedOSPlatform(""windows"")] + public class TypeSupportedOnWindows { + [SupportedOSPlatform(""browser"")] + public void TypeSupportedOnWindows_FunctionSupportedOnBrowser() { } + + [SupportedOSPlatform(""windows11.0"")] // more restrictive should be OK + public void TypeSupportedOnWindows_FunctionSupportedOnWindows11() { } + + [SupportedOSPlatform(""windows11.0""), SupportedOSPlatform(""browser"")] + public void TypeSupportedOnWindows_FunctionSupportedOnWindows11AndBrowser() { } + } + [SupportedOSPlatform(""browser"")] + public class TypeSupportedOnBrowser + { + [SupportedOSPlatform(""windows"")] + public void TypeSupportedOnBrowser_FunctionSupportedOnWindows() { } + + [SupportedOSPlatform(""windows11.0"")] + public void TypeSupportedOnBrowser_FunctionSupportedOnWindows11() { } + } + + [SupportedOSPlatform(""windows10.0"")] + public class TypeSupportedOnWindows10 + { + [SupportedOSPlatform(""windows"")] // less restrictive should be OK + public void TypeSupportedOnWindows10_FunctionSupportedOnWindows() { } + + [SupportedOSPlatform(""browser"")] + public void TypeSupportedOnWindows10_FunctionSupportedOnBrowser() { } + + [SupportedOSPlatform(""windows11.0"")] // more restrictive should be OK + public void TypeSupportedOnWindows10_FunctionSupportedOnWindows11() { } + + [SupportedOSPlatform(""windows11.0""), SupportedOSPlatform(""browser"")] + public void TypeSupportedOnWindows10_FunctionSupportedOnWindows11AndBrowser() { } + } + + + [SupportedOSPlatform(""windows""), SupportedOSPlatform(""browser"")] + public class TypeSupportedOnWindowsAndBrowser + { + [SupportedOSPlatform(""windows11.0"")] // more restrictive should be OK + public void TypeSupportedOnWindowsAndBrowser_FunctionSupportedOnWindows11() { } + } + + [SupportedOSPlatform(""windows10.0""), SupportedOSPlatform(""browser"")] + public class TypeSupportedOnWindows10AndBrowser + { + [SupportedOSPlatform(""windows11.0"")] // more restrictive should be OK + public void TypeSupportedOnWindows10AndBrowser_FunctionSupportedOnWindows11() { } + } +} +"; + private readonly string MockOperatingSystemApiSource = @" namespace System { diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs index 1f82ff893f..755843c515 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.cs @@ -14,9 +14,10 @@ namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests { - public partial class PlatformCompatabilityAnalyzerTests { + private const string s_msBuildPlatforms = "build_property._SupportedPlatformList=windows,browser, ios"; + [Fact(Skip = "TODO need to be fixed: Test for for wrong arguments, not sure how to report the Compiler error diagnostic")] public async Task TestOsPlatformAttributesWithNonStringArgument() { @@ -54,7 +55,7 @@ public void M1() [|WindowsOnly()|]; [|Unsupported()|]; } - [SupportedOSPlatform(""Linux"")] + [UnsupportedOSPlatform(""Linux4.1"")] public void Unsupported() { @@ -71,6 +72,7 @@ public void WindowsOnly() Imports System.Runtime.Versioning Public Class Test + Public Sub M1() [|WindowsOnly()|] [|Unsupported()|] @@ -79,7 +81,7 @@ End Sub Public Sub WindowsOnly() End Sub - + Public Sub Unsupported() End Sub @@ -100,16 +102,16 @@ public void M1() { Windows10(); Windows1_2_3_4_5(); - [|UnsupportedOSPlatformLinuxDash4_1()|]; - [|UnsupportedOSPlatformLinuxStar4_1()|]; - [|UnsupportedLinu4_1()|]; + [|UnsupportedOSPlatformIosDash4_1()|]; + [|UnsupportedOSPlatformIosStar4_1()|]; + [|SupportedLinu4_1()|]; UnsupportedOSPlatformWithNullString(); UnsupportedWithEmptyString(); - [|WindowsOnly()|]; + [|NotForWindows()|]; } - [SupportedOSPlatform(""Windows"")] - public void WindowsOnly() + [UnsupportedOSPlatform(""Windows"")] + public void NotForWindows() { } [SupportedOSPlatform(""Windows10"")] @@ -120,20 +122,20 @@ public void Windows10() public void Windows1_2_3_4_5() { } - [UnsupportedOSPlatform(""Linux-4.1"")] - public void UnsupportedOSPlatformLinuxDash4_1() + [SupportedOSPlatform(""Ios-4.1"")] + public void UnsupportedOSPlatformIosDash4_1() { } - [UnsupportedOSPlatform(""Linux*4.1"")] - public void UnsupportedOSPlatformLinuxStar4_1() + [SupportedOSPlatform(""Ios*4.1"")] + public void UnsupportedOSPlatformIosStar4_1() { } - [UnsupportedOSPlatform(null)] + [SupportedOSPlatform(null)] public void UnsupportedOSPlatformWithNullString() { } - [UnsupportedOSPlatform(""Linu4.1"")] - public void UnsupportedLinu4_1() + [SupportedOSPlatform(""Linu4.1"")] + public void SupportedLinu4_1() { } [UnsupportedOSPlatform("""")] @@ -142,7 +144,7 @@ public void UnsupportedWithEmptyString() } } " + MockAttributesCsSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms); } [Fact] @@ -157,6 +159,7 @@ public class Test public string WindowsStringProperty { get; set; } [UnsupportedOSPlatform(""Linux4.1"")] public byte UnsupportedProperty { get; } + [SupportedOSPlatform(""Linux"")] public void M1() { [|WindowsStringProperty|] = ""Hello""; @@ -178,7 +181,7 @@ public int M3(int option) } [Theory] - [MemberData(nameof(Create_AtrrbiuteProperty_WithCondtions))] + [MemberData(nameof(Create_AttributeProperty_WithCondtions))] public async Task OsDependentPropertyConditionalCheckWarns(string attribute, string property, string condition, string setter, string getter) { var source = @" @@ -202,10 +205,10 @@ public object M2(object option) } } " + MockAttributesCsSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms); } - public static IEnumerable Create_AtrrbiuteProperty_WithCondtions() + public static IEnumerable Create_AttributeProperty_WithCondtions() { yield return new object[] { "SupportedOSPlatform", "string StringProperty", " == [|StringProperty|]", @"StringProperty|] = ""Hello""", "StringProperty" }; yield return new object[] { "UnsupportedOSPlatform", "int UnsupportedProperty", " <= [|UnsupportedProperty|]", "UnsupportedProperty|] = 3", "UnsupportedProperty" }; @@ -241,7 +244,7 @@ public enum PlatformEnum NoPlatform } " + MockAttributesCsSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms); } [Fact] @@ -757,7 +760,7 @@ public static void UnsupportedOnWindowsUntilWindows11() { } } } " + MockAttributesCsSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms); } [Fact] @@ -792,7 +795,7 @@ public static void FunctionSupportedOnWindowsSameVersion() { } } } " + MockAttributesCsSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms); } [Fact] @@ -823,7 +826,7 @@ public static void FunctionSupportedOnWindowsSameVersion() { } } } " + MockAttributesCsSource; - await VerifyAnalyzerAsyncCs(source); + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms); } [Fact] @@ -879,7 +882,7 @@ public static void UnsupportedOnWindowsUntilWindows11() { } } } " + MockAttributesCsSource; - await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(13, 13) + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(13, 13) .WithMessage("'Target.SupportedOnWindowsAndBrowser()' is supported on 'browser'").WithArguments("Target.SupportedOnWindowsAndBrowser()", "browser")); } @@ -911,9 +914,9 @@ public static void TestWithBrowserUnsupported() [|Target.SupportedOnBrowser()|]; [|Target.SupportedOnWindowsAndBrowser()|]; - [|Target.UnsupportedOnWindows()|]; + Target.UnsupportedOnWindows(); // if call site has now support of it and MSbuild list not containg the platform name it will not be warned Target.UnsupportedOnBrowser(); - [|Target.UnsupportedOnWindowsAndBrowser()|]; + Target.UnsupportedOnWindowsAndBrowser(); // same here } } @@ -974,8 +977,8 @@ await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAna .WithMessage("'Some.Api1()' is supported on 'tvos' 4.0 and later").WithArguments("UnsupportedOnWindowsAndBrowser", "windows")); } - [Fact(Skip = "One failing, is this valid scenario?")] - public async Task OverridesPlatform() + [Fact] + public async Task PlatformOverrides() { var source = @" using System.Runtime.Versioning; @@ -998,12 +1001,11 @@ public void TestSupportedOnWindows() public void TestUnsupportedOnWindows() { TargetSupportedOnWindows.FunctionUnsupportedOnWindows(); - [|TargetSupportedOnWindows.FunctionUnsupportedOnBrowser()|]; // should warn supported on windows and unsupported on browser as this is deny list + [|TargetSupportedOnWindows.FunctionUnsupportedOnBrowser()|]; // should warn supported on windows ignore unsupported on browser as this is allow list - [|TargetUnsupportedOnWindows.FunctionSupportedOnBrowser();|] // Fail: should warn supported on browser, but is this valid scenario? - TargetUnsupportedOnWindows.FunctionSupportedOnWindows(); // It's unsupporting Windows at the call site, which means the call site supports all other platforms. - // It's calling into code that was NOT supported only on Windows but eventually added support, so it shouldn't raise diagnostic - } + TargetUnsupportedOnWindows.FunctionSupportedOnBrowser(); // should warn supported on browser, but is this valid scenario? => Invalid scenario, so not warn + TargetUnsupportedOnWindows.FunctionSupportedOnWindows(); // It's unsupporting Windows at the call site, which means the call site supports all other platforms. + } // It's calling into code that was NOT supported only on Windows but eventually added support, so it shouldn't raise diagnostic } [SupportedOSPlatform(""windows"")] @@ -1027,8 +1029,7 @@ public static void FunctionSupportedOnBrowser() { } } } " + MockAttributesCsSource; - await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(22, 13) - .WithMessage("FunctionUnsupportedOnBrowser' is supported on 'windows'").WithArguments("FunctionUnsupportedOnBrowser", "windows")); + await VerifyAnalyzerAsyncCs(source); } [Fact] @@ -1324,7 +1325,6 @@ await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAna .WithMessage("'C.StaticClass.LinuxMethod()' is unsupported on 'linux'").WithArguments("C.StaticClass.LinuxMethod()", "linux"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(12, 9) .WithMessage("'C.StaticClass.LinuxVersionedMethod()' is unsupported on 'linux' 4.8 and later").WithArguments("LinuxMethod", "linux")); - } [Fact] @@ -1390,7 +1390,7 @@ public void DoesNotWorkOnWindows() } } " + MockAttributesCsSource; - await VerifyAnalyzerAsyncCs(source, + await VerifyAnalyzerAsyncCs(source, s_msBuildPlatforms, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(18, 9).WithMessage("'C.DoesNotWorkOnWindows()' is unsupported on 'windows'"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsVersionRule).WithLocation(18, 9).WithMessage("'C.DoesNotWorkOnWindows()' is supported on 'windows' 10.0.1903 and later"), VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsRule).WithLocation(31, 9).WithMessage("'C.DoesNotWorkOnWindows()' is unsupported on 'windows'"), @@ -1459,6 +1459,103 @@ await VerifyAnalyzerAsyncCs(source, VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(29, 9).WithMessage("'C.WindowsOnlyMethod()' is unsupported on 'windows' 10.0.2004 and later")); } + [Fact] + public async Task CallSiteSupportedUnupportedNoMsBuildOptions() + { + var source = @" + using System.Runtime.Versioning; + +namespace PlatformCompatDemo.SupportedUnupported +{ + public class Test + { + public static void Supported() + { + var supported = new TypeWithoutAttributes(); + [|supported.TypeWithoutAttributes_FunctionSupportedOnWindows()|]; + [|supported.TypeWithoutAttributes_FunctionSupportedOnWindows10()|]; + [|supported.TypeWithoutAttributes_FunctionSupportedOnWindows10AndBrowser()|]; + + var supportedOnWindows = [|new TypeSupportedOnWindows()|]; + [|supportedOnWindows.TypeSupportedOnWindows_FunctionSupportedOnBrowser()|]; + [|supportedOnWindows.TypeSupportedOnWindows_FunctionSupportedOnWindows11AndBrowser()|]; + + var supportedOnBrowser = [|new TypeSupportedOnBrowser()|]; + [|supportedOnBrowser.TypeSupportedOnBrowser_FunctionSupportedOnWindows()|]; + + var supportedOnWindows10 = [|new TypeSupportedOnWindows10()|]; + [|supportedOnWindows10.TypeSupportedOnWindows10_FunctionSupportedOnBrowser()|]; + + var supportedOnWindowsAndBrowser = [|new TypeSupportedOnWindowsAndBrowser()|]; + [|supportedOnWindowsAndBrowser.TypeSupportedOnWindowsAndBrowser_FunctionSupportedOnWindows11()|]; + } + + public static void Unsupported() + { + var unsupported = new TypeWithoutAttributes(); + unsupported.TypeWithoutAttributes_FunctionUnsupportedOnWindows(); + unsupported.TypeWithoutAttributes_FunctionUnsupportedOnBrowser(); + unsupported.TypeWithoutAttributes_FunctionUnsupportedOnWindows10(); + unsupported.TypeWithoutAttributes_FunctionUnsupportedOnWindowsAndBrowser(); + unsupported.TypeWithoutAttributes_FunctionUnsupportedOnWindows10AndBrowser(); + + var unsupportedOnWindows = new TypeUnsupportedOnWindows(); + unsupportedOnWindows.TypeUnsupportedOnWindows_FunctionUnsupportedOnBrowser(); + unsupportedOnWindows.TypeUnsupportedOnWindows_FunctionUnsupportedOnWindows11(); + unsupportedOnWindows.TypeUnsupportedOnWindows_FunctionUnsupportedOnWindows11AndBrowser(); + + var unsupportedOnBrowser = new TypeUnsupportedOnBrowser(); + unsupportedOnBrowser.TypeUnsupportedOnBrowser_FunctionUnsupportedOnWindows(); + unsupportedOnBrowser.TypeUnsupportedOnBrowser_FunctionUnsupportedOnWindows10(); + + var unsupportedOnWindows10 = new TypeUnsupportedOnWindows10(); + unsupportedOnWindows10.TypeUnsupportedOnWindows10_FunctionUnsupportedOnBrowser(); + unsupportedOnWindows10.TypeUnsupportedOnWindows10_FunctionUnsupportedOnWindows11(); + unsupportedOnWindows10.TypeUnsupportedOnWindows10_FunctionUnsupportedOnWindows11AndBrowser(); + + var unsupportedOnWindowsAndBrowser = new TypeUnsupportedOnWindowsAndBrowser(); + unsupportedOnWindowsAndBrowser.TypeUnsupportedOnWindowsAndBrowser_FunctionUnsupportedOnWindows11(); + + var unsupportedOnWindows10AndBrowser = new TypeUnsupportedOnWindows10AndBrowser(); + unsupportedOnWindows10AndBrowser.TypeUnsupportedOnWindows10AndBrowser_FunctionUnsupportedOnWindows11(); + } + + public static void UnsupportedCombinations() // no any diagnostics as it is deny list + { + var withoutAttributes = new TypeWithoutAttributes(); + withoutAttributes.TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11(); + withoutAttributes.TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12(); + withoutAttributes.TypeWithoutAttributes_FunctionUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12SupportedOnWindows13(); + + var unsupportedOnWindows = new TypeUnsupportedOnWindows(); + unsupportedOnWindows.TypeUnsupportedOnWindows_FunctionSupportedOnWindows11(); + unsupportedOnWindows.TypeUnsupportedOnWindows_FunctionSupportedOnWindows11UnsupportedOnWindows12(); + unsupportedOnWindows.TypeUnsupportedOnWindows_FunctionSupportedOnWindows11UnsupportedOnWindows12SupportedOnWindows13(); + + var unsupportedOnBrowser = new TypeUnsupportedOnBrowser(); + unsupportedOnBrowser.TypeUnsupportedOnBrowser_FunctionSupportedOnBrowser(); + + var unsupportedOnWindowsSupportedOnWindows11 = new TypeUnsupportedOnWindowsSupportedOnWindows11(); + unsupportedOnWindowsSupportedOnWindows11.TypeUnsupportedOnWindowsSupportedOnWindows11_FunctionUnsupportedOnWindows12(); + unsupportedOnWindowsSupportedOnWindows11.TypeUnsupportedOnWindowsSupportedOnWindows11_FunctionUnsupportedOnWindows12SupportedOnWindows13(); + + var unsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12 = new TypeUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12(); + unsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12.TypeUnsupportedOnWindowsSupportedOnWindows11UnsupportedOnWindows12_FunctionSupportedOnWindows13(); + } + } +} +" + TargetTypesForTest + MockAttributesCsSource; + + await VerifyAnalyzerAsyncCs(source, + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithLocation(13, 13).WithMessage("'TypeWithoutAttributes.TypeWithoutAttributes_FunctionSupportedOnWindows10AndBrowser()' is supported on 'browser'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithLocation(16, 13).WithMessage("'TypeSupportedOnWindows.TypeSupportedOnWindows_FunctionSupportedOnBrowser()' is supported on 'browser'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.SupportedOsRule).WithLocation(17, 13).WithMessage("'TypeSupportedOnWindows.TypeSupportedOnWindows_FunctionSupportedOnWindows11AndBrowser()' is supported on 'browser'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(20, 13).WithMessage("'TypeSupportedOnBrowser.TypeSupportedOnBrowser_FunctionSupportedOnWindows()' is supported on 'browser'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(23, 13).WithMessage("'TypeSupportedOnWindows10.TypeSupportedOnWindows10_FunctionSupportedOnBrowser()' is supported on 'browser'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(25, 48).WithMessage("'TypeSupportedOnWindowsAndBrowser' is supported on 'browser'"), + VerifyCS.Diagnostic(PlatformCompatabilityAnalyzer.UnsupportedOsVersionRule).WithLocation(26, 13).WithMessage("'TypeSupportedOnWindowsAndBrowser.TypeSupportedOnWindowsAndBrowser_FunctionSupportedOnWindows11()' is supported on 'browser'")); + } + private static VerifyCS.Test PopulateTestCs(string sourceCode, params DiagnosticResult[] expected) { var test = new VerifyCS.Test @@ -1475,6 +1572,13 @@ private static VerifyCS.Test PopulateTestCs(string sourceCode, params Diagnostic private static async Task VerifyAnalyzerAsyncCs(string sourceCode, params DiagnosticResult[] expectedDiagnostics) => await PopulateTestCs(sourceCode, expectedDiagnostics).RunAsync(); + private static async Task VerifyAnalyzerAsyncCs(string sourceCode, string editorconfigText, params DiagnosticResult[] expectedDiagnostics) + { + var test = PopulateTestCs(sourceCode, expectedDiagnostics); + test.TestState.AdditionalFiles.Add((".editorconfig", editorconfigText)); + await test.RunAsync(); + } + private static async Task VerifyAnalyzerAsyncCs(string sourceCode, string editorconfigText) { var test = PopulateTestCs(sourceCode); @@ -1485,6 +1589,13 @@ private static async Task VerifyAnalyzerAsyncCs(string sourceCode, string editor private static async Task VerifyAnalyzerAsyncVb(string sourceCode, params DiagnosticResult[] expectedDiagnostics) => await PopulateTestVb(sourceCode, expectedDiagnostics).RunAsync(); + private static async Task VerifyAnalyzerAsyncVb(string sourceCode, string editorconfigText, params DiagnosticResult[] expectedDiagnostics) + { + var test = PopulateTestVb(sourceCode, expectedDiagnostics); + test.TestState.AdditionalFiles.Add((".editorconfig", editorconfigText)); + await test.RunAsync(); + } + private static VerifyVB.Test PopulateTestVb(string sourceCode, params DiagnosticResult[] expected) { var test = new VerifyVB.Test @@ -1492,6 +1603,7 @@ private static VerifyVB.Test PopulateTestVb(string sourceCode, params Diagnostic TestCode = sourceCode, ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp50, MarkupOptions = MarkupOptions.UseFirstDescriptor, + TestState = { }, }; test.ExpectedDiagnostics.AddRange(expected); return test; diff --git a/src/Utilities/Compiler/Options/MSBuildItemOptionNames.cs b/src/Utilities/Compiler/Options/MSBuildItemOptionNames.cs index 54a0612cb8..91e9faed8a 100644 --- a/src/Utilities/Compiler/Options/MSBuildItemOptionNames.cs +++ b/src/Utilities/Compiler/Options/MSBuildItemOptionNames.cs @@ -1,6 +1,7 @@ // 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.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -47,7 +48,15 @@ public static ImmutableArray ParseItemOptionValue(string? itemOptionValu return ImmutableArray.Empty; } - return itemOptionValue.Split(s_itemMetadataValuesSeparators, StringSplitOptions.RemoveEmptyEntries).ToImmutableArray(); + return ProduceTrimmedArray(itemOptionValue).ToImmutableArray(); + } + + private static IEnumerable ProduceTrimmedArray(string itemOptionValue) + { + foreach (var platform in itemOptionValue.Split(s_itemMetadataValuesSeparators, StringSplitOptions.RemoveEmptyEntries)) + { + yield return platform.Trim(); + } } } -} +} \ No newline at end of file From 865071dd007b136d8b15cf85b122bd4010a4db38 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Sat, 22 Aug 2020 22:24:09 -0700 Subject: [PATCH 43/48] Use local instead of static field --- .../PlatformCompatabilityAnalyzer.cs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index cadbd4ec00..33f9fb221d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -32,7 +32,6 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private static readonly ImmutableArray s_platformCheckMethodNames = ImmutableArray.Create(IsOSPlatformVersionAtLeast, IsOSPlatform, IsBrowser, IsLinux, IsFreeBSD, IsFreeBSDVersionAtLeast, IsAndroid, IsAndroidVersionAtLeast, IsIOS, IsIOSVersionAtLeast, IsMacOS, IsMacOSVersionAtLeast, IsTvOS, IsTvOSVersionAtLeast, IsWatchOS, IsWatchOSVersionAtLeast, IsWindows, IsWindowsVersionAtLeast); private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(SupportedOSPlatformAttribute, UnsupportedOSPlatformAttribute); - private static ImmutableArray s_supportedMsBuildPlatforms; 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)); @@ -123,7 +122,7 @@ public override void Initialize(AnalysisContext context) return; } - s_supportedMsBuildPlatforms = GetSupportedPlatforms(context.Options, context.Compilation, context.CancellationToken); + var msBuildPlatforms = GetSupportedPlatforms(context.Options, context.Compilation, context.CancellationToken); var runtimeIsOSPlatformMethod = runtimeInformationType.GetMembers().OfType().Where(m => IsOSPlatform == m.Name && m.IsStatic && @@ -134,7 +133,7 @@ public override void Initialize(AnalysisContext context) var guardMethods = GetRuntimePlatformGuardMethods(runtimeIsOSPlatformMethod, operatingSystemType!); var platformSpecificMembers = new ConcurrentDictionary?>(); - context.RegisterOperationBlockStartAction(context => AnalyzeOperationBlock(context, guardMethods, osPlatformType, platformSpecificMembers)); + context.RegisterOperationBlockStartAction(context => AnalyzeOperationBlock(context, guardMethods, osPlatformType, platformSpecificMembers, msBuildPlatforms)); }); static ImmutableArray GetRuntimePlatformGuardMethods(IMethodSymbol runtimeIsOSPlatformMethod, INamedTypeSymbol operatingSystemType) @@ -153,13 +152,14 @@ private void AnalyzeOperationBlock( OperationBlockStartAnalysisContext context, ImmutableArray guardMethods, INamedTypeSymbol osPlatformType, - ConcurrentDictionary?> platformSpecificMembers) + ConcurrentDictionary?> platformSpecificMembers, + ImmutableArray msBuildPlatforms) { var platformSpecificOperations = PooledConcurrentDictionary>.GetInstance(); context.RegisterOperationAction(context => { - AnalyzeOperation(context.Operation, context, platformSpecificOperations, platformSpecificMembers); + AnalyzeOperation(context.Operation, context, platformSpecificOperations, platformSpecificMembers, msBuildPlatforms); }, OperationKind.MethodReference, OperationKind.EventReference, @@ -504,7 +504,7 @@ private static void ReportUnsupportedDiagnostic(IOperation operation, OperationB private static void AnalyzeOperation(IOperation operation, OperationAnalysisContext context, PooledConcurrentDictionary> platformSpecificOperations, - ConcurrentDictionary?> platformSpecificMembers) + ConcurrentDictionary?> platformSpecificMembers, ImmutableArray msBuildPlatforms) { var symbol = GetOperationSymbol(operation); @@ -517,14 +517,14 @@ private static void AnalyzeOperation(IOperation operation, OperationAnalysisCont { if (TryGetOrCreatePlatformAttributes(context.ContainingSymbol, platformSpecificMembers, out var callSiteAttributes)) { - if (IsNotSuppressedByCallSite(operationAttributes, callSiteAttributes, out var notSuppressedAttributes)) + if (IsNotSuppressedByCallSite(operationAttributes, callSiteAttributes, msBuildPlatforms, out var notSuppressedAttributes)) { platformSpecificOperations.TryAdd(operation, notSuppressedAttributes); } } else { - if (TryCopyAttributesNotSuppressedByMsBuild(operationAttributes, out var copiedAttributes)) + if (TryCopyAttributesNotSuppressedByMsBuild(operationAttributes, msBuildPlatforms, out var copiedAttributes)) { platformSpecificOperations.TryAdd(operation, copiedAttributes); } @@ -532,12 +532,13 @@ private static void AnalyzeOperation(IOperation operation, OperationAnalysisCont } } - private static bool TryCopyAttributesNotSuppressedByMsBuild(SmallDictionary operationAttributes, out SmallDictionary 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) || s_supportedMsBuildPlatforms.IndexOf(platformName, 0, StringComparer.OrdinalIgnoreCase) != -1) + if (AllowList(attributes) || msBuildPlatforms.IndexOf(platformName, 0, StringComparer.OrdinalIgnoreCase) != -1) { copiedAttributes.Add(platformName, CopyAllAttributes(new PlatformAttributes(), attributes)); @@ -569,7 +570,8 @@ private static SmallDictionary CopyAttributes(SmallD /// 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, out SmallDictionary notSuppressedAttributes) + private static bool IsNotSuppressedByCallSite(SmallDictionary operationAttributes, SmallDictionary callSiteAttributes, + ImmutableArray msBuildPlatforms, out SmallDictionary notSuppressedAttributes) { notSuppressedAttributes = new SmallDictionary(StringComparer.OrdinalIgnoreCase); bool? supportedOnlyList = null; @@ -650,7 +652,7 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary DenyList(ca.Value))) { diagnosticAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone(); @@ -687,7 +689,7 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary 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 From 2dd62408e827820acf4e9102dd592d9b8acfafbc Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Sun, 23 Aug 2020 23:45:20 -0700 Subject: [PATCH 44/48] Fix and enable some failing tests --- ...mCompatabilityAnalyzer.OperationVisitor.cs | 4 +++ .../PlatformCompatabilityAnalyzer.cs | 26 +++++++------------ ...tabilityAnalyzerTests.GuardedCallsTests.cs | 13 +++++----- .../Extensions/IOperationExtensions.cs | 21 --------------- .../DataFlow/DataFlowAnalysisResult.cs | 4 +-- 5 files changed, 23 insertions(+), 45 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs index cbd54dabe2..a042aebc2b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs @@ -63,6 +63,10 @@ public override GlobalFlowStateAnalysisValueSet VisitEventReference(IEventRefere { 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.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 33f9fb221d..8ecd4e1696 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -201,9 +201,9 @@ private void AnalyzeOperationBlock( if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown) { - if (platformSpecificOperation.TryGetContainingLocalOrLambdaFunctionSymbol(out var containingSymbol)) + if (platformSpecificOperation.IsWithinLambdaOrLocalFunction()) { - var localResults = analysisResult.TryGetInterproceduralResultByDefinition(containingSymbol); + var localResults = analysisResult.TryGetInterproceduralResultByDefinition(); if (localResults != null) { var hasKnownUnguardedValue = false; @@ -588,14 +588,12 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary operation.GetAncestor(OperationKind.AnonymousFunction) != null || operation.GetAncestor(OperationKind.LocalFunction) != null; - public static bool TryGetContainingLocalOrLambdaFunctionSymbol(this IOperation operation, [NotNullWhen(true)] out IMethodSymbol? containingSymbol) - { - var localOperation = operation.GetAncestor(OperationKind.LocalFunction); - if (localOperation != null) - { - containingSymbol = localOperation.Symbol; - return true; - } - else - { - var anonymousOperation = operation.GetAncestor(OperationKind.AnonymousFunction); - if (anonymousOperation != null) - { - containingSymbol = anonymousOperation.Symbol; - return true; - } - } - containingSymbol = null; - return false; - } - public static ITypeSymbol? GetPatternType(this IPatternOperation pattern) { return pattern switch diff --git a/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs b/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs index 58a112692d..7565c57362 100644 --- a/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs +++ b/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs @@ -135,11 +135,11 @@ public TAbstractAnalysisValue this[IOperation operation] return null; } - internal IEnumerable>? TryGetInterproceduralResultByDefinition(IMethodSymbol symbol) + internal IEnumerable>? TryGetInterproceduralResultByDefinition() { foreach (var kvp in _interproceduralResultsMap) { - if (kvp.Key is IInvocationOperation iOperation && iOperation.TargetMethod.Equals(symbol)) + if (kvp.Key is IInvocationOperation) { yield return (DataFlowAnalysisResult)kvp.Value; } From 5335d5849030e80ca9ff2028f7f7f45711766984 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 24 Aug 2020 00:53:26 -0700 Subject: [PATCH 45/48] Match string pattern instead of constants for guard method names --- .../PlatformCompatabilityAnalyzer.Value.cs | 60 ++++++------------- .../PlatformCompatabilityAnalyzer.cs | 37 ++++-------- 2 files changed, 31 insertions(+), 66 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs index 5b2d38eaa1..40e7544b30 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -2,17 +2,17 @@ using System; using System.Collections.Immutable; -using Analyzer.Utilities.PooledObjects; +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; -using Microsoft.CodeAnalysis; -using System.Diagnostics.CodeAnalysis; -using Analyzer.Utilities; -using System.Diagnostics; -using Analyzer.Utilities.Extensions; namespace Microsoft.NetCore.Analyzers.InteropServices { @@ -20,15 +20,8 @@ namespace Microsoft.NetCore.Analyzers.InteropServices public sealed partial class PlatformCompatabilityAnalyzer { - private const string Browser = nameof(Browser); - private const string Linux = nameof(Linux); - private const string FreeBSD = nameof(FreeBSD); - private const string Android = nameof(Android); - private const string IOS = nameof(IOS); - private const string MacOS = nameof(MacOS); - private const string TvOS = nameof(TvOS); - private const string WatchOS = nameof(WatchOS); - private const string Windows = nameof(Windows); + private const string IsPrefix = "Is"; + private const string OptionalSuffix = "VersionAtLeast"; private readonly struct RuntimeMethodValue : IAbstractAnalysisValue, IEquatable { @@ -88,7 +81,7 @@ public static bool TryDecode( 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); + Debug.Assert(invokedPlatformCheckMethod.Name == "IsOSPlatformVersionAtLeast"); info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, literal.ConstantValue.Value.ToString(), version, negated: false); return true; } @@ -96,7 +89,7 @@ public static bool TryDecode( else if (literal.Type?.SpecialType == SpecialType.System_Int32) { // Accelerators like OperatingSystem.IsPlatformNameVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) - var platformName = SwitchVersionedPlatformName(invokedPlatformCheckMethod.Name); + var platformName = SwitchPlatformName(invokedPlatformCheckMethod.Name); if (platformName != null && TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var version)) { @@ -111,33 +104,16 @@ public static bool TryDecode( return false; } - private static string? SwitchVersionedPlatformName(string methodName) - => methodName switch + private static string? SwitchPlatformName(string methodName) { - IsWindowsVersionAtLeast => Windows, - IsMacOSVersionAtLeast => MacOS, - IsIOSVersionAtLeast => IOS, - IsAndroidVersionAtLeast => Android, - IsFreeBSDVersionAtLeast => FreeBSD, - IsTvOSVersionAtLeast => TvOS, - IsWatchOSVersionAtLeast => WatchOS, - _ => null - }; + if (!methodName.StartsWith(IsPrefix, StringComparison.Ordinal)) + return null; - private static string? SwitchPlatformName(string methodName) - => methodName switch - { - IsWindows => Windows, - IsLinux => Linux, - IsMacOS => MacOS, - IsIOS => IOS, - IsBrowser => Browser, - IsAndroid => Android, - IsFreeBSD => FreeBSD, - IsTvOS => TvOS, - IsWatchOS => WatchOS, - _ => null - }; + if (methodName.EndsWith(OptionalSuffix, StringComparison.Ordinal)) + return methodName.Substring(2, methodName.Length - 2 - OptionalSuffix.Length); + + return methodName.Substring(2); + } private static bool TryDecodeRuntimeInformationIsOSPlatform( IOperation argumentValue, diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 8ecd4e1696..33b70fd3c8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -29,8 +29,6 @@ namespace Microsoft.NetCore.Analyzers.InteropServices public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA1416"; - private static readonly ImmutableArray s_platformCheckMethodNames = ImmutableArray.Create(IsOSPlatformVersionAtLeast, IsOSPlatform, IsBrowser, IsLinux, IsFreeBSD, IsFreeBSDVersionAtLeast, - IsAndroid, IsAndroidVersionAtLeast, IsIOS, IsIOSVersionAtLeast, IsMacOS, IsMacOSVersionAtLeast, IsTvOS, IsTvOSVersionAtLeast, IsWatchOS, IsWatchOSVersionAtLeast, IsWindows, IsWindowsVersionAtLeast); 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)); @@ -46,24 +44,7 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private const string UnsupportedOSPlatformAttribute = nameof(UnsupportedOSPlatformAttribute); // Platform guard method names - private const string IsOSPlatformVersionAtLeast = nameof(IsOSPlatformVersionAtLeast); private const string IsOSPlatform = nameof(IsOSPlatform); - private const string IsBrowser = nameof(IsBrowser); - private const string IsLinux = nameof(IsLinux); - private const string IsFreeBSD = nameof(IsFreeBSD); - private const string IsFreeBSDVersionAtLeast = nameof(IsFreeBSDVersionAtLeast); - private const string IsAndroid = nameof(IsAndroid); - private const string IsAndroidVersionAtLeast = nameof(IsAndroidVersionAtLeast); - private const string IsIOS = nameof(IsIOS); - private const string IsIOSVersionAtLeast = nameof(IsIOSVersionAtLeast); - private const string IsMacOS = nameof(IsMacOS); - private const string IsMacOSVersionAtLeast = nameof(IsMacOSVersionAtLeast); - private const string IsTvOS = nameof(IsTvOS); - private const string IsTvOSVersionAtLeast = nameof(IsTvOSVersionAtLeast); - private const string IsWatchOS = nameof(IsWatchOS); - private const string IsWatchOSVersionAtLeast = nameof(IsWatchOSVersionAtLeast); - private const string IsWindows = nameof(IsWindows); - private const string IsWindowsVersionAtLeast = nameof(IsWindowsVersionAtLeast); internal static DiagnosticDescriptor SupportedOsVersionRule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, @@ -130,24 +111,32 @@ public override void Initialize(AnalysisContext context) m.Parameters.Length == 1 && m.Parameters[0].Type.Equals(osPlatformType)).FirstOrDefault(); - var guardMethods = GetRuntimePlatformGuardMethods(runtimeIsOSPlatformMethod, operatingSystemType!); + var guardMethods = GetOperatingSystemGuardMethods(runtimeIsOSPlatformMethod, operatingSystemType!); var platformSpecificMembers = new ConcurrentDictionary?>(); context.RegisterOperationBlockStartAction(context => AnalyzeOperationBlock(context, guardMethods, osPlatformType, platformSpecificMembers, msBuildPlatforms)); }); - static ImmutableArray GetRuntimePlatformGuardMethods(IMethodSymbol runtimeIsOSPlatformMethod, INamedTypeSymbol operatingSystemType) + static ImmutableArray GetOperatingSystemGuardMethods(IMethodSymbol runtimeIsOSPlatformMethod, INamedTypeSymbol operatingSystemType) { return operatingSystemType.GetMembers().OfType().Where(m => - s_platformCheckMethodNames.Contains(m.Name) && m.IsStatic && - m.ReturnType.SpecialType == SpecialType.System_Boolean).ToImmutableArray(). + m.ReturnType.SpecialType == SpecialType.System_Boolean && + (IsOSPlatform == m.Name) || NameAndParametersValid(m)). + ToImmutableArray(). Add(runtimeIsOSPlatformMethod); } static ImmutableArray GetSupportedPlatforms(AnalyzerOptions options, Compilation compilation, CancellationToken cancellationToken) => options.GetMSBuildItemMetadataValues(MSBuildItemOptionNames.SupportedPlatform, compilation, cancellationToken); } + + private static bool NameAndParametersValid(IMethodSymbol method) + { + return method.Name.StartsWith(IsPrefix, StringComparison.Ordinal) && + (method.Parameters.Length == 0 || method.Name.EndsWith(OptionalSuffix, StringComparison.Ordinal)); + } + private void AnalyzeOperationBlock( OperationBlockStartAnalysisContext context, ImmutableArray guardMethods, @@ -620,7 +609,7 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary Date: Mon, 24 Aug 2020 12:13:08 -0700 Subject: [PATCH 46/48] Small refactoring updates --- .../PlatformCompatabilityAnalyzer.Data.cs | 36 +----- ...mCompatabilityAnalyzer.OperationVisitor.cs | 2 +- .../PlatformCompatabilityAnalyzer.Value.cs | 54 ++++---- .../PlatformCompatabilityAnalyzer.cs | 119 +++++++++++------- ...tabilityAnalyzerTests.GuardedCallsTests.cs | 3 - .../DataFlow/DataFlowAnalysisResult.cs | 2 +- 6 files changed, 107 insertions(+), 109 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs index 86eb780ebc..b7015d064d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Data.cs @@ -1,30 +1,26 @@ // 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.Diagnostics.CodeAnalysis; namespace Microsoft.NetCore.Analyzers.InteropServices { public sealed partial class PlatformCompatabilityAnalyzer { /// - /// Class used for keeping platform information of an API, all are optional properties + /// 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) + /// 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 /// - /// I wouldn't expect that [ObsoletedInOSPlatform] attribute used more than once (like obsoleted once, supported back and obsoleted again), - /// so we will keep only one property for that, if any more attrbite found in the API parents chain we will keep the one with lowest versions - /// /// 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 - /// - Obsoleted - keeps lowest version of [ObsoletedInOSPlatform] attrbite found /// private class PlatformAttributes { @@ -35,29 +31,5 @@ private class PlatformAttributes public bool HasAttribute() => SupportedFirst != null || UnsupportedFirst != null || SupportedSecond != null || UnsupportedSecond != null; } - - 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; - } } } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs index a042aebc2b..f757236e0c 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.OperationVisitor.cs @@ -36,7 +36,7 @@ public override GlobalFlowStateAnalysisValueSet VisitInvocation_NonLambdaOrDeleg if (_platformCheckMethods.Contains(method.OriginalDefinition)) { - return RuntimeMethodValue.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; } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs index 40e7544b30..3c0b99e6b8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -20,12 +20,9 @@ namespace Microsoft.NetCore.Analyzers.InteropServices public sealed partial class PlatformCompatabilityAnalyzer { - private const string IsPrefix = "Is"; - private const string OptionalSuffix = "VersionAtLeast"; - - private readonly struct RuntimeMethodValue : IAbstractAnalysisValue, IEquatable + private readonly struct PlatformMethodValue : IAbstractAnalysisValue, IEquatable { - private RuntimeMethodValue(string invokedPlatformCheckMethodName, string platformPropertyName, Version version, bool negated) + private PlatformMethodValue(string invokedPlatformCheckMethodName, string platformPropertyName, Version version, bool negated) { InvokedMethodName = invokedPlatformCheckMethodName ?? throw new ArgumentNullException(nameof(invokedPlatformCheckMethodName)); PlatformName = platformPropertyName ?? throw new ArgumentNullException(nameof(platformPropertyName)); @@ -39,22 +36,21 @@ private RuntimeMethodValue(string invokedPlatformCheckMethodName, string platfor public bool Negated { get; } public IAbstractAnalysisValue GetNegatedValue() - => new RuntimeMethodValue(InvokedMethodName, PlatformName, Version, !Negated); + => new PlatformMethodValue(InvokedMethodName, PlatformName, Version, !Negated); public static bool TryDecode( IMethodSymbol invokedPlatformCheckMethod, ImmutableArray arguments, ValueContentAnalysisResult? valueContentAnalysisResult, INamedTypeSymbol osPlatformType, - [NotNullWhen(returnValue: true)] out RuntimeMethodValue? info) + [NotNullWhen(returnValue: true)] out PlatformMethodValue? info) { // Accelerators like OperatingSystem.IsPlatformName() if (arguments.IsEmpty) { - var platformName = SwitchPlatformName(invokedPlatformCheckMethod.Name); - if (platformName != null) + if (TryExtractPlatformName(invokedPlatformCheckMethod.Name, out var platformName)) { - info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, new Version(0, 0), negated: false); + info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, platformName, new Version(0, 0), negated: false); return true; } } @@ -62,7 +58,7 @@ public static bool TryDecode( { if (TryDecodeRuntimeInformationIsOSPlatform(arguments[0].Value, osPlatformType, out string? osPlatformName)) { - info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, osPlatformName, new Version(0, 0), negated: false); + info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, osPlatformName, new Version(0, 0), negated: false); return true; } @@ -75,25 +71,24 @@ public static bool TryDecode( if (invokedPlatformCheckMethod.Name == IsOSPlatform && TryParsePlatformNameAndVersion(literal.ConstantValue.Value.ToString(), out string platformName, out Version? version)) { - info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); + 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 RuntimeMethodValue(invokedPlatformCheckMethod.Name, literal.ConstantValue.Value.ToString(), version, negated: false); + 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) - var platformName = SwitchPlatformName(invokedPlatformCheckMethod.Name); - - if (platformName != null && TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var version)) + if (TryExtractPlatformName(invokedPlatformCheckMethod.Name, out var platformName) && + TryDecodeOSVersion(arguments, valueContentAnalysisResult, out var version)) { - info = new RuntimeMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); + info = new PlatformMethodValue(invokedPlatformCheckMethod.Name, platformName, version, negated: false); return true; } } @@ -104,15 +99,22 @@ public static bool TryDecode( return false; } - private static string? SwitchPlatformName(string methodName) + private static bool TryExtractPlatformName(string methodName, [NotNullWhen(true)] out string? platformName) { if (!methodName.StartsWith(IsPrefix, StringComparison.Ordinal)) - return null; + { + platformName = null; + return false; + } if (methodName.EndsWith(OptionalSuffix, StringComparison.Ordinal)) - return methodName.Substring(2, methodName.Length - 2 - OptionalSuffix.Length); + { + platformName = methodName.Substring(2, methodName.Length - 2 - OptionalSuffix.Length); + return true; + } - return methodName.Substring(2); + platformName = methodName.Substring(2); + return true; } private static bool TryDecodeRuntimeInformationIsOSPlatform( @@ -212,27 +214,27 @@ public override string ToString() return result; } - public bool Equals(RuntimeMethodValue other) + 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 RuntimeMethodValue otherInfo && Equals(otherInfo); + => 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 RuntimeMethodValue otherInfo && Equals(otherInfo); + => other is PlatformMethodValue otherInfo && Equals(otherInfo); - public static bool operator ==(RuntimeMethodValue left, RuntimeMethodValue right) + public static bool operator ==(PlatformMethodValue left, PlatformMethodValue right) { return left.Equals(right); } - public static bool operator !=(RuntimeMethodValue left, RuntimeMethodValue right) + public static bool operator !=(PlatformMethodValue left, PlatformMethodValue right) { return !(left == right); } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 33b70fd3c8..3b921783a4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -12,6 +12,7 @@ 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; @@ -43,8 +44,10 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer private const string SupportedOSPlatformAttribute = nameof(SupportedOSPlatformAttribute); private const string UnsupportedOSPlatformAttribute = nameof(UnsupportedOSPlatformAttribute); - // Platform guard method names + // 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, @@ -81,7 +84,9 @@ public sealed partial class PlatformCompatabilityAnalyzer : DiagnosticAnalyzer description: s_localizableDescription, isPortedFxCopRule: false, isDataflowRule: false); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(SupportedOsRule, SupportedOsVersionRule, UnsupportedOsRule, UnsupportedOsVersionRule); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create( + SupportedOsRule, SupportedOsVersionRule, UnsupportedOsRule, UnsupportedOsVersionRule); public override void Initialize(AnalysisContext context) { @@ -129,12 +134,9 @@ static ImmutableArray GetOperatingSystemGuardMethods(IMethodSymbo static ImmutableArray GetSupportedPlatforms(AnalyzerOptions options, Compilation compilation, CancellationToken cancellationToken) => options.GetMSBuildItemMetadataValues(MSBuildItemOptionNames.SupportedPlatform, compilation, cancellationToken); - } - private static bool NameAndParametersValid(IMethodSymbol method) - { - return method.Name.StartsWith(IsPrefix, StringComparison.Ordinal) && - (method.Parameters.Length == 0 || method.Name.EndsWith(OptionalSuffix, StringComparison.Ordinal)); + static bool NameAndParametersValid(IMethodSymbol method) => method.Name.StartsWith(IsPrefix, StringComparison.Ordinal) && + (method.Parameters.Length == 0 || method.Name.EndsWith(OptionalSuffix, StringComparison.Ordinal)); } private void AnalyzeOperationBlock( @@ -188,32 +190,8 @@ private void AnalyzeOperationBlock( { var value = analysisResult[platformSpecificOperation.Kind, platformSpecificOperation.Syntax]; - if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown) - { - if (platformSpecificOperation.IsWithinLambdaOrLocalFunction()) - { - var localResults = analysisResult.TryGetInterproceduralResultByDefinition(); - if (localResults != null) - { - var hasKnownUnguardedValue = false; - foreach (var localResult in localResults) - { - var localValue = localResult[platformSpecificOperation.Kind, platformSpecificOperation.Syntax]; - if (localValue.Kind == GlobalFlowStateAnalysisValueSetKind.Known && IsKnownValueGuarded(attributes, localValue)) - { - hasKnownUnguardedValue = true; - break; - } - } - - if (hasKnownUnguardedValue) - { - continue; - } - } - } - } - else if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Known && IsKnownValueGuarded(attributes, value)) + if ((value.Kind == GlobalFlowStateAnalysisValueSetKind.Known && IsKnownValueGuarded(attributes, value)) || + (value.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown && HasInterproceduralResult(platformSpecificOperation, attributes, analysisResult))) { continue; } @@ -237,6 +215,28 @@ private void AnalyzeOperationBlock( }); } + 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()) @@ -272,7 +272,7 @@ static bool IsKnownValueGuarded( // 'GlobalFlowStateAnalysisValueSet.AnalysisValues' represent the && of values. foreach (var analysisValue in value.AnalysisValues) { - if (analysisValue is RuntimeMethodValue info) + if (analysisValue is PlatformMethodValue info) { if (attributes.TryGetValue(info.PlatformName, out var attribute)) { @@ -282,10 +282,8 @@ static bool IsKnownValueGuarded( { if (attribute.UnsupportedFirst >= info.Version) { - if (attribute.SupportedFirst != null && - attribute.SupportedFirst >= attribute.UnsupportedFirst) + if (DenyList(attribute)) { - // deny list, no need further check attribute.SupportedFirst = null; attribute.SupportedSecond = null; attribute.UnsupportedSecond = null; @@ -469,15 +467,15 @@ private static void ReportDiagnostics(IOperation operation, SmallDictionary + 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)); - private static void ReportUnsupportedDiagnostic(IOperation operation, OperationBlockAnalysisContext context, string name, string platformName, string? version = null) => + 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(); @@ -533,6 +531,7 @@ private static bool TryCopyAttributesNotSuppressedByMsBuild(SmallDictionary CopyAttributes(SmallD { copy.Add(platformName, CopyAllAttributes(new PlatformAttributes(), attributes)); } + return copy; } @@ -559,8 +559,9 @@ private static SmallDictionary CopyAttributes(SmallD /// 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) + private static bool IsNotSuppressedByCallSite(SmallDictionary operationAttributes, + SmallDictionary callSiteAttributes, ImmutableArray msBuildPlatforms, + out SmallDictionary notSuppressedAttributes) { notSuppressedAttributes = new SmallDictionary(StringComparer.OrdinalIgnoreCase); bool? supportedOnlyList = null; @@ -720,7 +721,8 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary notSuppressedAttributes, string name) + static void AddOrUpdatedDiagnostic(PlatformAttributes operationAttributes, + SmallDictionary notSuppressedAttributes, string name) { if (operationAttributes.SupportedFirst != null) { @@ -844,6 +846,30 @@ attribute.ConstructorArguments[0] is { } argument && 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) @@ -856,7 +882,8 @@ private static void AddAttribute(string name, Version version, SmallDictionary existingAttributes) + static void AddOrUpdateUnsupportedAttribute(string name, PlatformAttributes attributes, + Version version, SmallDictionary existingAttributes) { if (attributes.UnsupportedFirst != null) { @@ -922,8 +949,8 @@ static void AddOrUpdateUnsupportedAttribute(string name, PlatformAttributes attr } static bool HasAnySupportedOnlyAttribute(string name, SmallDictionary existingAttributes) => - existingAttributes.Any(a => !a.Key.Equals(name, StringComparison.OrdinalIgnoreCase) && - AllowList(a.Value)); + existingAttributes.Any(a => !a.Key.Equals(name, StringComparison.OrdinalIgnoreCase) && + AllowList(a.Value)); } static void AddOrUpdateSupportedAttribute(PlatformAttributes attributes, Version version) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs index 0669a115ac..bd9fdc5d72 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzerTests.GuardedCallsTests.cs @@ -794,9 +794,6 @@ public class WindowsSpecificApis { [SupportedOSPlatform(""windows10.1.2.3"")] public static void WindowsOnlyMethod() { } - - [UnsupportedOSPlatform(""windows10.1.2.3"")] - public static void UnsupportedWindows10() { } } " + MockAttributesCsSource + MockOperatingSystemApiSource; await VerifyAnalyzerAsyncCs(source); diff --git a/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs b/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs index 7565c57362..1c32ce0bfb 100644 --- a/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs +++ b/src/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs @@ -135,7 +135,7 @@ public TAbstractAnalysisValue this[IOperation operation] return null; } - internal IEnumerable>? TryGetInterproceduralResultByDefinition() + internal IEnumerable>? TryGetInterproceduralResults() { foreach (var kvp in _interproceduralResultsMap) { From b749dfb2da9e996f4e5b92767f6ddba137e4f6e5 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 24 Aug 2020 16:20:56 -0700 Subject: [PATCH 47/48] Apply feedbacks --- .../PlatformCompatabilityAnalyzer.Value.cs | 1 - .../PlatformCompatabilityAnalyzer.cs | 16 ++++++++++------ .../MicrosoftNetCoreAnalyzersResources.resx | 2 +- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 4 ++-- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 4 ++-- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 4 ++-- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 4 ++-- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 4 ++-- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 4 ++-- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 4 ++-- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 4 ++-- ...MicrosoftNetCoreAnalyzersResources.pt-BR.xlf | 4 ++-- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 4 ++-- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 4 ++-- ...crosoftNetCoreAnalyzersResources.zh-Hans.xlf | 4 ++-- ...crosoftNetCoreAnalyzersResources.zh-Hant.xlf | 4 ++-- .../Compiler/PooledObjects/PooledSortedSet.cs | 17 ----------------- 17 files changed, 37 insertions(+), 51 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs index 3c0b99e6b8..9fd08d8390 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.Value.cs @@ -155,7 +155,6 @@ private static bool TryDecodeOSVersion( } osVersion = CreateVersion(versionBuilder); - return true; static bool TryDecodeOSVersionPart(IArgumentOperation argument, ValueContentAnalysisResult? valueContentAnalysisResult, out int osVersionPart) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs index 3b921783a4..d05b349d70 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatabilityAnalyzer.cs @@ -122,14 +122,19 @@ public override void Initialize(AnalysisContext context) context.RegisterOperationBlockStartAction(context => AnalyzeOperationBlock(context, guardMethods, osPlatformType, platformSpecificMembers, msBuildPlatforms)); }); - static ImmutableArray GetOperatingSystemGuardMethods(IMethodSymbol runtimeIsOSPlatformMethod, INamedTypeSymbol operatingSystemType) + static ImmutableArray GetOperatingSystemGuardMethods(IMethodSymbol? runtimeIsOSPlatformMethod, INamedTypeSymbol operatingSystemType) { - return operatingSystemType.GetMembers().OfType().Where(m => + var methods = operatingSystemType.GetMembers().OfType().Where(m => m.IsStatic && m.ReturnType.SpecialType == SpecialType.System_Boolean && (IsOSPlatform == m.Name) || NameAndParametersValid(m)). - ToImmutableArray(). - Add(runtimeIsOSPlatformMethod); + ToImmutableArray(); + + if (runtimeIsOSPlatformMethod != null) + { + return methods.Add(runtimeIsOSPlatformMethod); + } + return methods; } static ImmutableArray GetSupportedPlatforms(AnalyzerOptions options, Compilation compilation, CancellationToken cancellationToken) => @@ -218,7 +223,6 @@ private void AnalyzeOperationBlock( private static bool HasInterproceduralResult(IOperation platformSpecificOperation, SmallDictionary attributes, DataFlowAnalysisResult analysisResult) { - if (platformSpecificOperation.IsWithinLambdaOrLocalFunction()) { var results = analysisResult.TryGetInterproceduralResults(); @@ -234,6 +238,7 @@ private static bool HasInterproceduralResult(IOperation platformSpecificOperatio } } } + return false; } @@ -527,7 +532,6 @@ private static bool TryCopyAttributesNotSuppressedByMsBuild(SmallDictionaryUse `{0}` instead of Range-based indexers on an array - Call of platform dependent API + Validate Platform Compatibility Using platform dependent API on a component makes the code no longer work across all platforms. 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 56fb34d8b3..a38cb550b9 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1518,8 +1518,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility 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 c464217234..ba395d1e18 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1518,8 +1518,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility 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 08df5ed93a..157d20102b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1518,8 +1518,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility 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 777d6ebdd3..61e324170a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1518,8 +1518,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility 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 8c59353755..3265186678 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1518,8 +1518,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility 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 324bb0ee21..c74da0bd5f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1518,8 +1518,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility 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 f9776e88fe..910b8faa33 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1518,8 +1518,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility 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 19d4a7ed08..248ae7e32c 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1519,8 +1519,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility 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 05b35361c7..65d5db8c65 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 @@ -1518,8 +1518,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility 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 e19f6f224d..d9b7f1e84b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1518,8 +1518,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility 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 97ff45a283..b556264f09 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1518,8 +1518,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility 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 6c048d7699..8d0762085b 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 @@ -1518,8 +1518,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility 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 f551288d71..e5747d5af6 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 @@ -1518,8 +1518,8 @@ - Call of platform dependent API - Call of platform dependent API + Validate Platform Compatibility + Validate Platform Compatibility diff --git a/src/Utilities/Compiler/PooledObjects/PooledSortedSet.cs b/src/Utilities/Compiler/PooledObjects/PooledSortedSet.cs index c8c5ebacc8..3f012cc2f0 100644 --- a/src/Utilities/Compiler/PooledObjects/PooledSortedSet.cs +++ b/src/Utilities/Compiler/PooledObjects/PooledSortedSet.cs @@ -59,22 +59,5 @@ public static PooledSortedSet GetInstance(IComparer? comparer = null) Debug.Assert(instance.Count == 0); return instance; } - - /// - /// Gets a pooled instance of a with an initializer and an optional comparer. - /// - /// Initial values for the set. - /// Comparer to use, or null for the element type's default comparer. - /// An empty . - public static PooledSortedSet GetInstance(IEnumerable initializer, IComparer? comparer = null) - { - var instance = GetInstance(comparer); - foreach (var value in initializer) - { - instance.Add(value); - } - - return instance; - } } } From c252e9b4b9e78017c17f7fff1f6a551e85c70889 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 24 Aug 2020 20:49:12 -0700 Subject: [PATCH 48/48] Lower case 'platform compatibility' --- .../MicrosoftNetCoreAnalyzersResources.resx | 2 +- .../xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.de.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.es.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.it.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf | 4 ++-- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 24ee439746..3a2393de18 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1426,7 +1426,7 @@ Use `{0}` instead of Range-based indexers on an array - Validate Platform Compatibility + Validate platform compatibility Using platform dependent API on a component makes the code no longer work across all platforms. 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 a38cb550b9..34b8ac9900 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1518,8 +1518,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility 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 ba395d1e18..03337c34de 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1518,8 +1518,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility 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 157d20102b..67cba7a62e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1518,8 +1518,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility 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 61e324170a..af384ee4a3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1518,8 +1518,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility 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 3265186678..f41044208d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1518,8 +1518,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility 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 c74da0bd5f..c003dc6b47 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1518,8 +1518,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility 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 910b8faa33..91a241277c 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1518,8 +1518,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility 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 248ae7e32c..d76871845b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1519,8 +1519,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility 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 65d5db8c65..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 @@ -1518,8 +1518,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility 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 d9b7f1e84b..d4b7a45cd7 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1518,8 +1518,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility 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 b556264f09..cc0c4f17fb 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1518,8 +1518,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility 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 8d0762085b..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 @@ -1518,8 +1518,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility 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 e5747d5af6..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 @@ -1518,8 +1518,8 @@ - Validate Platform Compatibility - Validate Platform Compatibility + Validate platform compatibility + Validate platform compatibility