Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,24 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo
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())
Expand Down Expand Up @@ -139,6 +157,28 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo
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, _))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,5 +152,7 @@ public static string MarshalEx(InteropGenerationOptions options)
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";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -223,5 +223,114 @@ unsafe partial class Test
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,58 @@ await VerifyCodeFixAsync(
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
Expand Down