Block "Convert To LibraryImport" in more unsupported cases (#87503)
authorJeremy Koritzinsky <jekoritz@microsoft.com>
Thu, 22 Jun 2023 20:32:24 +0000 (13:32 -0700)
committerGitHub <noreply@github.com>
Thu, 22 Jun 2023 20:32:24 +0000 (13:32 -0700)
src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ConvertToLibraryImportAnalyzer.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportFixerTests.cs

index 0789028..f0e9ab5 100644 (file)
@@ -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, _))
index 04602aa..7f869fe 100644 (file)
@@ -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";
     }
 }
index 3288160..854e58c 100644 (file)
@@ -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);
+        }
     }
 }
index 4bc8194..0dfe12b 100644 (file)
@@ -1109,6 +1109,58 @@ namespace LibraryImportGenerator.UnitTests
                 new Dictionary<string, Option> { { 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