Skip to content

Commit 2a09527

Browse files
authored
Block "Convert To LibraryImport" in more unsupported cases (#87503)
1 parent 9955370 commit 2a09527

File tree

4 files changed

+203
-0
lines changed

4 files changed

+203
-0
lines changed

src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/ConvertToLibraryImportAnalyzer.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,24 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo
7676
if (dllImportData == null)
7777
return;
7878

79+
if (dllImportData.ThrowOnUnmappableCharacter == true)
80+
{
81+
// LibraryImportGenerator doesn't support ThrowOnUnmappableCharacter = true
82+
return;
83+
}
84+
85+
// LibraryImportGenerator doesn't support BestFitMapping = true
86+
if (IsBestFitMapping(method, dllImportData))
87+
{
88+
return;
89+
}
90+
91+
if (method.IsVararg)
92+
{
93+
// LibraryImportGenerator doesn't support varargs
94+
return;
95+
}
96+
7997
// Ignore methods already marked LibraryImport
8098
// This can be the case when the generator creates an extern partial function for blittable signatures.
8199
foreach (AttributeData attr in method.GetAttributes())
@@ -139,6 +157,28 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo
139157
context.ReportDiagnostic(method.CreateDiagnosticInfo(ConvertToLibraryImport, properties.ToImmutable(), method.Name).ToDiagnostic());
140158
}
141159

160+
private static bool IsBestFitMapping(IMethodSymbol method, DllImportData? dllImportData)
161+
{
162+
if (dllImportData.BestFitMapping.HasValue)
163+
{
164+
return dllImportData.BestFitMapping.Value;
165+
}
166+
167+
AttributeData? bestFitMappingContainingType = method.ContainingType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.System_Runtime_InteropServices_BestFitMappingAttribute);
168+
if (bestFitMappingContainingType is not null)
169+
{
170+
return bestFitMappingContainingType.ConstructorArguments[0].Value is true;
171+
}
172+
173+
AttributeData? bestFitMappingContainingAssembly = method.ContainingAssembly.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.System_Runtime_InteropServices_BestFitMappingAttribute);
174+
if (bestFitMappingContainingAssembly is not null)
175+
{
176+
return bestFitMappingContainingAssembly.ConstructorArguments[0].Value is true;
177+
}
178+
179+
return false;
180+
}
181+
142182
private static bool HasUnsupportedMarshalAsInfo(TypePositionInfo info)
143183
{
144184
if (info.MarshallingAttributeInfo is not MarshalAsInfo(UnmanagedType unmanagedType, _))

src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,5 +154,7 @@ public static string MarshalEx(InteropGenerationOptions options)
154154
public const string System_Runtime_InteropServices_Marshalling_ComInterfaceMarshaller_Metadata = "System.Runtime.InteropServices.Marshalling.ComInterfaceMarshaller`1";
155155

156156
public const string System_Runtime_InteropServices_Marshalling_ComObject = "System.Runtime.InteropServices.Marshalling.ComObject";
157+
158+
public const string System_Runtime_InteropServices_BestFitMappingAttribute = "System.Runtime.InteropServices.BestFitMappingAttribute";
157159
}
158160
}

