From 094e3b0cc40b617863f978681c6b91b30058d567 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 11 Jun 2024 14:33:10 -0700 Subject: [PATCH 1/4] Do not warn when invoking APIs that has SupportedOSPlatformGuard attribute --- .../PlatformCompatibilityAnalyzer.cs | 12 +-- ...tibilityAnalyzerTests.GuardedCallsTests.cs | 73 ++++++++++++++++++- 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs index d38d19b817..1bf6950d50 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs @@ -1407,7 +1407,8 @@ void CheckOperationAttributes(ISymbol symbol, bool checkParents) platformSpecificOperations.TryAdd(new KeyValuePair(operation, symbol), (notSuppressedAttributes, callSiteAttributes.Platforms)); } } - else if (TryCopyAttributesNotSuppressedByMsBuild(operationAttributes.Platforms!, msBuildPlatforms, out var copiedAttributes)) + else if (!OperationHasOnlyAssemblyAttributesAndCalledFromSameAssembly(operationAttributes, symbol, containingSymbol) && + TryCopyAttributesNotSuppressedByMsBuild(operationAttributes.Platforms!, msBuildPlatforms, out var copiedAttributes)) { platformSpecificOperations.TryAdd(new KeyValuePair(operation, symbol), (copiedAttributes, null)); } @@ -1415,6 +1416,9 @@ void CheckOperationAttributes(ISymbol symbol, bool checkParents) } } + private static bool OperationHasOnlyAssemblyAttributesAndCalledFromSameAssembly(PlatformAttributes operationAttributes, ISymbol symbol, ISymbol containingSymbol) => + operationAttributes.IsAssemblyAttribute && containingSymbol.ContainingAssembly == symbol.ContainingAssembly; + private static bool UsedInCreatingNotSupportedException(IArgumentOperation operation, ITypeSymbol? notSupportedExceptionType) { if (operation.Parent is IObjectCreationOperation creation && @@ -1799,11 +1803,7 @@ static void MergePlatformAttributes(ImmutableArray immediateAttri if (attribute.AttributeClass.Name is SupportedOSPlatformGuardAttribute or UnsupportedOSPlatformGuardAttribute) { - if (!parentAttributes.IsAssemblyAttribute) - { - parentAttributes = new PlatformAttributes(); - } - + parentAttributes = new PlatformAttributes(); // The API is for guard, clear parent attributes return; } 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 a230b74ec1..4c75ebff72 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; using Test.Utilities; using Xunit; @@ -1216,6 +1217,76 @@ static void M1() await VerifyAnalyzerCSAsync(csSource); } + [Fact] + public async Task CallGuardFromPlatformSpecificAssembly() + { + string csDependencyCode = @" +public class Library +{ + [System.Runtime.Versioning.SupportedOSPlatformGuard(""windows"")] + [System.Runtime.Versioning.SupportedOSPlatformGuard(""linux"")] + public static bool IsSupported => false; + + public static void AMethod() { } +}"; + csDependencyCode = @$"[assembly: System.Runtime.Versioning.SupportedOSPlatform(""windows"")] + [assembly: System.Runtime.Versioning.SupportedOSPlatform(""linux"")] + {csDependencyCode}"; + + string csCurrentAssemblyCode = @" +using System; + +public class Program +{ + public void ProgramMethod() + { + [|Library.AMethod()|]; // Not guarded, warns + if (Library.IsSupported) + { + Library.AMethod(); + } + } +}"; + var test = SetupDependencyAndTestCSWithOneSourceFile(csCurrentAssemblyCode, csDependencyCode); + test.TestState.AnalyzerConfigFiles.Add(("/.editorconfig", $@"root = true + +[*] +build_property.TargetFramework = net5 +")); + await test.RunAsync(); + } + + private static VerifyCS.Test SetupDependencyAndTestCSWithOneSourceFile(string csInput, string csDependencyCode) + { + return new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp10, + MarkupOptions = MarkupOptions.UseFirstDescriptor, + TestState = + { + Sources = + { + csInput + }, + AdditionalProjects = + { + ["PreviewAssembly"] = + { + Sources = + { + ("/PreviewAssembly/AssemblyInfo.g.cs", csDependencyCode) + }, + }, + }, + AdditionalProjectReferences = + { + "PreviewAssembly", + }, + }, + }; + } + [Fact] public async Task GuardedWith_RuntimeInformation_IsOSPlatform_SimpleIfElseAsync() { @@ -4181,7 +4252,7 @@ public async Task GuardCallingCachedValue_CallSiteHasAssemblyAttributeAsync() [assembly: SupportedOSPlatform(""ios10.0"")] class Test { - static bool s_isiOS11OrNewer => false; + static bool s_isiOS11OrNewer = false; [SupportedOSPlatformGuard(""ios11.0"")] private bool IsIos11Supported() => s_isiOS11OrNewer; // should not warn From 9c924f87423b9ab5bd9146ef981f67ddc17add44 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Wed, 12 Jun 2024 16:46:27 -0700 Subject: [PATCH 2/4] dotnet build /t:pack update --- src/NetAnalyzers/RulesMissingDocumentation.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index f0385ea23d..85974017d4 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -2,7 +2,5 @@ Rule ID | Missing Help Link | Title | --------|-------------------|-------| -CA1871 | | Do not pass a nullable struct to 'ArgumentNullException.ThrowIfNull' | CA2022 | | Avoid inexact read with 'Stream.Read' | -CA2264 | | Do not pass a non-nullable value to 'ArgumentNullException.ThrowIfNull' | CA2265 | | Do not compare Span\ to 'null' or 'default' | From 5725558b96b1b5ce9b76adc734ac3ad2fb6e28fe Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 13 Jun 2024 16:13:01 -0700 Subject: [PATCH 3/4] Add more scenarios in the test --- ...tibilityAnalyzerTests.GuardedCallsTests.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) 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 4c75ebff72..71a11fcc72 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.GuardedCallsTests.cs @@ -1223,9 +1223,15 @@ public async Task CallGuardFromPlatformSpecificAssembly() string csDependencyCode = @" public class Library { + static bool s_isWindowsOrLinux = false; + [System.Runtime.Versioning.SupportedOSPlatformGuard(""windows"")] [System.Runtime.Versioning.SupportedOSPlatformGuard(""linux"")] - public static bool IsSupported => false; + public static bool IsSupported => s_isWindowsOrLinux; + + [System.Runtime.Versioning.UnsupportedOSPlatformGuard(""windows"")] + [System.Runtime.Versioning.UnsupportedOSPlatformGuard(""linux"")] + public static bool IsNotSupported => false; public static void AMethod() { } }"; @@ -1245,6 +1251,15 @@ public void ProgramMethod() { Library.AMethod(); } + + if (Library.IsNotSupported) + { + [|Library.AMethod()|]; // warn because guarded by unsupported + } + else + { + Library.AMethod(); // guarded + } } }"; var test = SetupDependencyAndTestCSWithOneSourceFile(csCurrentAssemblyCode, csDependencyCode); @@ -1275,7 +1290,7 @@ private static VerifyCS.Test SetupDependencyAndTestCSWithOneSourceFile(string cs { Sources = { - ("/PreviewAssembly/AssemblyInfo.g.cs", csDependencyCode) + ("/PreviewAssembly/AssemblyInfo.cs", csDependencyCode) }, }, }, From a532d1c8a7b32d6238f82f75cf8df635b3c3da13 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Thu, 13 Jun 2024 17:03:24 -0700 Subject: [PATCH 4/4] Again doc updated --- src/NetAnalyzers/RulesMissingDocumentation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index 85974017d4..d19d2205c7 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -4,3 +4,4 @@ Rule ID | Missing Help Link | Title | --------|-------------------|-------| CA2022 | | Avoid inexact read with 'Stream.Read' | CA2265 | | Do not compare Span\ to 'null' or 'default' | +CA5360 | | Do Not Call Dangerous Methods In Deserialization |