From 2a09527f02086a4b6d39c1a648ce5ab93488dc15 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 22 Jun 2023 13:32:24 -0700 Subject: [PATCH] Block "Convert To LibraryImport" in more unsupported cases (#87503) --- .../Analyzers/ConvertToLibraryImportAnalyzer.cs | 40 ++++++++ .../TypeNames.cs | 2 + .../ConvertToLibraryImportAnalyzerTests.cs | 109 +++++++++++++++++++++ .../ConvertToLibraryImportFixerTests.cs | 52 ++++++++++ 4 files changed, 203 insertions(+) diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ConvertToLibraryImportAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ConvertToLibraryImportAnalyzer.cs index 0789028..f0e9ab5 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ConvertToLibraryImportAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ConvertToLibraryImportAnalyzer.cs @@ -76,6 +76,24 @@ namespace Microsoft.Interop.Analyzers if (dllImportData == null) return; + if (dllImportData.ThrowOnUnmappableCharacter == true) + { + // LibraryImportGenerator doesn't support ThrowOnUnmappableCharacter = true + return; + } + + // LibraryImportGenerator doesn't support BestFitMapping = true + if (IsBestFitMapping(method, dllImportData)) + { + return; + } + + if (method.IsVararg) + { + // LibraryImportGenerator doesn't support varargs + return; + } + // Ignore methods already marked LibraryImport // This can be the case when the generator creates an extern partial function for blittable signatures. foreach (AttributeData attr in method.GetAttributes()) @@ -139,6 +157,28 @@ namespace Microsoft.Interop.Analyzers context.ReportDiagnostic(method.CreateDiagnosticInfo(ConvertToLibraryImport, properties.ToImmutable(), method.Name).ToDiagnostic()); } + private static bool IsBestFitMapping(IMethodSymbol method, DllImportData? dllImportData) + { + if (dllImportData.BestFitMapping.HasValue) + { + return dllImportData.BestFitMapping.Value; + } + + AttributeData? bestFitMappingContainingType = method.ContainingType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.System_Runtime_InteropServices_BestFitMappingAttribute); + if (bestFitMappingContainingType is not null) + { + return bestFitMappingContainingType.ConstructorArguments[0].Value is true; + } + + AttributeData? bestFitMappingContainingAssembly = method.ContainingAssembly.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.System_Runtime_InteropServices_BestFitMappingAttribute); + if (bestFitMappingContainingAssembly is not null) + { + return bestFitMappingContainingAssembly.ConstructorArguments[0].Value is true; + } + + return false; + } + private static bool HasUnsupportedMarshalAsInfo(TypePositionInfo info) { if (info.MarshallingAttributeInfo is not MarshalAsInfo(UnmanagedType unmanagedType, _)) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs index 04602aa..7f869fe 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs @@ -154,5 +154,7 @@ namespace Microsoft.Interop public const string System_Runtime_InteropServices_Marshalling_ComInterfaceMarshaller_Metadata = "System.Runtime.InteropServices.Marshalling.ComInterfaceMarshaller`1"; public const string System_Runtime_InteropServices_Marshalling_ComObject = "System.Runtime.InteropServices.Marshalling.ComObject"; + + public const string System_Runtime_InteropServices_BestFitMappingAttribute = "System.Runtime.InteropServices.BestFitMappingAttribute"; } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs index 3288160..854e58c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs @@ -223,5 +223,114 @@ namespace LibraryImportGenerator.UnitTests public static extern {{typeName}} {|#1:Method_Return|}(); } """; + + [Fact] + public async Task BestFitMapping_True_NoDiagnostic() + { + string source = """ + using System.Runtime.InteropServices; + partial class Test + { + [DllImport("DoesNotExist", BestFitMapping = true)] + public static extern void Method2(); + } + + """; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task ThrowOnUnmappableChar_True_NoDiagnostic() + { + string source = """ + using System.Runtime.InteropServices; + partial class Test + { + [DllImport("DoesNotExist", ThrowOnUnmappableChar = true)] + public static extern void Method2(); + } + + """; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task BestFitMapping_Assembly_True_NoDiagnostic() + { + string source = """ + using System.Runtime.InteropServices; + [assembly:BestFitMapping(true)] + partial class Test + { + [DllImport("DoesNotExist")] + public static extern void Method2(); + } + + """; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task BestFitMapping_Type_True_NoDiagnostic() + { + string source = """ + using System.Runtime.InteropServices; + [BestFitMapping(true)] + partial class Test + { + [DllImport("DoesNotExist")] + public static extern void Method2(); + } + + """; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task BestFitMapping_Assembly_False_Diagnostic() + { + string source = """ + using System.Runtime.InteropServices; + [assembly:BestFitMapping(false)] + partial class Test + { + [DllImport("DoesNotExist")] + public static extern void [|Method2|](); + } + + """; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task BestFitMapping_Type_False_Diagnostic() + { + string source = """ + using System.Runtime.InteropServices; + [BestFitMapping(false)] + partial class Test + { + [DllImport("DoesNotExist")] + public static extern void [|Method2|](); + } + + """; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task Varargs_NoDiagnostic() + { + string source = """ + using System.Runtime.InteropServices; + partial class Test + { + [DllImport("DoesNotExist")] + public static extern void Method2(__arglist); + } + + """; + await VerifyCS.VerifyAnalyzerAsync(source); + } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportFixerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportFixerTests.cs index 4bc8194..0dfe12b 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportFixerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportFixerTests.cs @@ -1109,6 +1109,58 @@ namespace LibraryImportGenerator.UnitTests new Dictionary { { Option.MayRequireAdditionalWork, new Option.Bool(true) } }); } + [Fact] + public async Task BestFitMappingExplicitFalse() + { + string source = """ + using System.Runtime.InteropServices; + partial class Test + { + [DllImport("DoesNotExist", BestFitMapping = false)] + public static extern void [|Method|](); + } + + """; + + string fixedSource = """ + using System.Runtime.InteropServices; + partial class Test + { + [LibraryImport("DoesNotExist")] + public static partial void {|CS8795:Method|}(); + } + + """; + + await VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ThrowOnUnmappableCharExplicitFalse() + { + string source = """ + using System.Runtime.InteropServices; + partial class Test + { + [DllImport("DoesNotExist", ThrowOnUnmappableChar = false)] + public static extern void [|Method|](); + } + + """; + + string fixedSource = """ + using System.Runtime.InteropServices; + partial class Test + { + [LibraryImport("DoesNotExist")] + public static partial void {|CS8795:Method|}(); + } + + """; + + await VerifyCodeFixAsync(source, fixedSource); + } + private static async Task VerifyCodeFixAsync(string source, string fixedSource) { var test = new VerifyCS.Test -- 2.7.4