src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,5 +223,114 @@ unsafe partial class Test
223223
public static extern {{typeName}} {|#1:Method_Return|}();
224224
}
225225
""";
226+
227+
[Fact]
228+
public async Task BestFitMapping_True_NoDiagnostic()
229+
{
230+
string source = """
231+
using System.Runtime.InteropServices;
232+
partial class Test
233+
{
234+
[DllImport("DoesNotExist", BestFitMapping = true)]
235+
public static extern void Method2();
236+
}
237+
238+
""";
239+
await VerifyCS.VerifyAnalyzerAsync(source);
240+
}
241+
242+
[Fact]
243+
public async Task ThrowOnUnmappableChar_True_NoDiagnostic()
244+
{
245+
string source = """
246+
using System.Runtime.InteropServices;
247+
partial class Test
248+
{
249+
[DllImport("DoesNotExist", ThrowOnUnmappableChar = true)]
250+
public static extern void Method2();
251+
}
252+
253+
""";
254+
await VerifyCS.VerifyAnalyzerAsync(source);
255+
}
256+
257+
[Fact]
258+
public async Task BestFitMapping_Assembly_True_NoDiagnostic()
259+
{
260+
string source = """
261+
using System.Runtime.InteropServices;
262+
[assembly:BestFitMapping(true)]
263+
partial class Test
264+
{
265+
[DllImport("DoesNotExist")]
266+
public static extern void Method2();
267+
}
268+
269+
""";
270+
await VerifyCS.VerifyAnalyzerAsync(source);
271+
}
272+
273+
[Fact]
274+
public async Task BestFitMapping_Type_True_NoDiagnostic()
275+
{
276+
string source = """
277+
using System.Runtime.InteropServices;
278+
[BestFitMapping(true)]
279+
partial class Test
280+
{
281+
[DllImport("DoesNotExist")]
282+
public static extern void Method2();
283+
}
284+
285+
""";
286+
await VerifyCS.VerifyAnalyzerAsync(source);
287+
}
288+
289+
[Fact]
290+
public async Task BestFitMapping_Assembly_False_Diagnostic()
291+
{
292+
string source = """
293+
using System.Runtime.InteropServices;
294+
[assembly:BestFitMapping(false)]
295+
partial class Test
296+
{
297+
[DllImport("DoesNotExist")]
298+
public static extern void [|Method2|]();
299+
}
300+
301+
""";
302+
await VerifyCS.VerifyAnalyzerAsync(source);
303+
}
304+
305+
[Fact]
306+
public async Task BestFitMapping_Type_False_Diagnostic()
307+
{
308+
string source = """
309+
using System.Runtime.InteropServices;
310+
[BestFitMapping(false)]
311+
partial class Test
312+
{
313+
[DllImport("DoesNotExist")]
314+
public static extern void [|Method2|]();
315+
}
316+
317+
""";
318+
await VerifyCS.VerifyAnalyzerAsync(source);
319+
}
320+
321+
[Fact]
322+
public async Task Varargs_NoDiagnostic()
323+
{
324+
string source = """
325+
using System.Runtime.InteropServices;
326+
partial class Test
327+
{
328+
[DllImport("DoesNotExist")]
329+
public static extern void Method2(__arglist);
330+
}
331+
332+
""";
333+
await VerifyCS.VerifyAnalyzerAsync(source);
334+
}
226335
}
227336
}

src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportFixerTests.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,58 @@ await VerifyCodeFixAsync(
11091109
new Dictionary<string, Option> { { Option.MayRequireAdditionalWork, new Option.Bool(true) } });
11101110
}
11111111

1112+
[Fact]
1113+
public async Task BestFitMappingExplicitFalse()
1114+
{
1115+
string source = """
1116+
using System.Runtime.InteropServices;
1117+
partial class Test
1118+
{
1119+
[DllImport("DoesNotExist", BestFitMapping = false)]
1120+
public static extern void [|Method|]();
1121+
}
1122+
1123+
""";
1124+
1125+
string fixedSource = """
1126+
using System.Runtime.InteropServices;
1127+
partial class Test
1128+
{
1129+
[LibraryImport("DoesNotExist")]
1130+
public static partial void {|CS8795:Method|}();
1131+
}
1132+
1133+
""";
1134+
1135+
await VerifyCodeFixAsync(source, fixedSource);
1136+
}
1137+
1138+
[Fact]
1139+
public async Task ThrowOnUnmappableCharExplicitFalse()
1140+
{
1141+
string source = """
1142+
using System.Runtime.InteropServices;
1143+
partial class Test
1144+
{
1145+
[DllImport("DoesNotExist", ThrowOnUnmappableChar = false)]
1146+
public static extern void [|Method|]();
1147+
}
1148+
1149+
""";
1150+
1151+
string fixedSource = """
1152+
using System.Runtime.InteropServices;
1153+
partial class Test
1154+
{
1155+
[LibraryImport("DoesNotExist")]
1156+
public static partial void {|CS8795:Method|}();
1157+
}
1158+
1159+
""";
1160+
1161+
await VerifyCodeFixAsync(source, fixedSource);
1162+
}
1163+
11121164
private static async Task VerifyCodeFixAsync(string source, string fixedSource)
11131165
{
11141166
var test = new VerifyCS.Test

0 commit comments

Comments
 (0)