diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs index 6086108e20..6265980e1d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs @@ -563,10 +563,13 @@ static bool IsKnownValueGuarded( } } - if (attribute.SupportedFirst != null && - info.Version.IsGreaterThanOrEqualTo(attribute.SupportedFirst)) + var checkVersion = attribute.SupportedSecond ?? attribute.SupportedFirst; + + if (checkVersion != null && + info.Version.IsGreaterThanOrEqualTo(checkVersion)) { attribute.SupportedFirst = null; + attribute.SupportedSecond = null; RemoveUnsupportedWithLessVersion(info.Version, attribute); RemoveOtherSupportsOnDifferentPlatforms(attributes, info.PlatformName); } @@ -816,8 +819,11 @@ static void ReportSupportedDiagnostic(IOperation operation, OperationBlockAnalys csPlatformNames = string.Join(CommaSeparator, csPlatformNames, PlatformCompatibilityAllPlatforms); } - var rule = supportedRule ? SwitchSupportedRule(callsite) : SwitchRule(callsite, true); - context.ReportDiagnostic(operation.CreateDiagnostic(rule, operationName, JoinNames(platformNames), csPlatformNames)); + if (!platformNames.IsEmpty) + { + var rule = supportedRule ? SwitchSupportedRule(callsite) : SwitchRule(callsite, true); + context.ReportDiagnostic(operation.CreateDiagnostic(rule, operationName, JoinNames(platformNames), csPlatformNames)); + } if (!obsoletedPlatforms.IsEmpty) { diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index 09d8fc98a0..7518811391 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -5,7 +5,7 @@ { "tool": { "name": "Microsoft.CodeAnalysis.CSharp.NetAnalyzers", - "version": "7.0.0", + "version": "7.0.1", "language": "en-US" }, "rules": { @@ -538,7 +538,7 @@ { "tool": { "name": "Microsoft.CodeAnalysis.NetAnalyzers", - "version": "7.0.0", + "version": "7.0.1", "language": "en-US" }, "rules": { @@ -5815,7 +5815,7 @@ { "tool": { "name": "Microsoft.CodeAnalysis.VisualBasic.NetAnalyzers", - "version": "7.0.0", + "version": "7.0.1", "language": "en-US" }, "rules": { diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index b3c8905c00..75e0f77588 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -2,8 +2,3 @@ Rule ID | Missing Help Link | Title | --------|-------------------|-------| -CA1311 | | Specify a culture or use an invariant version | -CA1421 | | This method uses runtime marshalling even when the 'DisableRuntimeMarshallingAttribute' is applied | -CA1852 | | Seal internal types | -CA1853 | | Unnecessary call to 'Dictionary.ContainsKey(key)' | -CA1855 | | Prefer 'Clear' over 'Fill' | diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.ObsoletedOSPlatformTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.ObsoletedOSPlatformTests.cs index c841e2f33a..2ef6414a7a 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.ObsoletedOSPlatformTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.ObsoletedOSPlatformTests.cs @@ -454,6 +454,37 @@ public void ObsoletedOnLinuxAndWindows10() { } await VerifyAnalyzerCSAsync(csSource, s_msBuildPlatforms); } + [Fact] + public async Task CalledApiHasSupportedAndObsoletedAttributes_CallsiteSupressesSupportedAttributeWarnsForObsoletedOnly() + { + var source = @" +using System; +using System.Runtime.Versioning; + +class Program +{ + [Mock.ObsoletedOSPlatform(""ios7.0"", ""Use 'NSString.GetBoundingRect (CGSize, NSStringDrawingOptions, UIStringAttributes, NSStringDrawingContext)' instead."")] + [Mock.ObsoletedOSPlatform(""maccatalyst7.0"", ""Use 'NSString.GetBoundingRect (CGSize, NSStringDrawingOptions, UIStringAttributes, NSStringDrawingContext)' instead."")] + [SupportedOSPlatform(""ios"")] + [SupportedOSPlatform(""maccatalyst"")] + public static void M3() { } + + [SupportedOSPlatform(""ios"")] + public static void M1() + { + {|CA1422:M3()|}; // This call site is reachable on: 'ios', 'maccatalyst'. 'Program.M3()' is obsoleted on: 'ios' 7.0 and later (Use 'NSString.GetBoundingRect (CGSize, NSStringDrawingOptions, UIStringAttributes, NSStringDrawingContext)' instead.), 'maccatalyst' 7.0 and later (Use 'NSString.GetBoundingRect (CGSize, NSStringDrawingOptions, UIStringAttributes, NSStringDrawingContext)' instead.). + } + + [SupportedOSPlatform(""ios10.0"")] + public static void M2() + { + {|CA1422:M3()|}; // This call site is reachable on: 'ios' 10.0 and later, 'maccatalyst' 10.0 and later. 'Program.M3()' is obsoleted on: 'ios' 7.0 and later (Use 'NSString.GetBoundingRect (CGSize, NSStringDrawingOptions, UIStringAttributes, NSStringDrawingContext)' instead.), 'maccatalyst' 7.0 and later (Use 'NSString.GetBoundingRect (CGSize, NSStringDrawingOptions, UIStringAttributes, NSStringDrawingContext)' instead.). + } +}" + MockObsoletedAttributeCS; + + await VerifyAnalyzerCSAsync(source); + } + private readonly string MockObsoletedAttributeCS = @" namespace Mock { diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs index bb5d77427b..f39716ecde 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs @@ -4968,6 +4968,39 @@ void M1() await VerifyAnalyzerCSAsync(source); } + [Fact, WorkItem(4372, "https://github.com/dotnet/roslyn-analyzers/issues/6158")] + public async Task ChildApiNarrowedParentSupport_GuardingVersionShouldBeComparedWithChildVersion() + { + var source = @" +using System; +using System.Runtime.Versioning; + +[SupportedOSPlatform(""ios"")] +[SupportedOSPlatform(""tvos"")] +[SupportedOSPlatform(""maccatalyst"")] +class Program +{ + [SupportedOSPlatform(""tvos10.2"")] + [SupportedOSPlatform(""ios10.3"")] + [SupportedOSPlatform(""maccatalyst10.3"")] + public static int P1 => 1; +} +class Test +{ + [SupportedOSPlatform(""ios10.0"")] + public void M1() + { + var rate = (OperatingSystem.IsIOSVersionAtLeast(10, 3) || OperatingSystem.IsMacCatalystVersionAtLeast(10, 3) || OperatingSystem.IsTvOSVersionAtLeast(10, 3)) + ? Program.P1 : 0; // guarded + + if (OperatingSystem.IsIOSVersionAtLeast(10, 3) || OperatingSystem.IsMacCatalystVersionAtLeast(10, 3) || OperatingSystem.IsTvOSVersionAtLeast(10)) + rate = [|Program.P1|]; // version of TvOS is not guarded + } +}"; + + await VerifyAnalyzerCSAsync(source); + } + [Fact] public async Task ApiAndGuardAttributeBothHasVersions_AttributeVersionWins() {