diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index d929849d5f..58e13eed62 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -7,6 +7,7 @@ Rule ID | Category | Severity | Notes CA1311 | Globalization | Hidden | SpecifyCultureForToLowerAndToUpper, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1311) CA1420 | Interoperability | Warning | FeatureUnsupportedWhenRuntimeMarshallingDisabled, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1420) CA1421 | Interoperability | Info | MethodUsesRuntimeMarshallingEvenWhenMarshallingDisabled, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1421) +CA1422 | Interoperability | Warning | PlatformCompatibilityAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422) CA1849 | Performance | Disabled | UseAsyncMethodInAsyncContext, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1849) CA1850 | Performance | Info | PreferHashDataOverComputeHashAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1850) CA1851 | Performance | Disabled | AvoidMultipleEnumerations, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1851) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.Data.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.Data.cs index 4059c6ad86..634f3e0de2 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.Data.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.Data.cs @@ -18,6 +18,7 @@ public sealed partial class PlatformCompatibilityAnalyzer /// So we only keep at most 2 versions of [UnsupportedOSPlatform] first one will be the lowest version found, second one will be second lowest if there is any /// /// Properties: + /// - ObsoletedIn - keeps lowest version of [ObsoletedInOSPlatform] attribute found /// - 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 @@ -25,12 +26,16 @@ public sealed partial class PlatformCompatibilityAnalyzer /// private class Versions { + public Version? ObsoletedIn { get; set; } + public string? ObsoletedMessage { get; set; } + public string? ObsoletedUrl { get; set; } public Version? SupportedFirst { get; set; } public Version? SupportedSecond { get; set; } public Version? UnsupportedFirst { get; set; } + public string? UnsupportedMessage { get; set; } public Version? UnsupportedSecond { get; set; } public bool IsSet() => SupportedFirst != null || UnsupportedFirst != null || - SupportedSecond != null || UnsupportedSecond != null; + SupportedSecond != null || UnsupportedSecond != null || ObsoletedIn != null; } private sealed class PlatformAttributes diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs index fa324a6d1e..0f35ce425d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs @@ -33,14 +33,16 @@ namespace Microsoft.NetCore.Analyzers.InteropServices [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed partial class PlatformCompatibilityAnalyzer : DiagnosticAnalyzer { - internal const string RuleId = "CA1416"; - private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(SupportedOSPlatformAttribute, UnsupportedOSPlatformAttribute); + internal const string SupportRuleId = "CA1416"; + internal const string ObsoletedRuleId = "CA1422"; + private static readonly ImmutableArray s_osPlatformAttributes = ImmutableArray.Create(SupportedOSPlatformAttribute, UnsupportedOSPlatformAttribute, ObsoletedInOSPlatformAttribute); private static readonly LocalizableString s_localizableTitle = CreateLocalizableResourceString(nameof(PlatformCompatibilityTitle)); private static readonly LocalizableString s_localizableDescription = CreateLocalizableResourceString(nameof(PlatformCompatibilityDescription)); // 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 ObsoletedInOSPlatformAttribute = nameof(ObsoletedInOSPlatformAttribute); private const string SupportedOSPlatformAttribute = nameof(SupportedOSPlatformAttribute); private const string UnsupportedOSPlatformAttribute = nameof(UnsupportedOSPlatformAttribute); private const string UnsupportedOSPlatformGuardAttribute = nameof(UnsupportedOSPlatformGuardAttribute); @@ -57,7 +59,7 @@ public sealed partial class PlatformCompatibilityAnalyzer : DiagnosticAnalyzer private static readonly Version EmptyVersion = new(0, 0); internal static readonly DiagnosticDescriptor OnlySupportedCsReachable = DiagnosticDescriptorHelper.Create( - RuleId, + SupportRuleId, s_localizableTitle, CreateLocalizableResourceString(nameof(PlatformCompatibilityOnlySupportedCsReachableMessage)), DiagnosticCategory.Interoperability, @@ -67,7 +69,7 @@ public sealed partial class PlatformCompatibilityAnalyzer : DiagnosticAnalyzer isDataflowRule: false); internal static readonly DiagnosticDescriptor OnlySupportedCsUnreachable = DiagnosticDescriptorHelper.Create( - RuleId, + SupportRuleId, s_localizableTitle, CreateLocalizableResourceString(nameof(PlatformCompatibilityOnlySupportedCsUnreachableMessage)), DiagnosticCategory.Interoperability, @@ -77,7 +79,7 @@ public sealed partial class PlatformCompatibilityAnalyzer : DiagnosticAnalyzer isDataflowRule: false); internal static readonly DiagnosticDescriptor OnlySupportedCsAllPlatforms = DiagnosticDescriptorHelper.Create( - RuleId, + SupportRuleId, s_localizableTitle, CreateLocalizableResourceString(nameof(PlatformCompatibilityOnlySupportedCsAllPlatformMessage)), DiagnosticCategory.Interoperability, @@ -87,7 +89,7 @@ public sealed partial class PlatformCompatibilityAnalyzer : DiagnosticAnalyzer isDataflowRule: false); internal static readonly DiagnosticDescriptor SupportedCsAllPlatforms = DiagnosticDescriptorHelper.Create( - RuleId, + SupportRuleId, s_localizableTitle, CreateLocalizableResourceString(nameof(PlatformCompatibilitySupportedCsAllPlatformMessage)), DiagnosticCategory.Interoperability, @@ -97,7 +99,7 @@ public sealed partial class PlatformCompatibilityAnalyzer : DiagnosticAnalyzer isDataflowRule: false); internal static readonly DiagnosticDescriptor SupportedCsReachable = DiagnosticDescriptorHelper.Create( - RuleId, + SupportRuleId, s_localizableTitle, CreateLocalizableResourceString(nameof(PlatformCompatibilitySupportedCsReachableMessage)), DiagnosticCategory.Interoperability, @@ -107,7 +109,7 @@ public sealed partial class PlatformCompatibilityAnalyzer : DiagnosticAnalyzer isDataflowRule: false); internal static readonly DiagnosticDescriptor UnsupportedCsAllPlatforms = DiagnosticDescriptorHelper.Create( - RuleId, + SupportRuleId, s_localizableTitle, CreateLocalizableResourceString(nameof(PlatformCompatibilityUnsupportedCsAllPlatformMessage)), DiagnosticCategory.Interoperability, @@ -117,7 +119,7 @@ public sealed partial class PlatformCompatibilityAnalyzer : DiagnosticAnalyzer isDataflowRule: false); internal static readonly DiagnosticDescriptor UnsupportedCsReachable = DiagnosticDescriptorHelper.Create( - RuleId, + SupportRuleId, s_localizableTitle, CreateLocalizableResourceString(nameof(PlatformCompatibilityUnsupportedCsReachableMessage)), DiagnosticCategory.Interoperability, @@ -126,8 +128,28 @@ public sealed partial class PlatformCompatibilityAnalyzer : DiagnosticAnalyzer isPortedFxCopRule: false, isDataflowRule: false); + internal static readonly DiagnosticDescriptor ObsoletedCsAllPlatforms = DiagnosticDescriptorHelper.Create( + ObsoletedRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(PlatformCompatibilityObsoletedCsAllPlatformMessage)), + DiagnosticCategory.Interoperability, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: false, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor ObsoletedCsReachable = DiagnosticDescriptorHelper.Create( + ObsoletedRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(PlatformCompatibilityObsoletedCsReachableMessage)), + DiagnosticCategory.Interoperability, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: false, + isDataflowRule: false); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(OnlySupportedCsReachable, OnlySupportedCsUnreachable, - OnlySupportedCsAllPlatforms, SupportedCsAllPlatforms, SupportedCsReachable, UnsupportedCsAllPlatforms, UnsupportedCsReachable); + OnlySupportedCsAllPlatforms, SupportedCsAllPlatforms, SupportedCsReachable, UnsupportedCsAllPlatforms, UnsupportedCsReachable, ObsoletedCsAllPlatforms, ObsoletedCsReachable); public override void Initialize(AnalysisContext context) { @@ -480,6 +502,7 @@ static bool IsKnownValueGuarded( attribute.UnsupportedSecond = null; } attribute.UnsupportedFirst = null; + attribute.UnsupportedMessage = null; } else { @@ -491,6 +514,7 @@ static bool IsKnownValueGuarded( attribute.SupportedSecond = null; attribute.UnsupportedSecond = null; attribute.UnsupportedFirst = null; + attribute.UnsupportedMessage = null; } else if (value.AnalysisValues.Contains(new PlatformMethodValue(info.PlatformName, EmptyVersion, false))) { @@ -504,6 +528,14 @@ static bool IsKnownValueGuarded( attribute.UnsupportedSecond = null; } + if (attribute.ObsoletedIn != null && + attribute.ObsoletedIn.IsGreaterThanOrEqualTo(info.Version)) + { + attribute.ObsoletedIn = null; + attribute.ObsoletedMessage = null; + attribute.ObsoletedUrl = null; + } + if (!IsEmptyVersion(info.Version)) { capturedVersions[info.PlatformName] = info.Version; @@ -518,6 +550,7 @@ static bool IsKnownValueGuarded( attribute.UnsupportedFirst.IsGreaterThanOrEqualTo(version)) { attribute.UnsupportedFirst = null; + attribute.UnsupportedMessage = null; } if (attribute.UnsupportedSecond != null && @@ -525,6 +558,7 @@ static bool IsKnownValueGuarded( version.IsGreaterThanOrEqualTo(attribute.UnsupportedSecond)) { attribute.UnsupportedSecond = null; + attribute.UnsupportedMessage = null; } } @@ -708,13 +742,19 @@ static void RemoveUnsupportsOnDifferentPlatforms(SmallDictionary operatio static void ReportSupportedDiagnostic(IOperation operation, OperationBlockAnalysisContext context, string operationName, SmallDictionary attributes, SmallDictionary? callsiteAttributes) { - var supportedRule = GetSupportedPlatforms(attributes, callsiteAttributes, out var platformNames); - var callSitePlatforms = GetCallsitePlatforms(attributes, callsiteAttributes, out var callsite, supported: supportedRule); - var csPlatformNames = JoinNames(callSitePlatforms); + var supportedRule = GetSupportedPlatforms(attributes, callsiteAttributes, out var platformNames, out var obsoletedPlatforms); + var csPlatformNames = JoinNames(GetCallsitePlatforms(attributes, callsiteAttributes, out var callsite, supported: supportedRule)); if (callsite == Callsite.Reachable && IsDenyList(callsiteAttributes)) { @@ -779,6 +818,11 @@ static void ReportSupportedDiagnostic(IOperation operation, OperationBlockAnalys var rule = supportedRule ? SwitchSupportedRule(callsite) : SwitchRule(callsite, true); context.ReportDiagnostic(operation.CreateDiagnostic(rule, operationName, JoinNames(platformNames), csPlatformNames)); + if (!obsoletedPlatforms.IsEmpty) + { + context.ReportDiagnostic(operation.CreateDiagnostic(SwitchObsoletedRule(callsite), operationName, JoinNames(obsoletedPlatforms), csPlatformNames)); + } + static DiagnosticDescriptor SwitchSupportedRule(Callsite callsite) => callsite switch { @@ -791,10 +835,12 @@ static DiagnosticDescriptor SwitchSupportedRule(Callsite callsite) static bool IsDenyList(SmallDictionary? callsiteAttributes) => callsiteAttributes != null && callsiteAttributes.Any(csa => DenyList(csa.Value)); - static bool GetSupportedPlatforms(SmallDictionary attributes, SmallDictionary? csAttributes, out List platformNames) + static bool GetSupportedPlatforms(SmallDictionary attributes, SmallDictionary? csAttributes, + out ImmutableArray platformNames, out ImmutableArray obsoletedPlatforms) { + using var obsoletedBuilder = ArrayBuilder.GetInstance(); bool? supportedRule = null; - platformNames = new List(); + using var platformsBuilder = ArrayBuilder.GetInstance(); foreach (var (pName, pAttribute) in attributes) { if (pAttribute.SupportedFirst != null && supportedRule.GetValueOrDefault(true)) @@ -805,56 +851,69 @@ static bool GetSupportedPlatforms(SmallDictionary attributes, { if (IsEmptyVersion(supportedVersion)) { - platformNames.Add(GetFormattedString(PlatformCompatibilityVersionAndBefore, - pName, pAttribute.UnsupportedFirst)); + platformsBuilder.Add(AppendMessage(pAttribute, + GetFormattedString(PlatformCompatibilityVersionAndBefore, pName, pAttribute.UnsupportedFirst))); } else { - platformNames.Add(GetFormattedString(PlatformCompatibilityFromVersionToVersion, - pName, supportedVersion, pAttribute.UnsupportedFirst)); + platformsBuilder.Add(AppendMessage(pAttribute, + GetFormattedString(PlatformCompatibilityFromVersionToVersion, pName, supportedVersion, pAttribute.UnsupportedFirst))); } } else if (IsEmptyVersion(supportedVersion)) { if (csAttributes != null && HasSameVersionedPlatformSupport(csAttributes, pName, checkSupport: false)) { - platformNames.Add(GetFormattedString(PlatformCompatibilityAllVersions, pName)); + platformsBuilder.Add(GetFormattedString(PlatformCompatibilityAllVersions, pName)); continue; } - platformNames.Add(EncloseWithQuotes(pName)); + platformsBuilder.Add(EncloseWithQuotes(pName)); } else { - platformNames.Add(GetFormattedString(PlatformCompatibilityVersionAndLater, pName, supportedVersion)); + platformsBuilder.Add(GetFormattedString(PlatformCompatibilityVersionAndLater, pName, supportedVersion)); } } else if (pAttribute.UnsupportedFirst != null) { if (supportedRule.GetValueOrDefault()) { - platformNames.Clear(); + platformsBuilder.Clear(); } supportedRule = false; if (IsEmptyVersion(pAttribute.UnsupportedFirst)) { if (csAttributes != null && HasSameVersionedPlatformSupport(csAttributes, pName, checkSupport: true)) { - platformNames.Add(GetFormattedString(PlatformCompatibilityAllVersions, pName)); + platformsBuilder.Add(AppendMessage(pAttribute, GetFormattedString(PlatformCompatibilityAllVersions, pName))); continue; } - platformNames.Add(EncloseWithQuotes(pName)); + platformsBuilder.Add(AppendMessage(pAttribute, EncloseWithQuotes(pName))); } else { - platformNames.Add(GetFormattedString(PlatformCompatibilityVersionAndLater, - pName, pAttribute.UnsupportedFirst)); + platformsBuilder.Add(AppendMessage(pAttribute, + GetFormattedString(PlatformCompatibilityVersionAndLater, pName, pAttribute.UnsupportedFirst))); } } + + AddObsoletedIn(csAttributes, obsoletedBuilder, pName, pAttribute); } + obsoletedPlatforms = obsoletedBuilder.ToImmutable(); + platformNames = platformsBuilder.ToImmutable(); return supportedRule.GetValueOrDefault(true); } } + static DiagnosticDescriptor SwitchObsoletedRule(Callsite callsite) + { + return callsite switch + { + Callsite.AllPlatforms => ObsoletedCsAllPlatforms, + Callsite.Reachable => ObsoletedCsReachable, + _ => throw new NotImplementedException() + }; + } static DiagnosticDescriptor SwitchRule(Callsite callsite, bool unsupported) { @@ -878,16 +937,56 @@ static DiagnosticDescriptor SwitchRule(Callsite callsite, bool unsupported) } } + static string AppendMessage(Versions attribute, string message) + { + if (attribute.UnsupportedMessage is not null) + { + message += string.Format(CultureInfo.InvariantCulture, ParenthesisWithPlaceHolder, attribute.UnsupportedMessage); + } + + return message; + } + + static string AppendMessageAndUrl(Versions attribute, string message) + { + if (attribute.ObsoletedMessage is not null) + { + string customMessge = attribute.ObsoletedMessage; + if (attribute.ObsoletedUrl is not null) + { + customMessge = $"{customMessge} {attribute.ObsoletedUrl}"; + } + message += string.Format(CultureInfo.InvariantCulture, ParenthesisWithPlaceHolder, customMessge); + } + else if (attribute.ObsoletedUrl is not null) + { + message += string.Format(CultureInfo.InvariantCulture, ParenthesisWithPlaceHolder, attribute.ObsoletedUrl); + } + + return message; + } + static void ReportUnsupportedDiagnostic(IOperation operation, OperationBlockAnalysisContext context, string operationName, SmallDictionary attributes, SmallDictionary? callsiteAttributes) { - var unsupportedRule = GetPlatformNames(attributes, callsiteAttributes, out var platformNames); + var unsupportedRule = GetPlatformNames(attributes, callsiteAttributes, out var platformNames, out var obsoletedPlatforms); var csPlatformNames = JoinNames(GetCallsitePlatforms(attributes, callsiteAttributes, out var callsite, supported: !unsupportedRule)); - context.ReportDiagnostic(operation.CreateDiagnostic(SwitchRule(callsite, unsupportedRule), operationName, JoinNames(platformNames), csPlatformNames)); - static bool GetPlatformNames(SmallDictionary attributes, SmallDictionary? csAttributes, out List platformNames) + if (!platformNames.IsEmpty) + { + context.ReportDiagnostic(operation.CreateDiagnostic(SwitchRule(callsite, unsupportedRule), operationName, JoinNames(platformNames), csPlatformNames)); + } + + if (!obsoletedPlatforms.IsEmpty) + { + context.ReportDiagnostic(operation.CreateDiagnostic(SwitchObsoletedRule(callsite), operationName, JoinNames(obsoletedPlatforms), csPlatformNames)); + } + + static bool GetPlatformNames(SmallDictionary attributes, SmallDictionary? csAttributes, + out ImmutableArray platformNames, out ImmutableArray obsoletedPlatforms) { - platformNames = new List(); + using var obsoletedBuilder = ArrayBuilder.GetInstance(); + using var platformsBuilder = ArrayBuilder.GetInstance(); var unsupportedRule = true; foreach (var (pName, pAttribute) in attributes) { @@ -908,20 +1007,23 @@ static bool GetPlatformNames(SmallDictionary attributes, Small unsupportedRule = true; if (IsEmptyVersion(pAttribute.UnsupportedFirst!)) { - platformNames.Add(GetFormattedString(PlatformCompatibilityVersionAndBefore, pName, supportedVersion)); + platformsBuilder.Add(AppendMessage(pAttribute, GetFormattedString(PlatformCompatibilityVersionAndBefore, pName, supportedVersion))); } else { - platformNames.Add(GetFormattedString(PlatformCompatibilityFromVersionToVersion, pName, unsupportedVersion, supportedVersion)); + platformsBuilder.Add(AppendMessage(pAttribute, + GetFormattedString(PlatformCompatibilityFromVersionToVersion, pName, unsupportedVersion, supportedVersion))); } - continue; } - platformNames.Add(GetFormattedString(PlatformCompatibilityVersionAndLater, pName, supportedVersion)); + else + { + platformsBuilder.Add(AppendMessage(pAttribute, GetFormattedString(PlatformCompatibilityVersionAndLater, pName, supportedVersion))); + } } else { - platformNames.Add(GetFormattedString(PlatformCompatibilityFromVersionToVersion, - pName, supportedVersion, unsupportedVersion)); + platformsBuilder.Add(AppendMessage(pAttribute, + GetFormattedString(PlatformCompatibilityFromVersionToVersion, pName, supportedVersion, unsupportedVersion))); } } else @@ -930,14 +1032,16 @@ static bool GetPlatformNames(SmallDictionary attributes, Small { if (csAttributes != null && HasSameVersionedPlatformSupport(csAttributes, pName, checkSupport: true)) { - platformNames.Add(GetFormattedString(PlatformCompatibilityAllVersions, pName)); - continue; + platformsBuilder.Add(AppendMessage(pAttribute, GetFormattedString(PlatformCompatibilityAllVersions, pName))); + } + else + { + platformsBuilder.Add(AppendMessage(pAttribute, EncloseWithQuotes(pName))); } - platformNames.Add(EncloseWithQuotes(pName)); } else { - platformNames.Add(GetFormattedString(PlatformCompatibilityVersionAndLater, pName, unsupportedVersion)); + platformsBuilder.Add(AppendMessage(pAttribute, GetFormattedString(PlatformCompatibilityVersionAndLater, pName, unsupportedVersion))); } } } @@ -946,23 +1050,50 @@ static bool GetPlatformNames(SmallDictionary attributes, Small unsupportedRule = false; if (IsEmptyVersion(supportedVersion)) { - platformNames.Add(EncloseWithQuotes(pName)); + platformsBuilder.Add(EncloseWithQuotes(pName)); } else { - platformNames.Add(GetFormattedString(PlatformCompatibilityVersionAndLater, pName, supportedVersion)); + platformsBuilder.Add(GetFormattedString(PlatformCompatibilityVersionAndLater, pName, supportedVersion)); } } + + AddObsoletedIn(csAttributes, obsoletedBuilder, pName, pAttribute); } + + obsoletedPlatforms = obsoletedBuilder.ToImmutable(); + platformNames = platformsBuilder.ToImmutable(); return unsupportedRule; } } - static List GetCallsitePlatforms(SmallDictionary attributes, + static void AddObsoletedIn(SmallDictionary? csAttributes, ArrayBuilder obsoletedBuilder, string pName, Versions pAttribute) + { + if (pAttribute.ObsoletedIn != null) + { + if (IsEmptyVersion(pAttribute.ObsoletedIn)) // Do not need to add the version part if it is 0.0 + { + if (csAttributes != null && HasVersionedCallsite(csAttributes, pName)) + { + obsoletedBuilder.Add(AppendMessage(pAttribute, GetFormattedString(PlatformCompatibilityAllVersions, pName))); + } + else + { + obsoletedBuilder.Add(AppendMessage(pAttribute, EncloseWithQuotes(pName))); + } + } + else + { + obsoletedBuilder.Add(AppendMessageAndUrl(pAttribute, GetFormattedString(PlatformCompatibilityVersionAndLater, pName, pAttribute.ObsoletedIn))); + } + } + } + + static ImmutableArray GetCallsitePlatforms(SmallDictionary attributes, SmallDictionary? callsiteAttributes, out Callsite callsite, bool supported) { callsite = Callsite.AllPlatforms; - var platformNames = new List(); + using var platformNames = ArrayBuilder.GetInstance(); if (callsiteAttributes != null) { foreach (var (pName, csAttribute) in callsiteAttributes) @@ -1045,7 +1176,7 @@ static List GetCallsitePlatforms(SmallDictionary attri } } } - return platformNames; + return platformNames.ToImmutable(); } static string GetFormattedString(string resource, string platformName, object? arg1 = null, object? arg2 = null) => @@ -1056,9 +1187,9 @@ static string AddOsxIfMacOS(string platformName) => static string EncloseWithQuotes(string pName) => $"'{AddOsxIfMacOS(pName)}'"; - static string JoinNames(List platformNames) + static string JoinNames(ImmutableArray platformNames) { - platformNames.Sort(StringComparer.OrdinalIgnoreCase); + platformNames = platformNames.Sort(StringComparer.OrdinalIgnoreCase); return string.Join(CommaSeparator, platformNames); } @@ -1077,18 +1208,6 @@ static bool HasSameVersionedPlatformSupport(SmallDictionary at { version = supportedVersion.IsGreaterThanOrEqualTo(version) ? supportedVersion : version; } - else - { - var unsupportedVersion = attribute.UnsupportedSecond ?? attribute.UnsupportedFirst; - if (unsupportedVersion != null) - { - version = unsupportedVersion.IsGreaterThanOrEqualTo(version) ? unsupportedVersion : version; - } - else - { - version = supportedVersion; - } - } } if (version != null && !IsEmptyVersion(version)) { @@ -1097,6 +1216,25 @@ static bool HasSameVersionedPlatformSupport(SmallDictionary at } return false; } + + static bool HasVersionedCallsite(SmallDictionary csAttributes, string pName) + { + if (csAttributes.TryGetValue(pName, out var attribute)) + { + var version = attribute.ObsoletedIn; + var supportedVersion = attribute.SupportedSecond ?? attribute.SupportedFirst; + if (supportedVersion != null) + { + version = supportedVersion.IsGreaterThanOrEqualTo(version) ? supportedVersion : version; + } + + if (version != null && !IsEmptyVersion(version)) + { + return true; + } + } + return false; + } } private enum Callsite @@ -1156,8 +1294,8 @@ private static ISymbol GetEventAccessor(IEventSymbol iEvent, IOperation operatio private static void AnalyzeOperation(IOperation operation, OperationAnalysisContext context, PooledConcurrentDictionary, (SmallDictionary attributes, SmallDictionary? csAttributes)> platformSpecificOperations, - ConcurrentDictionary platformSpecificMembers, ImmutableArray msBuildPlatforms, - ITypeSymbol? notSupportedExceptionType, bool crossPlatform, SmallDictionary relatedPlatforms) + ConcurrentDictionary platformSpecificMembers, ImmutableArray msBuildPlatforms, + ITypeSymbol? notSupportedExceptionType, bool crossPlatform, SmallDictionary relatedPlatforms) { if (operation.Parent is IArgumentOperation argumentOperation && UsedInCreatingNotSupportedException(argumentOperation, notSupportedExceptionType)) { @@ -1362,6 +1500,7 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst)))) { diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone(); + diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage; } } } @@ -1379,12 +1518,14 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary if (!UnsupportedFirstSuppressed(attribute, callSiteAttribute)) { diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone(); + diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage; } if (attribute.UnsupportedSecond != null && !UnsupportedSecondSuppressed(attribute, callSiteAttribute)) { diagnosticAttribute.UnsupportedSecond = (Version)attribute.UnsupportedSecond.Clone(); + diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage; } } else if (msBuildPlatforms.Contains(platformName, StringComparer.OrdinalIgnoreCase)) @@ -1393,12 +1534,14 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary { diagnosticAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone(); diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone(); + diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage; } if (attribute.UnsupportedSecond != null && !SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedSecond)) { diagnosticAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone(); diagnosticAttribute.UnsupportedSecond = (Version)attribute.UnsupportedSecond.Clone(); + diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage; } } } @@ -1422,21 +1565,25 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary if (!SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst)) { diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone(); + diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage; } else if (DenyList(callSiteAttribute)) { diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone(); + diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage; } } else { diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone(); + diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage; } } else if (msBuildPlatforms.Contains(platformName, StringComparer.OrdinalIgnoreCase) && !SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst)) { diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone(); + diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage; } } else if (msBuildPlatforms.Contains(platformName, StringComparer.OrdinalIgnoreCase) && @@ -1444,6 +1591,40 @@ private static bool IsNotSuppressedByCallSite(SmallDictionary { // 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(); + diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage; + } + } + + // Check if obsoleted attribute guarded by callsite attributes + if (attribute.ObsoletedIn != null) + { + if (callSiteAttributes.TryGetValue(platformName, out var callSiteAttribute)) + { + if (callSiteAttribute.SupportedFirst != null) + { + if ((callSiteAttribute.ObsoletedIn == null || callSiteAttribute.ObsoletedIn > attribute.ObsoletedIn) && + (callSiteAttribute.UnsupportedFirst == null || callSiteAttribute.UnsupportedFirst > attribute.ObsoletedIn)) + { + diagnosticAttribute.ObsoletedIn = (Version)attribute.ObsoletedIn.Clone(); + diagnosticAttribute.ObsoletedMessage = attribute.ObsoletedMessage; + diagnosticAttribute.ObsoletedUrl = attribute.ObsoletedUrl; + } + } + else if (msBuildPlatforms.Contains(platformName, StringComparer.OrdinalIgnoreCase) && + (callSiteAttribute.UnsupportedFirst != null && callSiteAttribute.UnsupportedFirst > attribute.ObsoletedIn || + callSiteAttribute.ObsoletedIn != null && callSiteAttribute.ObsoletedIn > attribute.ObsoletedIn)) + { + diagnosticAttribute.ObsoletedIn = (Version)attribute.ObsoletedIn.Clone(); + diagnosticAttribute.ObsoletedMessage = attribute.ObsoletedMessage; + diagnosticAttribute.ObsoletedUrl = attribute.ObsoletedUrl; + } + } + else if (msBuildPlatforms.Contains(platformName, StringComparer.OrdinalIgnoreCase) && + !callSiteAttributes.Values.Any(AllowList)) + { + diagnosticAttribute.ObsoletedIn = (Version)attribute.ObsoletedIn.Clone(); + diagnosticAttribute.ObsoletedMessage = attribute.ObsoletedMessage; + diagnosticAttribute.ObsoletedUrl = attribute.ObsoletedUrl; } } @@ -1534,6 +1715,10 @@ private static Versions CopyAllAttributes(Versions copyTo, Versions copyFrom) copyTo.SupportedSecond = (Version?)copyFrom.SupportedSecond?.Clone(); copyTo.UnsupportedFirst = (Version?)copyFrom.UnsupportedFirst?.Clone(); copyTo.UnsupportedSecond = (Version?)copyFrom.UnsupportedSecond?.Clone(); + copyTo.UnsupportedMessage = copyFrom.UnsupportedMessage; + copyTo.ObsoletedIn = (Version?)copyFrom.ObsoletedIn?.Clone(); + copyTo.ObsoletedMessage = copyFrom.ObsoletedMessage; + copyTo.ObsoletedUrl = copyFrom.ObsoletedUrl; return copyTo; } @@ -1643,6 +1828,17 @@ static void MergePlatformAttributes(ImmutableArray immediateAttri existing.SupportedFirst = childAttribute.SupportedFirst; } } + + if (childAttribute.ObsoletedIn != null && + (childAttribute.ObsoletedIn < existing.UnsupportedFirst || + existing.SupportedFirst != null && + (existing.UnsupportedSecond == null || existing.UnsupportedSecond > childAttribute.ObsoletedIn)) && + (existing.ObsoletedIn == null || childAttribute.ObsoletedIn < existing.ObsoletedIn)) + { + existing.ObsoletedIn = childAttribute.ObsoletedIn; + existing.ObsoletedMessage = childAttribute.ObsoletedMessage; + existing.ObsoletedUrl = childAttribute.ObsoletedMessage; + } } else { @@ -1672,10 +1868,12 @@ static void MergePlatformAttributes(ImmutableArray immediateAttri parentAttributes.Callsite = Callsite.Empty; attributes.SupportedFirst = childAttribute.SupportedFirst > attributes.SupportedFirst ? childAttribute.SupportedFirst : null; attributes.UnsupportedFirst = childAttribute.UnsupportedFirst; + attributes.UnsupportedMessage = childAttribute.UnsupportedMessage; } else if (attributes.UnsupportedFirst == null || attributes.UnsupportedFirst > childAttribute.UnsupportedFirst) { attributes.UnsupportedFirst = childAttribute.UnsupportedFirst; + attributes.UnsupportedMessage = childAttribute.UnsupportedMessage; } if (attributes.SupportedSecond.IsGreaterThanOrEqualTo(childAttribute.UnsupportedFirst)) @@ -1685,6 +1883,7 @@ static void MergePlatformAttributes(ImmutableArray immediateAttri if (childAttribute.UnsupportedSecond != null && childAttribute.UnsupportedSecond > attributes.UnsupportedFirst) { attributes.UnsupportedFirst = childAttribute.UnsupportedSecond; + attributes.UnsupportedMessage = childAttribute.UnsupportedMessage; } } } @@ -1694,6 +1893,16 @@ static void MergePlatformAttributes(ImmutableArray immediateAttri notFoundPlatforms.Add(platform); } } + + // Check for Obsoleted attributes, only lower version could overwrite + if (childAttributes.TryGetValue(platform, out var childAttr) && + childAttr.ObsoletedIn != null && + (attributes.ObsoletedIn == null || childAttr.ObsoletedIn < attributes.ObsoletedIn)) + { + attributes.ObsoletedIn = childAttr.ObsoletedIn; + attributes.ObsoletedMessage = childAttr.ObsoletedMessage; + attributes.ObsoletedUrl = childAttr.ObsoletedUrl; + } } CheckAttributesConsistency(pAttributes); @@ -1751,9 +1960,8 @@ static void CheckAttributesConsistency(SmallDictionary childAt { allowList = true; } - else + else if (DenyList(attributes)) { - Debug.Assert(DenyList(attributes)); unsupportedList.Add(platform); } } @@ -1801,7 +2009,7 @@ private static bool TryAddValidAttribute([NotNullWhen(true)] ref SmallDictionary attributes[platformName] = new Versions(); } - if (!AddAttribute(attribute.AttributeClass.Name, version, attributes[platformName])) + if (!AddAttribute(attribute, version, attributes[platformName])) { attributes.Remove(platformName); } @@ -1812,7 +2020,7 @@ private static bool TryAddValidAttribute([NotNullWhen(true)] ref SmallDictionary attributes[relation.relatedPlatform] = new Versions(); } - AddAttribute(attribute.AttributeClass.Name, version, attributes[relation.relatedPlatform]); + AddAttribute(attribute, version, attributes[relation.relatedPlatform]); } return true; @@ -1876,8 +2084,9 @@ private static bool TryParsePlatformNameAndVersion(string osString, out string o private static string GetNameAsMacOsWhenOSX(string platformName) => platformName.Equals(OSX, StringComparison.OrdinalIgnoreCase) ? macOS : platformName; - private static bool AddAttribute(string name, Version version, Versions attributes) + private static bool AddAttribute(AttributeData attribute, Version version, Versions attributes) { + string name = attribute.AttributeClass.Name; if (name == SupportedOSPlatformAttribute) { if (attributes.UnsupportedFirst != null && attributes.UnsupportedFirst == version) @@ -1890,38 +2099,68 @@ private static bool AddAttribute(string name, Version version, Versions attribut AddOrUpdateSupportedAttribute(attributes, version); } } - else + else if (name == UnsupportedOSPlatformAttribute) { - Debug.Assert(name == UnsupportedOSPlatformAttribute); - if (attributes.SupportedFirst != null && attributes.SupportedFirst == version) { attributes.SupportedFirst = null; } - AddOrUpdateUnsupportedAttribute(attributes, version); + AddOrUpdateUnsupportedAttribute(attribute, attributes, version); + } + else + { + Debug.Assert(name == ObsoletedInOSPlatformAttribute); + AddOrUpdateObsoletedAttribute(attribute, attributes, version); } return true; - static void AddOrUpdateUnsupportedAttribute(Versions attributes, Version version) + static void AddOrUpdateObsoletedAttribute(AttributeData attribute, Versions attributes, Version version) + { + var message = PopulateMessage(attribute); + var url = PopulateUrl(attribute); + + if (attributes.ObsoletedIn != null) + { + // only keep lowest version, ignore other versions + if (attributes.ObsoletedIn > version) + { + attributes.ObsoletedIn = version; + attributes.ObsoletedMessage = message; + attributes.ObsoletedUrl = url; + } + } + else + { + attributes.ObsoletedIn = version; + attributes.ObsoletedMessage = message; + attributes.ObsoletedUrl = url; + } + } + + static void AddOrUpdateUnsupportedAttribute(AttributeData attribute, Versions attributes, Version version) { + var message = PopulateMessage(attribute); if (attributes.UnsupportedFirst != null) { if (attributes.UnsupportedFirst > version) { attributes.UnsupportedSecond = attributes.UnsupportedFirst; attributes.UnsupportedFirst = version; + attributes.UnsupportedMessage = message; } else if (attributes.UnsupportedSecond == null || attributes.UnsupportedSecond > version) { attributes.UnsupportedSecond = version; + attributes.UnsupportedMessage = message; } } else { attributes.UnsupportedFirst = version; + attributes.UnsupportedMessage = message; } } @@ -1942,6 +2181,26 @@ static void AddOrUpdateSupportedAttribute(Versions attributes, Version version) } } + private static string? PopulateMessage(AttributeData attribute) + { + if (attribute.ConstructorArguments.Length == 2) + { + return attribute.ConstructorArguments[1].Value?.ToString(); + } + + return null; + } + + private static string? PopulateUrl(AttributeData attribute) + { + if (attribute.NamedArguments.Length == 1 && attribute.NamedArguments[0].Key is "Url") + { + return attribute.NamedArguments[0].Value.Value.ToString(); + } + + return null; + } + /// /// Determines if the attributes supported only for the platform (allow list) /// diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 1afda5dd95..9fd249e48a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1948,4 +1948,15 @@ The behavior of '{0}' could vary based on the current user's locale settings. Provide a value for the 'IFormatProvider' argument. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + ({0}) + \ 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 a8ae91171d..041cd83a1a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1832,6 +1832,11 @@ Metody P/Invoke nemají být viditelné + + ({0}) + ({0}) + + and all other platforms a všechny ostatní platformy @@ -1852,6 +1857,16 @@ {0} od verze {1} do {2} 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. Toto místo volání je k dispozici na všech platformách. {0} se podporuje jen na {1}. @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - Toto místo volání je k dispozici na všech platformách. {0} se nepodporuje na {1}. + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - Toto místo volání je k dispozici na {2}. {0} se nepodporuje na {1}. + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. 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 1a686fe537..69ed32acd8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1832,6 +1832,11 @@ P/Invokes dürfen nicht sichtbar sein + + ({0}) + ({0}) + + and all other platforms und alle anderen Plattformen @@ -1852,6 +1857,16 @@ {0}, Version {1} bis {2} 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. Diese Aufrufsite ist auf allen Plattformen erreichbar. "{0}" nur unterstützt für: {1}. @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - Diese Aufrufsite ist auf allen Plattformen erreichbar. "{0}" nicht unterstützt für: {1}. + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - Diese Aufrufsite ist erreichbar für: {2}. "{0}" nicht unterstützt für: {1}. + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. 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 6a75f6fa38..d087e2a755 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1832,6 +1832,11 @@ Los elementos P/Invoke no deben estar visibles + + ({0}) + ({0}) + + and all other platforms y el resto de plataformas @@ -1852,6 +1857,16 @@ "{0}" de la versión {1} a la {2} 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. Se puede acceder a este sitio de llamada en todas las plataformas. "{0}" solo se admite en {1}. @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - Se puede acceder a este sitio de llamada en todas las plataformas. "{0}" no se admite en {1}. + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - Se puede acceder a este sitio de llamada en {2}. "{0}" no se admite en {1}. + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. 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 93ff5d5afd..3a20f66f0c 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1832,6 +1832,11 @@ Les P/Invoke ne doivent pas être visibles + + ({0}) + ({0}) + + and all other platforms et toutes les autres plateformes @@ -1852,6 +1857,16 @@ '{0}' de la version {1} à {2} 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. Ce site d'appel est accessible sur toutes les plateformes. '{0}' est uniquement pris en charge sur {1}. @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - Ce site d'appel est accessible sur toutes les plateformes. '{0}' n'est pas pris en charge sur {1}. + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - Ce site d'appel est accessible sur {2}. '{0}' n'est pas pris en charge sur {1}. + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. 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 7e4006d87f..0283857596 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1832,6 +1832,11 @@ I metodi P/Invoke non devono essere visibili + + ({0}) + ({0}) + + and all other platforms e tutte le altre piattaforme @@ -1852,6 +1857,16 @@ '{0}' dalla versione {1} alla versione {2} 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. Questo sito di chiamata è raggiungibile da tutte le piattaforme. '{0}' è supportato solo in {1}. @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - Questo sito di chiamata è raggiungibile da tutte le piattaforme. '{0}' non è supportato in {1}. + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - Questo sito di chiamata è raggiungibile da {2}. '{0}' non è supportato in {1}. + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. 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 5a11f87e84..5270c1f896 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1832,6 +1832,11 @@ P/Invokes は参照可能にすることはできません + + ({0}) + ({0}) + + and all other platforms およびその他すべてのプラットフォーム @@ -1852,6 +1857,16 @@ バージョン {1} から {2} の '{0}' 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. この呼び出しサイトはすべてのプラットフォームで到達可能です。'{0}' は {1} でのみサポートされています。 @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - この呼び出しサイトはすべてのプラットフォームで到達可能です。'{0}' は {1} でサポートされていません。 + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - この呼び出しサイトは {2} で到達可能です。'{0}' は {1} でサポートされていません。 + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. 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 66b05763d3..45035073ea 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1832,6 +1832,11 @@ P/Invokes를 표시하지 않아야 합니다. + + ({0}) + ({0}) + + and all other platforms 및 다른 모든 플랫폼 @@ -1852,6 +1857,16 @@ '{0}' 버전 {1}~{2} 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. 이 호출 사이트에는 모든 플랫폼에서 연결할 수 있습니다. '{0}'은(는) {1}에서만 지원됩니다. @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - 이 호출 사이트에는 모든 플랫폼에서 연결할 수 있습니다. '{0}'은(는) {1}에서 지원되지 않습니다. + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - 이 호출 사이트에는 {2}에서 연결할 수 있습니다. '{0}'은(는) {1}에서 지원되지 않습니다. + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. 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 1b07cfdfff..b74a8655d8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1832,6 +1832,11 @@ Elementy P/Invoke nie powinny być widoczne + + ({0}) + ({0}) + + and all other platforms i wszystkie inne platformy @@ -1852,6 +1857,16 @@ system „{0}” w wersji od {1} do {2} 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. To miejsce wywołania jest osiągalne na wszystkich platformach. Metoda „{0}” jest obsługiwana tylko na następujących platformach: {1}. @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - To miejsce wywołania jest osiągalne na wszystkich platformach. Metoda „{0}” nie jest obsługiwana na następujących platformach: {1}. + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - To miejsce wywołania jest osiągalne na platformie: {2}. Metoda „{0}” nie jest obsługiwana na następujących platformach: {1}. + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. 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 15412da949..c799b15293 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 @@ -1832,6 +1832,11 @@ P/Invokes não deve ser visível + + ({0}) + ({0}) + + and all other platforms e todas as outras plataformas @@ -1852,6 +1857,16 @@ '{0}' da versão {1} à {2} 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. Este site de chamada pode ser acessado em todas as plataformas. Só há suporte para '{0}' em: {1}. @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - Este site de chamada pode ser acessado em todas as plataformas. Não há suporte para '{0}' em: {1}. + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - Este site de chamada pode ser acessado em: {2}. Não há suporte para '{0}' em: {1}. + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. 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 25134844e9..9bd1ca7323 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1832,6 +1832,11 @@ Методы P/Invoke не должны быть видимыми + + ({0}) + ({0}) + + and all other platforms и на всех остальных платформах @@ -1852,6 +1857,16 @@ "{0}" с версии {1} до {2} 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. Этот сайт вызова доступен на всех платформах. "{0}" поддерживается только в {1}. @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - Этот сайт вызова доступен на всех платформах. "{0}" не поддерживается в {1}. + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - Этот сайт вызова доступен в {2}. "{0}" не поддерживается в {1}. + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. 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 136d1fda74..b9ce1103d6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1832,6 +1832,11 @@ P/Invokes görünür olmamalıdır + + ({0}) + ({0}) + + and all other platforms ve diğer tüm platformlar @@ -1852,6 +1857,16 @@ '{0}' {1} sürümünden {2} sürümüne kadar 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. Bu çağrı sitesine tüm platformlarda ulaşılabilir. '{0}' yalnızca şurada desteklenir: {1}. @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - Bu çağrı sitesine tüm platformlarda ulaşılabilir. '{0}' şurada desteklenmez: {1}. + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - Bu çağrı sitesine şurada ulaşılabilir: {2}. '{0}' şurada desteklenmez: {1}. + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. 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 d6fe644987..287002ea92 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 @@ -1832,6 +1832,11 @@ P/Invokes 应该是不可见的 + + ({0}) + ({0}) + + and all other platforms 和其他所有平台 @@ -1852,6 +1857,16 @@ "{0}" 版本 {1} 到 {2}。 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. 可在所有平台上访问此调用站点。"{0}" 仅在 {1} 上受支持。 @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - 可在所有平台上访问此调用站点。"{0}" 在 {1} 上不受支持。 + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - 可在 {2} 上访问此调用站点。"{0}" 在 {1} 上不受支持。 + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. 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 0b61f2cda2..f9f174d063 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 @@ -1832,6 +1832,11 @@ 不應看得見 P/Invoke + + ({0}) + ({0}) + + and all other platforms 及所有其他平台 @@ -1852,6 +1857,16 @@ '{0}' 從 {1} 至 {2} 的版本 'SupportedOnWindows1903UnsupportedOn2004()' is supported on: 'windows' from version 10.0.1903 to 10.0.2004. + + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. '{0}' is obsoleted on: {1}. + This call site is reachable on all platforms. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + + + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on: {2}. '{0}' is obsoleted on: {1}. + This call site is reachable on 'macos', 'linux'. 'OboletedOnMacOS()' is obsoleted on: 'macos'. + This call site is reachable on all platforms. '{0}' is only supported on: {1}. 可在所有平台上連線到此呼叫網站。只有 {1} 支援 '{0}'。 @@ -1884,12 +1899,12 @@ This call site is reachable on all platforms. '{0}' is unsupported on: {1}. - 可在所有平台上連線到此呼叫網站。{1} 不支援 '{0}'。 + This call site is reachable on all platforms. '{0}' is unsupported on: {1}. This call site is reachable on all platforms. 'UnsupportedOnWindows()' is unsupported on: 'windows' This call site is reachable on: {2}. '{0}' is unsupported on: {1}. - 可在 {2} 上連線到此呼叫網站。{1} 不支援 '{0}'。 + This call site is reachable on: {2}. '{0}' is unsupported on: {1}. This call site is reachable on: 'windows', 'browser'. 'UnsupportedOnBrowser()' is unsupported on: 'browser'. diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index 308d72dcec..76c3b85978 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -768,6 +768,18 @@ This method uses runtime marshalling even when runtime marshalling is disabled, |CodeFix|True| --- +## [CA1422](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422): Validate platform compatibility + +Using platform dependent API on a component makes the code no longer work across all platforms. + +|Item|Value| +|-|-| +|Category|Interoperability| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + ## [CA1501](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1501): Avoid excessive inheritance Deeply nested type hierarchies can be difficult to follow, understand, and maintain. This rule limits analysis to hierarchies in the same module. To fix a violation of this rule, derive the type from a base type that is less deep in the inheritance hierarchy or eliminate some of the intermediate base types. diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index cec4eac782..e66380c020 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -1710,6 +1710,26 @@ ] } }, + "CA1422": { + "id": "CA1422", + "shortDescription": "Validate platform compatibility", + "fullDescription": "Using platform dependent API on a component makes the code no longer work across all platforms.", + "defaultLevel": "warning", + "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422", + "properties": { + "category": "Interoperability", + "isEnabledByDefault": true, + "typeName": "PlatformCompatibilityAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, "CA1501": { "id": "CA1501", "shortDescription": "Avoid excessive inheritance", diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index c882b1714b..1096512739 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -5,6 +5,7 @@ Rule ID | Missing Help Link | Title | CA1311 | | Specify a culture or use an invariant version | CA1420 | | Property, type, or attribute requires runtime marshalling | CA1421 | | This method uses runtime marshalling even when the 'DisableRuntimeMarshallingAttribute' is applied | +CA1422 | | Validate platform compatibility | CA1852 | | Seal internal types | CA1853 | | Unnecessary call to 'Dictionary.ContainsKey(key)' | CA2019 | | Improper 'ThreadStatic' field initialization | diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.ObsoletedInOSPlatformTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.ObsoletedInOSPlatformTests.cs new file mode 100644 index 0000000000..d0f0655a37 --- /dev/null +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.ObsoletedInOSPlatformTests.cs @@ -0,0 +1,547 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.NetCore.Analyzers.InteropServices.PlatformCompatibilityAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.NetCore.Analyzers.InteropServices.PlatformCompatibilityAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.NetCore.Analyzers.InteropServices.UnitTests +{ + public partial class PlatformCompatabilityAnalyzerTests + { + [Fact] + public async Task ObsoletedMethodsCalledWarns() + { + var csSource = @" +using System; +using System.Runtime.Versioning; +using Mock; + +public class Test +{ + [SupportedOSPlatform(""Linux"")] + public void M1() + { + ObsoletedOnWindows10(); // Should not warn as only accessible on Linux + {|#0:ObsoletedOnLinux4()|}; // This call site is reachable on: 'Linux'. 'Test.ObsoletedOnLinux()' is obsoleted on: 'Linux' 4.1 and later. + {|CA1422:ObsoletedOnLinux4AndWindows10()|}; // This call site is reachable on: 'Linux'. 'Test.ObsoletedOnLinux4AndWindows10()' is obsoleted on: 'Linux' 4.1 and later. + } + + [ObsoletedInOSPlatform(""Linux4.1"")] + public void ObsoletedOnLinux4() { } + + [ObsoletedInOSPlatform(""Windows10.1.1.1"")] + public void ObsoletedOnWindows10() { } + + [ObsoletedInOSPlatform(""Linux4.1"", ""Use Linux4Supported"")] + [ObsoletedInOSPlatform(""Windows10.1.1.1"",""Use Windows10Supported"")] + public void ObsoletedOnLinux4AndWindows10() { } +}" + MockObsoletedAttributeCS; + await VerifyAnalyzerCSAsync(csSource, VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.ObsoletedCsReachable).WithLocation(0) + .WithArguments("Test.ObsoletedOnLinux4()", "'Linux' 4.1 and later", "'Linux'")); + + + var vbSource = @" +Imports System +Imports System.Runtime.Versioning +Imports Mock +Public Class Test + + Public Sub M1() + ObsoletedOnWindows10() + {|#0:ObsoletedOnLinux()|} ' This call site is reachable on: 'Linux'. 'Public Sub ObsoletedOnLinux()' is obsoleted on: 'Linux' 4.1 and later. + End Sub + + + Public Sub ObsoletedOnWindows10() + End Sub + + + Public Sub ObsoletedOnLinux() + End Sub +End Class +" + MockObsoletedAttributeVB; + await VerifyAnalyzerVBAsync(vbSource, VerifyVB.Diagnostic(PlatformCompatibilityAnalyzer.ObsoletedCsReachable).WithLocation(0) + .WithArguments("Public Sub ObsoletedOnLinux()", "'Linux' 4.1 and later", "'Linux'")); + } + + [Fact] + public async Task ObsoletedAndSupportedMixedDiagnostics() + { + var csSource = @" +using System; +using System.Runtime.Versioning; +using Mock; + +public class Test +{ + public void CrossPlatform() + { + {|CA1416:{|CA1422:Supported8Obsoleted10Unsupported11()|}|}; // This call site is reachable on all platforms. 'Test.Supported8Obsoleted10Unsupported11()' is only supported on: 'Windows' from version 8.0 to 11.0. + // This call site is reachable on all platforms. 'Test.Supported8Obsoleted10Unsupported11()' is obsoleted on: 'Windows' 10.0 and later. + {|CA1416:{|CA1422:Supported10.Obsoleted11Unsupported12()|}|}; // This call site is reachable on all platforms. 'Supported10.Obsoleted11Unsupported12()' is only supported on: 'Windows' from version 10.0 to 12.0. + // This call site is reachable on all platforms. 'Supported10.Obsoleted11Unsupported12()' is obsoleted on: 'Windows' 11.0 and later. + {|CA1416:{|CA1422:Supported10.Obsoleted11()|}|}; // This call site is reachable on all platforms. 'Supported10.Obsoleted11()' is only supported on: 'Windows' 10.0 and later. + // This call site is reachable on all platforms. 'Supported10.Obsoleted11()' is obsoleted on: 'Windows' 11.0 and later. + {|CA1416:{|CA1422:Supported10.Unsupported11Obsoleted12()|}|}; // This call site is reachable on all platforms. 'Supported10.Unsupported11Obsoleted12()' is only supported on: 'Windows' from version 10.0 to 11.0. + // This call site is reachable on all platforms. 'Supported10.Unsupported11Obsoleted12()' is obsoleted on: 'Windows' 12.0 and later. + {|CA1416:{|CA1422:Unsupported.Obsoleted1Windows10Linux()|}|}; // This call site is reachable on all platforms. 'Unsupported.Obsoleted1Windows10Linux()' is unsupported on: 'Windows'. + // This call site is reachable on all platforms. 'Unsupported.Obsoleted1Windows10Linux()' is obsoleted on: 'Linux'. + {|CA1416:{|CA1422:UnsupportedSupported8.Obsoleted10()|}|}; // This call site is reachable on all platforms. 'UnsupportedSupported8.Obsoleted10()' is unsupported on: 'Windows' 8.0 and before + // This call site is reachable on all platforms. 'UnsupportedSupported8.Obsoleted10()' is obsoleted on: 'Windows' 10.0 and later. + {|CA1416:{|CA1422:UnsupportedSupported8.Obsoleted10Unspported11()|}|}; // This call site is reachable on all platforms. 'UnsupportedSupported8.Obsoleted10Unspported11()' is unsupported on: 'Windows' 8.0 and before. + // This call site is reachable on all platforms. 'UnsupportedSupported8.Obsoleted10Unspported11()' is obsoleted on: 'Windows' 10.0 and later. + {|CA1416:{|CA1422:UnsupportedSupported8.ObsoletedWindows10Linux()|}|}; // This call site is reachable on all platforms. 'UnsupportedSupported8.ObsoletedWindows10Linux()' is unsupported on: 'Windows' 8.0 and before. + // This call site is reachable on all platforms. 'UnsupportedSupported8.ObsoletedWindows10Linux()' is obsoleted on: 'Linux', 'Windows' 10.0 and later. + } + + [SupportedOSPlatform(""Windows"")] + public void WindowsOnly() + { + {|CA1416:{|CA1422:Supported8Obsoleted10Unsupported11()|}|}; // This call site is reachable on: 'Windows' all versions. 'Test.Supported8Obsoleted10Unsupported11()' is only supported on: 'Windows' from version 8.0 to 11.0. + // This call site is reachable on: 'Windows' all versions. 'Test.Supported8Obsoleted10Unsupported11()' is obsoleted on: 'Windows' 10.0 and later. + {|CA1416:{|CA1422:Supported10.Obsoleted11Unsupported12()|}|}; // This call site is reachable on: 'Windows' all versions. 'Supported10.Obsoleted11Unsupported12()' is only supported on: 'Windows' from version 10.0 to 12.0. + // This call site is reachable on: 'Windows' all versions. 'Supported10.Obsoleted11Unsupported12()' is obsoleted on: 'Windows' 11.0 and later. + {|CA1416:{|CA1422:Supported10.Obsoleted11()|}|}; // This call site is reachable on: 'Windows' all versions. 'Supported10.Obsoleted11()' is only supported on: 'Windows' 10.0 and later + // This call site is reachable on: 'Windows' all versions. 'Supported10.Obsoleted11()' is obsoleted on: 'Windows' 11.0 and later. + {|CA1416:{|CA1422:Supported10.Unsupported11Obsoleted12()|}|}; // This call site is reachable on: 'Windows' all versions. 'Supported10.Unsupported11Obsoleted12()' is only supported on: 'Windows' from version 10.0 to 11.0. + // This call site is reachable on: 'Windows' all versions. 'Supported10.Unsupported11Obsoleted12()' is obsoleted on: 'Windows' 12.0 and later. + } + + [SupportedOSPlatform(""Windows8.0"")] + [ObsoletedInOSPlatform(""Windows10.0"")] + [Mock.UnsupportedOSPlatform(""Windows11.0"")] + public void Supported8Obsoleted10Unsupported11() { } +} + +[SupportedOSPlatform(""Windows10.0"")] +public class Supported10 +{ + [ObsoletedInOSPlatform(""Windows11.0"")] + [Mock.UnsupportedOSPlatform(""Windows12.0"")] + public static void Obsoleted11Unsupported12() { } + + [ObsoletedInOSPlatform(""Windows11.0"")] + public static void Obsoleted11() { } + + [ObsoletedInOSPlatform(""Windows12.0"")] + [Mock.UnsupportedOSPlatform(""Windows11.0"")] + public static void Unsupported11Obsoleted12() { } +} +[Mock.UnsupportedOSPlatform(""Windows"")] +[SupportedOSPlatform(""Windows8.0"")] +public class UnsupportedSupported8 +{ + [Mock.ObsoletedInOSPlatform(""Windows10.0"")] + public static void Obsoleted10() { } + + [Mock.ObsoletedInOSPlatform(""Linux"")] + [Mock.ObsoletedInOSPlatform(""Windows10.0"")] + public static void ObsoletedWindows10Linux() { } + + [Mock.ObsoletedInOSPlatform(""Windows10.0"")] + [Mock.UnsupportedOSPlatform(""Windows11.0"")] + public static void Obsoleted10Unspported11() { } +} +[Mock.UnsupportedOSPlatform(""Windows"")] +public class Unsupported +{ + [Mock.ObsoletedInOSPlatform(""Linux"")] + [Mock.ObsoletedInOSPlatform(""Windows10.0"")] + public static void Obsoleted1Windows10Linux() { } +} +" + MockObsoletedAttributeCS; + await VerifyAnalyzerCSAsync(csSource, s_msBuildPlatforms); + } + + [Fact] + public async Task ObsoletedWithMessageUrlCalledWarns() + { + var csSource = @" +using System; +using System.Runtime.Versioning; +using Mock; + +public class Test +{ + public void CrossPlatform() + { + {|#0:ObsoletedWithMessageAndUrl()|}; // This call site is reachable on all platforms. 'Test.ObsoletedWithMessageAndUrl()' is obsoleted on: 'Windows' 10.1.1.1 and later (Use other method instead http://www/look.for.more.info). + {|#1:ObsoletedWithMessage()|}; // This call site is reachable on all platforms. 'Test.ObsoletedWithMessage()' is obsoleted on: 'Windows' 10.1.1.1 and later (Use other method instead). + {|#2:ObsoletedWithUrl()|}; // This call site is reachable on all platforms. 'Test.ObsoletedWithUrl()' is obsoleted on: 'Windows' 10.1.1.1 and later (http://www/look.for.more.info). + } + + [ObsoletedInOSPlatform(""Windows10.1.1.1"", Url = ""http://www/look.for.more.info"")] + public void ObsoletedWithUrl() { } + [ObsoletedInOSPlatform(""Windows10.1.1.1"", ""Use other method instead"", Url = ""http://www/look.for.more.info"")] + public void ObsoletedWithMessageAndUrl() { } + [ObsoletedInOSPlatform(""Windows10.1.1.1"", ""Use other method instead"")] + public void ObsoletedWithMessage() { } +}" + MockObsoletedAttributeCS; + await VerifyAnalyzerCSAsync(csSource, s_msBuildPlatforms, VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.ObsoletedCsAllPlatforms).WithLocation(0). + WithArguments("Test.ObsoletedWithMessageAndUrl()", "'Windows' 10.1.1.1 and later (Use other method instead http://www/look.for.more.info)"), + VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.ObsoletedCsAllPlatforms).WithLocation(1). + WithArguments("Test.ObsoletedWithMessage()", "'Windows' 10.1.1.1 and later (Use other method instead)"), + VerifyCS.Diagnostic(PlatformCompatibilityAnalyzer.ObsoletedCsAllPlatforms).WithLocation(2). + WithArguments("Test.ObsoletedWithUrl()", "'Windows' 10.1.1.1 and later (http://www/look.for.more.info)")); + } + + [Fact] + public async Task ObsoletedAPIsCAlledFromDifferentCallsite() + { + var csSource = @" +using System; +using System.Runtime.Versioning; +using Mock; + +public class Test +{ + [SupportedOSPlatform(""IOS"")] + public void CallsiteReachableOnIOS() + { + ObsoletedOnWindows(); // Unreachable on windows, not warn + ObsoletedWithMessage(); + {|CA1422:ObsoletedOnIOS14()|}; // This call site is reachable on: 'IOS', 'maccatalyst'. 'Test.ObsoletedOnIOS14()' is obsoleted on: 'ios' 14.0 and later, 'maccatalyst' 14.0 and later. + } + + [Mock.UnsupportedOSPlatform(""ios13.0"")] + [System.Runtime.Versioning.UnsupportedOSPlatform(""Windows10.1.0"")] + public void SuppressedByCallsiteUnsupported() + { + ObsoletedOnWindows(); // Not supported on windows with matching version, not warn + ObsoletedWithMessage(); // Same as above + ObsoletedOnIOS14(); + } + + [Mock.UnsupportedOSPlatform(""ios15.0"")] + [System.Runtime.Versioning.UnsupportedOSPlatform(""Windows11.1.0"")] + public void NotSuppressedByCallsiteUnsupported() // obsoleted before unsupported version + { + {|CA1422:ObsoletedOnWindows()|}; // This call site is reachable on: 'Windows' 11.1.0 and before. 'Test.ObsoletedOnWindows()' is obsoleted on: 'Windows' 10.1.1.1 and later. + {|CA1422:ObsoletedWithMessage()|}; // This call site is reachable on: 'Windows' 11.1.0 and before. 'Test.ObsoletedWithMessage()' is obsoleted on: 'Windows' 10.1.1.1 and later (Use other method instead). + {|CA1422:ObsoletedOnIOS14()|}; // This call site is reachable on: 'ios' 15.0 and before, 'maccatalyst' 15.0 and before. 'Test.ObsoletedOnIOS14()' is obsoleted on: 'ios' 14.0 and later, 'maccatalyst' 14.0 and later. + } + + [ObsoletedInOSPlatform(""ios13.0"")] + [ObsoletedInOSPlatform(""Windows10.1.0"")] + public void SuppressedByCallsiteObsoleted() + { + ObsoletedOnWindows(); // All calls Obsoleted within version range, not warn + ObsoletedWithMessage(); + ObsoletedOnIOS14(); + } + + [ObsoletedInOSPlatform(""ios16.0"")] + [ObsoletedInOSPlatform(""Windows11.1.0"")] + public void NotSuppressedByCallsiteObsoleted() + { + {|CA1422:ObsoletedOnWindows()|}; //This call site is reachable on all platforms. 'Test.ObsoletedOnWindows()' is obsoleted on: 'Windows' 10.1.1.1 and later. + {|CA1422:ObsoletedWithMessage()|}; // This call site is reachable on all platforms. 'Test.ObsoletedWithMessage()' is obsoleted on: 'Windows' 10.1.1.1 and later (Use other method instead). + {|CA1422:ObsoletedOnIOS14()|}; // This call site is reachable on all platforms. 'Test.ObsoletedOnIOS14()' is obsoleted on: 'ios' 14.0 and later, 'maccatalyst' 14.0 and later. + } + + [ObsoletedInOSPlatform(""ios14.0"")] + public void ObsoletedOnIOS14() { } + + [ObsoletedInOSPlatform(""Windows10.1.1.1"")] + public void ObsoletedOnWindows() { } + [ObsoletedInOSPlatform(""Windows10.1.1.1"", ""Use other method instead"")] + public void ObsoletedWithMessage() { } +}" + MockObsoletedAttributeCS; + await VerifyAnalyzerCSAsync(csSource, s_msBuildPlatforms); + } + + [Fact] + public async Task UnsuportedWithMessageCalledWarnsAsync() + { + var csSource = @" +using System; +using System.Runtime.Versioning; +using Mock; + +public class Test +{ + public void CrossPlatform() + { + [|UnsupportedIosBrowserWatchOS()|]; // This call site is reachable on all platforms. 'Test.UnsupportedIosBrowserWatchOS()' is unsupported on: 'Browser' (Use BrowserSupported() method instead), + // 'ios' 13.0 and later (Use Test.IOsSupported() method instead), 'maccatalyst' 13.0 and later (Use Test.IOsSupported() method instead). + UnsupportedAndroid(); // Cross platform and android not in the MSBuild list so not warn + } + + [SupportedOSPlatform(""Android"")] + [SupportedOSPlatform(""browser"")] + public void ReachableOnAndroidAndBrowser() + { + [|UnsupportedIosBrowserWatchOS()|]; // This call site is reachable on: 'Android', 'browser'. 'Test.UnsupportedIosBrowserWatchOS()' is unsupported on: 'Browser' (Use BrowserSupported() method instead). + [|UnsupportedAndroid()|]; // This call site is reachable on: 'Android' all versions, 'browser'. 'Test.UnsupportedAndroid()' is unsupported on: 'Android' 21.0 and later (Use other method instead). + } + + [Mock.UnsupportedOSPlatform(""Android21.0"", ""Use other method instead"")] + public void UnsupportedAndroid() { } + + [Mock.UnsupportedOSPlatform(""ios13.0"", ""Use Test.IOsSupported() method instead"")] + [Mock.UnsupportedOSPlatform(""Browser"", ""Use BrowserSupported() method instead"")] + [Mock.UnsupportedOSPlatform(""Watchos"", ""Use WitchSupported() method instead"")] + public void UnsupportedIosBrowserWatchOS() { } +}" + MockObsoletedAttributeCS; + await VerifyAnalyzerCSAsync(csSource, s_msBuildPlatforms); + } + + [Fact] + public async Task ObsoletedWarningsGuardedWithOperatingSystemAPIs() + { + var csSource = @" +using System; +using System.Runtime.Versioning; +using Mock; + +public class Test +{ + public void CrossPlatform() + { + if (OperatingSystem.IsMacOS()) + { + {|CA1422:ObsoletedOnMacOS()|}; // This call site is reachable on: 'macOS/OSX'. 'Test.ObsoletedOnMacOS()' is obsoleted on: 'macOS/OSX'. + ObsoletedOnAndroid21(); // Call site only reachable on MacOS, no warning + ObsoletedOnLinux4AndWindows10(); // Same, no warning + } + else + { + ObsoletedOnMacOS(); + ObsoletedOnAndroid21(); // Android is not in MSBuild support list, no warning + {|CA1422:ObsoletedOnLinux4AndWindows10()|}; // This call site is reachable on all platforms. 'Test.ObsoletedOnLinux4AndWindows10()' is obsoleted on: 'Linux' 4.1 and later, Use Linux4Supported, 'Windows' 10.1.1.1 and later (Use Windows10Supported). + } + + if (OperatingSystem.IsMacOSVersionAtLeast(11)) + { + {|CA1422:ObsoletedOnMacOS()|}; // This call site is reachable on: 'macOS/OSX' 11.0 and later. 'Test.ObsoletedOnMacOS()' is obsoleted on: 'macOS/OSX' all versions. + } + else + { + {|CA1422:ObsoletedOnMacOS()|}; // It could be macos with less version, so warns: This call site is reachable on all platforms. 'Test.ObsoletedOnMacOS()' is obsoleted on: 'macOS/OSX'. + } + } + + [ObsoletedInOSPlatform(""Android21.0"")] + public void ObsoletedOnAndroid21() { } + + [ObsoletedInOSPlatform(""MacOS"")] + public void ObsoletedOnMacOS() { } + + [ObsoletedInOSPlatform(""Linux4.1"", ""Use Linux4Supported"")] + [ObsoletedInOSPlatform(""Windows10.1.1.1"",""Use Windows10Supported"")] + public void ObsoletedOnLinux4AndWindows10() { } +}" + MockObsoletedAttributeCS; + await VerifyAnalyzerCSAsync(csSource, s_msBuildPlatforms); + } + + [Fact] + public async Task ObsoletedWarningsGuardedWithOperatingSystemAPIsFromDifferentCallsite() + { + var csSource = @" +using System; +using System.Runtime.Versioning; +using Mock; + +public class Test +{ + [SupportedOSPlatform(""Android"")] + public void AndroidOnly() + { + if (OperatingSystem.IsMacOS()) + { + ObsoletedOnMacOS(); // The method is not reachable on MacOS, so no warning + ObsoletedOnAndroid21(); // Conditional only reachable on MacOS, no warning + ObsoletedOnLinux4AndWindows10(); + } + else + { + ObsoletedOnMacOS(); // Method only for android, no warning + {|CA1422:ObsoletedOnAndroid21()|}; // This call site is reachable on: 'Android'. 'Test.ObsoletedOnAndroid21()' is obsoleted on: 'Android' 21.0 and later. + ObsoletedOnLinux4AndWindows10(); // Method only for android + } + } + + [SupportedOSPlatform(""Android"")] + [SupportedOSPlatform(""MacOS"")] + public void AndroidMacOSOnly() + { + if (OperatingSystem.IsMacOS()) + { + {|CA1422:ObsoletedOnMacOS()|}; // This call site is reachable on: 'Android', 'macOS/OSX'. 'Test.ObsoletedOnMacOS()' is obsoleted on: 'macOS/OSX'. + ObsoletedOnAndroid21(); + ObsoletedOnLinux4AndWindows10(); + } + else + { + ObsoletedOnMacOS(); + {|CA1422:ObsoletedOnAndroid21()|}; // This call site is reachable on: 'Android', 'macOS/OSX'. 'Test.ObsoletedOnAndroid21()' is obsoleted on: 'Android' 21.0 and later. + ObsoletedOnLinux4AndWindows10(); + } + } + + [ObsoletedInOSPlatform(""Android21.0"")] + public void ObsoletedOnAndroid21() { } + + [ObsoletedInOSPlatform(""MacOS"")] + public void ObsoletedOnMacOS() { } + + [ObsoletedInOSPlatform(""Linux4.1"", ""Use Linux4Supported"")] + [ObsoletedInOSPlatform(""Windows10.1.1.1"",""Use Windows10Supported"")] + public void ObsoletedOnLinux4AndWindows10() { } +}" + MockObsoletedAttributeCS; + await VerifyAnalyzerCSAsync(csSource, s_msBuildPlatforms); + } + + [Fact] + public async Task ObsoletedWarningsGuardedWithUnsupportedOSPlatformGuardAttribute() + { + var csSource = @" +using System; +using System.Runtime.Versioning; +using Mock; + +public class Test +{ + [UnsupportedOSPlatformGuard(""MacOS"")] + private readonly bool _macOSNotSupported; + + [SupportedOSPlatformGuard(""Windows11.0"")] + public bool IsWindows11Supported { get; } + + [UnsupportedOSPlatformGuard(""linux"")] + [UnsupportedOSPlatformGuard(""Windows10.0"")] + private bool IsLinuxWindows10NotSupported() => true; + + public void CrossPlatform() + { + if (_macOSNotSupported) + { + ObsoletedOnMacOS(); // Guarded with the attributed field + ObsoletedOnMacOS15(); + {|CA1422:ObsoletedOnLinuxAndWindows10()|}; // This call site is reachable on all platforms. 'Test.ObsoletedOnLinux4AndWindows10()' is obsoleted on: 'Linux', 'Windows' 10.1.1.1 and later (Use Windows10Supported). + } + + if (IsWindows11Supported) + { + ObsoletedOnMacOS(); // reachable on 'Windows' 11.0 and later only + ObsoletedOnMacOS15(); + {|CA1422:ObsoletedOnLinuxAndWindows10()|}; // This call site is reachable on: 'Windows' 11.0 and later. 'Test.ObsoletedOnLinux4AndWindows10()' is obsoleted on: 'Windows' 10.1.1.1 and later (Use Windows10Supported). + } + + if (IsLinuxWindows10NotSupported()) + { + {|CA1422:ObsoletedOnMacOS()|}; // This call site is reachable on all platforms. 'Test.ObsoletedOnMacOS()' is obsoleted on: 'macOS/OSX'. + {|CA1422:ObsoletedOnMacOS15()|}; // This call site is reachable on all platforms. 'Test.ObsoletedOnMacOS15()' is obsoleted on: 'macOS/OSX' 15.0 and later. + ObsoletedOnLinuxAndWindows10(); // Guarded with the attrubuted API + } + } + + [ObsoletedInOSPlatform(""MacOS15.0"")] + public void ObsoletedOnMacOS15() { } + + [ObsoletedInOSPlatform(""MacOS"")] + public void ObsoletedOnMacOS() { } + + [ObsoletedInOSPlatform(""Linux"", ""Use LinuxSupported"")] + [ObsoletedInOSPlatform(""Windows10.1.1.1"",""Use Windows10Supported"")] + public void ObsoletedOnLinuxAndWindows10() { } +}" + MockObsoletedAttributeCS; + await VerifyAnalyzerCSAsync(csSource, s_msBuildPlatforms); + } + + private readonly string MockObsoletedAttributeCS = @" +namespace Mock +{ + public abstract class OSPlatformAttribute : Attribute + { + private protected OSPlatformAttribute(string platformName) + { + PlatformName = platformName; + } + public string PlatformName { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Enum | + AttributeTargets.Event | + AttributeTargets.Field | + AttributeTargets.Interface | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] + public sealed class UnsupportedOSPlatformAttribute : OSPlatformAttribute + { + public UnsupportedOSPlatformAttribute(string platformName) : base(platformName) + { + } + public UnsupportedOSPlatformAttribute(string platformName, string message) : base(platformName) + { + Message = message; + } + public string Message { get; } + } + + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Enum | + AttributeTargets.Event | + AttributeTargets.Field | + AttributeTargets.Interface | + 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; } + } +}"; + + private readonly string MockObsoletedAttributeVB = @" +Namespace Mock + + Public NotInheritable Class ObsoletedInOSPlatformAttribute + Inherits Attribute + + Public Sub New(ByVal platformName As String) + PlatformName = platformName + End Sub + + Public Sub New(ByVal platformName As String, ByVal message As String) + PlatformName = platformName + Message = message + End Sub + + Public ReadOnly Property PlatformName As String + Public ReadOnly Property Message As String + Public Property Url As String + End Class +End Namespace"; + } +} diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt index 428f549aad..cc1d32e615 100644 --- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt +++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -16,7 +16,7 @@ Performance: HA, CA1800-CA1854 Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5405 Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2259 Naming: CA1700-CA1727 -Interoperability: CA1400-CA1421 +Interoperability: CA1400-CA1422 Maintainability: CA1500-CA1509 Reliability: CA9998-CA9999, CA2000-CA2019 Documentation: CA1200-CA1200