diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 114a4c53..a47400de 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -1589,10 +1589,12 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod var remappedName = FixupNameForMultipleHits(cxxMethodDecl); var name = GetRemappedCursorName(cxxMethodDecl); var needsReturnFixup = false; + var needsCastToTransparentStruct = false; if (returnType.Kind != CXTypeKind.CXType_Void) { needsReturnFixup = NeedsReturnFixup(cxxMethodDecl); + needsCastToTransparentStruct = _config.WithTransparentStructs.TryGetValue(returnTypeName, out var transparentStruct) && IsTransparentStructHandle(transparentStruct.Kind); } var desc = new FunctionOrDelegateDesc<(string Name, PInvokeGenerator This)> @@ -1652,6 +1654,13 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod body.Write("return "); } + if (needsCastToTransparentStruct) + { + body.Write("(("); + body.Write(returnTypeName); + body.Write(")("); + } + if (needsReturnFixup) { body.Write('*'); @@ -1748,6 +1757,11 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod body.Write(" != 0"); } + if (needsCastToTransparentStruct) + { + body.Write("))"); + } + body.WriteSemicolon(); body.WriteNewline(); @@ -2743,9 +2757,9 @@ private void VisitVarDecl(VarDecl varDecl) { flags |= ValueFlags.Copy; } - else if (_config.WithTransparentStructs.TryGetValue(typeName, out var transparentValueTypeName)) + else if (_config.WithTransparentStructs.TryGetValue(typeName, out var transparentStruct)) { - typeName = transparentValueTypeName; + typeName = transparentStruct.Name; } } else if ((varDecl.StorageClass == CX_StorageClass.CX_SC_Static) || openedOutputBuilder) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index 7e614b97..c93ca931 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -702,9 +702,9 @@ private void VisitExplicitCastExpr(ExplicitCastExpr explicitCastExpr) { var cursorName = GetCursorName(varDecl); - if (cursorName.StartsWith("ClangSharpMacro_") && _config.WithTransparentStructs.TryGetValue(typeName, out var transparentValueTypeName)) + if (cursorName.StartsWith("ClangSharpMacro_") && _config.WithTransparentStructs.TryGetValue(typeName, out var transparentStruct)) { - typeName = transparentValueTypeName; + typeName = transparentStruct.Name; } } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 480dcb63..786135f4 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -427,7 +427,11 @@ static void GenerateTransparentStructs(PInvokeGenerator generator) foreach (var transparentStruct in config.WithTransparentStructs) { - var outputPath = Path.Combine(config.OutputLocation, $"{transparentStruct.Key}.cs"); + var name = transparentStruct.Key; + var type = transparentStruct.Value.Name; + var kind = transparentStruct.Value.Kind; + + var outputPath = Path.Combine(config.OutputLocation, $"{name}.cs"); using var sw = new StreamWriter(outputPath); sw.NewLine = "\n"; @@ -437,7 +441,8 @@ static void GenerateTransparentStructs(PInvokeGenerator generator) sw.WriteLine(config.HeaderText); } - var isUnsafe = transparentStruct.Key.Contains('*'); + var isUnsafe = type.Contains('*') || IsTransparentStructHandle(kind); + var secondaryType = GetSecondaryType(type); sw.WriteLine("using System;"); sw.WriteLine(); @@ -454,65 +459,370 @@ static void GenerateTransparentStructs(PInvokeGenerator generator) } sw.Write("partial struct "); - sw.Write(transparentStruct.Key); + sw.Write(name); sw.Write(" : IEquatable<"); - sw.Write(transparentStruct.Key); + sw.Write(name); sw.WriteLine('>'); sw.WriteLine(" {"); sw.Write(" public readonly "); - sw.Write(transparentStruct.Value); + sw.Write(type); sw.WriteLine(" Value;"); sw.WriteLine(); - sw.Write(" public "); - sw.Write(transparentStruct.Key); - sw.Write('('); - sw.Write(transparentStruct.Value); - sw.WriteLine(" value)"); - sw.WriteLine(" {"); - sw.WriteLine(" Value = value;"); - sw.WriteLine(" }"); - sw.WriteLine(); + if (kind == PInvokeGeneratorTransparentStructKind.Win32Handle) + { + sw.Write(" public static "); + sw.Write(name); + sw.Write(" NULL => ("); + sw.Write(name); + sw.WriteLine(")(0);"); + sw.WriteLine(); + } + + switch (kind) + { + case PInvokeGeneratorTransparentStructKind.Typedef: + { + // Typedefs can be created directly from the underlying type + // and from the secondary type if one exists + + sw.Write(" public "); + sw.Write(name); + sw.Write('('); + sw.Write(type); + sw.WriteLine(" value)"); + sw.WriteLine(" {"); + sw.WriteLine(" Value = value;"); + sw.WriteLine(" }"); + sw.WriteLine(); + + if (!string.IsNullOrEmpty(secondaryType)) + { + sw.Write(" public "); + sw.Write(name); + sw.Write('('); + sw.Write(secondaryType); + sw.WriteLine(" value)"); + sw.WriteLine(" {"); + sw.Write(" Value = (("); + sw.Write(type); + sw.WriteLine(")(value));"); + sw.WriteLine(" }"); + sw.WriteLine(); + } + + break; + } + + case PInvokeGeneratorTransparentStructKind.Handle: + case PInvokeGeneratorTransparentStructKind.Win32Handle: + { + // Handles can be created from nint, nuint, or void* + // We also provide int and uint overloads for convenience + + sw.Write(" public "); + sw.Write(name); + sw.WriteLine("(int value)"); + sw.WriteLine(" {"); + sw.Write(" Value = (("); + sw.Write(type); + sw.WriteLine(")(value));"); + sw.WriteLine(" }"); + sw.WriteLine(); + + sw.Write(" public "); + sw.Write(name); + sw.WriteLine("(uint value)"); + sw.WriteLine(" {"); + sw.Write(" Value = (("); + sw.Write(type); + sw.WriteLine(")(value));"); + sw.WriteLine(" }"); + sw.WriteLine(); + + sw.Write(" public "); + sw.Write(name); + sw.WriteLine("(nint value)"); + sw.WriteLine(" {"); + sw.Write(" Value = (("); + sw.Write(type); + sw.WriteLine(")(value));"); + sw.WriteLine(" }"); + sw.WriteLine(); + + sw.Write(" public "); + sw.Write(name); + sw.WriteLine("(nuint value)"); + sw.WriteLine(" {"); + sw.Write(" Value = (("); + sw.Write(type); + sw.WriteLine(")(value));"); + sw.WriteLine(" }"); + sw.WriteLine(); + + sw.Write(" public "); + sw.Write(name); + sw.WriteLine("(void* value)"); + sw.WriteLine(" {"); + sw.Write(" Value = (("); + sw.Write(type); + sw.WriteLine(")(value));"); + sw.WriteLine(" }"); + sw.WriteLine(); + + if (kind == PInvokeGeneratorTransparentStructKind.Win32Handle) + { + // Win32 Handles have additional support for creation from HANDLE + + sw.Write(" public "); + sw.Write(name); + sw.WriteLine("(HANDLE value)"); + sw.WriteLine(" {"); + sw.WriteLine(" Value = value.Value;"); + sw.WriteLine(" }"); + sw.WriteLine(); + } + + break; + } + + case PInvokeGeneratorTransparentStructKind.Boolean: + { + // Booleans can be created directly from the underlying type or bool + + sw.Write(" public "); + sw.Write(name); + sw.Write('('); + sw.Write(type); + sw.WriteLine(" value)"); + sw.WriteLine(" {"); + sw.WriteLine(" Value = value;"); + sw.WriteLine(" }"); + sw.WriteLine(); + + sw.Write(" public "); + sw.Write(name); + sw.WriteLine("(bool value)"); + sw.WriteLine(" {"); + sw.Write(" Value = (("); + sw.Write(type); + sw.WriteLine(")(value ? 1 : 0));"); + sw.WriteLine(" }"); + sw.WriteLine(); + + break; + } + } sw.Write(" public static bool operator ==("); - sw.Write(transparentStruct.Key); + sw.Write(name); sw.Write(" left, "); - sw.Write(transparentStruct.Key); + sw.Write(name); sw.WriteLine(" right) => left.Value == right.Value;"); sw.WriteLine(); sw.Write(" public static bool operator !=("); - sw.Write(transparentStruct.Key); + sw.Write(name); sw.Write(" left, "); - sw.Write(transparentStruct.Key); + sw.Write(name); sw.WriteLine(" right) => left.Value != right.Value;"); sw.WriteLine(); - sw.Write(" public static implicit operator "); - sw.Write(transparentStruct.Key); - sw.Write('('); - sw.Write(transparentStruct.Value); - sw.Write(" value) => new "); - sw.Write(transparentStruct.Key); - sw.WriteLine("(value);"); - sw.WriteLine(); + switch (kind) + { + case PInvokeGeneratorTransparentStructKind.Typedef: + { + // Typedefs can implicitly convert to and from the underlying type + // They can also explicitly convert to and from the secondary type, if one exists - sw.Write(" public static implicit operator "); - sw.Write(transparentStruct.Value); - sw.Write('('); - sw.Write(transparentStruct.Key); - sw.WriteLine(" value) => value.Value;"); - sw.WriteLine(); + if (!string.IsNullOrEmpty(secondaryType)) + { + sw.Write(" public static explicit operator "); + sw.Write(name); + sw.Write('('); + sw.Write(secondaryType); + sw.Write(" value) => new "); + sw.Write(name); + sw.WriteLine("(value);"); + sw.WriteLine(); + + sw.Write(" public static explicit operator "); + sw.Write(secondaryType); + sw.Write('('); + sw.Write(name); + sw.Write(" value) => ("); + sw.Write(secondaryType); + sw.WriteLine(")(value.Value);"); + sw.WriteLine(); + } + + sw.Write(" public static implicit operator "); + sw.Write(name); + sw.Write('('); + sw.Write(type); + sw.Write(" value) => new "); + sw.Write(name); + sw.WriteLine("(value);"); + sw.WriteLine(); + + sw.Write(" public static implicit operator "); + sw.Write(type); + sw.Write('('); + sw.Write(name); + sw.WriteLine(" value) => value.Value;"); + sw.WriteLine(); + + break; + } + + case PInvokeGeneratorTransparentStructKind.Handle: + case PInvokeGeneratorTransparentStructKind.Win32Handle: + { + // Handles can implicitly convert to int, uint, nint, nuint, or void* + // Casts from however are explicit + + sw.Write(" public static explicit operator "); + sw.Write(name); + sw.Write("(int value) => new "); + sw.Write(name); + sw.WriteLine("(value);"); + sw.WriteLine(); + + sw.Write(" public static explicit operator "); + sw.Write(name); + sw.Write("(uint value) => new "); + sw.Write(name); + sw.WriteLine("(value);"); + sw.WriteLine(); + + sw.Write(" public static explicit operator "); + sw.Write(name); + sw.Write("(nint value) => new "); + sw.Write(name); + sw.WriteLine("(value);"); + sw.WriteLine(); + + sw.Write(" public static explicit operator "); + sw.Write(name); + sw.Write("(nuint value) => new "); + sw.Write(name); + sw.WriteLine("(value);"); + sw.WriteLine(); + + sw.Write(" public static explicit operator "); + sw.Write(name); + sw.Write("(void* value) => new "); + sw.Write(name); + sw.WriteLine("(value);"); + sw.WriteLine(); + + if (kind == PInvokeGeneratorTransparentStructKind.Win32Handle) + { + // Win32 handles can also be explicitly cast from HANDLE + + sw.Write(" public static explicit operator "); + sw.Write(name); + sw.Write("(HANDLE value) => new "); + sw.Write(name); + sw.WriteLine("(value);"); + sw.WriteLine(); + } + + sw.Write(" public static explicit operator int("); + sw.Write(name); + sw.WriteLine(" value) => (int)(value.Value);"); + sw.WriteLine(); + + sw.Write(" public static explicit operator uint("); + sw.Write(name); + sw.WriteLine(" value) => (uint)(value.Value);"); + sw.WriteLine(); + + sw.Write(" public static implicit operator nint("); + sw.Write(name); + sw.WriteLine(" value) => (nint)(value.Value);"); + sw.WriteLine(); + + sw.Write(" public static implicit operator nuint("); + sw.Write(name); + sw.WriteLine(" value) => (nuint)(value.Value);"); + sw.WriteLine(); + + sw.Write(" public static implicit operator void*("); + sw.Write(name); + sw.WriteLine(" value) => (void*)(value.Value);"); + sw.WriteLine(); + + if (kind == PInvokeGeneratorTransparentStructKind.Win32Handle) + { + // Win32 handles can also be implicitly cast to HANDLE + + sw.Write(" public static implicit operator HANDLE("); + sw.Write(name); + sw.WriteLine(" value) => (HANDLE)(value.Value);"); + sw.WriteLine(); + } + + break; + } + + case PInvokeGeneratorTransparentStructKind.Boolean: + { + // Booleans can implicitly convert from the underlying type and bool + // They can implicitly convert to the underlying type and explicitly to bool + // They also provide true/false operators to ease their use in bool like scenarios + + sw.Write(" public static explicit operator bool("); + sw.Write(name); + sw.WriteLine(" value) => value.Value != 0;"); + sw.WriteLine(); + + sw.Write(" public static implicit operator "); + sw.Write(name); + sw.Write('('); + sw.Write(type); + sw.Write(" value) => new "); + sw.Write(name); + sw.WriteLine("(value);"); + sw.WriteLine(); + + sw.Write(" public static implicit operator "); + sw.Write(name); + sw.Write("(bool value) => new "); + sw.Write(name); + sw.WriteLine("(value);"); + sw.WriteLine(); + + sw.Write(" public static implicit operator "); + sw.Write(type); + sw.Write('('); + sw.Write(name); + sw.WriteLine(" value) => value.Value;"); + sw.WriteLine(); + + sw.Write(" public static bool operator false("); + sw.Write(name); + sw.WriteLine(" value) => value.Value == 0;"); + sw.WriteLine(); + + sw.Write(" public static bool operator true("); + sw.Write(name); + sw.WriteLine(" value) => value.Value != 0;"); + sw.WriteLine(); + + break; + } + } sw.Write(" public override bool Equals(object? obj) => (obj is "); - sw.Write(transparentStruct.Key); + sw.Write(name); sw.WriteLine(" other) && Equals(other);"); sw.WriteLine(); sw.Write(" public bool Equals("); - sw.Write(transparentStruct.Key); + sw.Write(name); sw.WriteLine(" other) => (this == other);"); sw.WriteLine(); @@ -534,6 +844,23 @@ static void GenerateTransparentStructs(PInvokeGenerator generator) sw.WriteLine(" }"); sw.WriteLine('}'); } + + static string GetSecondaryType(string type) + { + return type switch { + "byte" => "sbyte", + "short" => "ushort", + "int" => "uint", + "long" => "ulong", + "nint" => "nuint", + "sbyte" => "byte", + "ushort" => "short", + "uint" => "int", + "ulong" => "long", + "nuint" => "nint", + _ => "", + }; + } } } @@ -2375,9 +2702,9 @@ private string GetTypeNameForPointeeType(Cursor cursor, Cursor context, Type roo _ = nameBuilder.Append(' '); } - if (!needsReturnFixup && ignoreTransparentStructsWhereRequired && _config.WithTransparentStructs.TryGetValue(returnTypeName, out var transparentValueTypeName)) + if (!needsReturnFixup && ignoreTransparentStructsWhereRequired && _config.WithTransparentStructs.TryGetValue(returnTypeName, out var transparentStruct)) { - _ = nameBuilder.Append(transparentValueTypeName); + _ = nameBuilder.Append(transparentStruct.Name); } else { @@ -3523,15 +3850,20 @@ internal static bool IsSupportedFixedSizedBufferType(string typeName) } } + private static bool IsTransparentStructHandle(PInvokeGeneratorTransparentStructKind kind) + { + return (kind == PInvokeGeneratorTransparentStructKind.Handle) || (kind == PInvokeGeneratorTransparentStructKind.Win32Handle); + } + private bool IsUnchecked(string targetTypeName, Stmt stmt) { if (IsPrevContextDecl(out var parentVarDecl, out _)) { var cursorName = GetCursorName(parentVarDecl); - if (cursorName.StartsWith("ClangSharpMacro_") && _config.WithTransparentStructs.TryGetValue(targetTypeName, out var transparentValueTypeName)) + if (cursorName.StartsWith("ClangSharpMacro_") && _config.WithTransparentStructs.TryGetValue(targetTypeName, out var transparentStruct)) { - targetTypeName = transparentValueTypeName; + targetTypeName = transparentStruct.Name; } } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs index 4456eac5..949f7f52 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs @@ -18,13 +18,13 @@ public sealed class PInvokeGeneratorConfiguration private readonly Dictionary> _withAttributes; private readonly Dictionary _withCallConvs; private readonly Dictionary _withLibraryPaths; - private readonly Dictionary _withTransparentStructs; + private readonly Dictionary _withTransparentStructs; private readonly Dictionary _withTypes; private readonly Dictionary> _withUsings; private PInvokeGeneratorConfigurationOptions _options; - public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, string testOutputLocation, PInvokeGeneratorOutputMode outputMode = PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, string headerFile = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary remappedNames = null, string[] traversalNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null) + public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, string testOutputLocation, PInvokeGeneratorOutputMode outputMode = PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, string headerFile = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary remappedNames = null, string[] traversalNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null) { if (excludedNames is null) { @@ -93,7 +93,7 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s _withAttributes = new Dictionary>(); _withCallConvs = new Dictionary(); _withLibraryPaths = new Dictionary(); - _withTransparentStructs = new Dictionary(); + _withTransparentStructs = new Dictionary(); _withTypes = new Dictionary(); _withUsings = new Dictionary>(); @@ -244,7 +244,7 @@ public bool ExcludeFnptrCodegen public string[] WithSetLastErrors { get; } - public IReadOnlyDictionary WithTransparentStructs => _withTransparentStructs; + public IReadOnlyDictionary WithTransparentStructs => _withTransparentStructs; public IReadOnlyDictionary WithTypes => _withTypes; @@ -324,6 +324,8 @@ private static AccessSpecifier ConvertStringToAccessSpecifier(string input) private static string RemoveAtPrefix(string value) => ValueStartsWithAt(value) ? value[1..] : value; + private static (string, PInvokeGeneratorTransparentStructKind) RemoveAtPrefix((string Name, PInvokeGeneratorTransparentStructKind Kind) value) => (ValueStartsWithAt(value.Name) ? value.Name[1..] : value.Name, value.Kind); + private static bool ValueStartsWithAt(string value) => value.StartsWith("@"); } } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorTransparentStructKind.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorTransparentStructKind.cs new file mode 100644 index 00000000..aad1f260 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorTransparentStructKind.cs @@ -0,0 +1,11 @@ +namespace ClangSharp +{ + public enum PInvokeGeneratorTransparentStructKind + { + Unknown = 0, + Typedef = 1, + Handle = 2, + Boolean = 3, + Win32Handle = 4, + } +} diff --git a/sources/ClangSharpPInvokeGenerator/Program.cs b/sources/ClangSharpPInvokeGenerator/Program.cs index 49b4e9ea..fd98c462 100644 --- a/sources/ClangSharpPInvokeGenerator/Program.cs +++ b/sources/ClangSharpPInvokeGenerator/Program.cs @@ -186,7 +186,7 @@ public static int Run(InvocationContext context) ParseKeyValuePairs(withAttributeNameValuePairs, errorList, out Dictionary> withAttributes); ParseKeyValuePairs(withCallConvNameValuePairs, errorList, out Dictionary withCallConvs); ParseKeyValuePairs(withLibraryPathNameValuePairs, errorList, out Dictionary withLibraryPath); - ParseKeyValuePairs(withTransparentStructNameValuePairs, errorList, out Dictionary withTransparentStructs); + ParseKeyValuePairs(withTransparentStructNameValuePairs, errorList, out Dictionary withTransparentStructs); ParseKeyValuePairs(withTypeNameValuePairs, errorList, out Dictionary withTypes); ParseKeyValuePairs(withUsingNameValuePairs, errorList, out Dictionary> withUsings); @@ -631,6 +631,46 @@ private static void ParseKeyValuePairs(IEnumerable keyValuePairs, List keyValuePairs, List errorList, out Dictionary result) + { + result = new Dictionary(); + + foreach (var keyValuePair in keyValuePairs) + { + var parts = keyValuePair.Split('='); + + if (parts.Length != 2) + { + errorList.Add($"Error: Invalid key/value pair argument: {keyValuePair}. Expected 'name=value' or 'name=value;kind'"); + continue; + } + + var key = parts[0].TrimEnd(); + + if (result.ContainsKey(key)) + { + errorList.Add($"Error: A key with the given name already exists: {key}. Existing: {result[key]}"); + continue; + } + + parts = parts[1].Split(';'); + + if (parts.Length == 1) + { + result.Add(key, (parts[0], PInvokeGeneratorTransparentStructKind.Unknown)); + } + else if ((parts.Length == 2) && Enum.TryParse(parts[1], out var transparentStructKind)) + { + result.Add(key, (parts[0], transparentStructKind)); + } + else + { + errorList.Add($"Error: Invalid key/value pair argument: {keyValuePair}. Expected 'name=value' or 'name=value;kind'"); + continue; + } + } + } + private static void ParseKeyValuePairs(IEnumerable keyValuePairs, List errorList, out Dictionary> result) { result = new Dictionary>(); diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs index 4a7899f0..59ff851b 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs @@ -36,23 +36,23 @@ public abstract class PInvokeGeneratorTest protected static string EscapeXml(string value) => new XText(value).ToString(); - protected static Task ValidateGeneratedCSharpLatestWindowsBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions.None | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); + protected static Task ValidateGeneratedCSharpLatestWindowsBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions.None | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); - protected static Task ValidateGeneratedCSharpLatestUnixBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions.GenerateUnixTypes | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); + protected static Task ValidateGeneratedCSharpLatestUnixBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions.GenerateUnixTypes | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); - protected static Task ValidateGeneratedCSharpCompatibleWindowsBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); + protected static Task ValidateGeneratedCSharpCompatibleWindowsBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); - protected static Task ValidateGeneratedCSharpCompatibleUnixBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode | PInvokeGeneratorConfigurationOptions.GenerateUnixTypes | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); + protected static Task ValidateGeneratedCSharpCompatibleUnixBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode | PInvokeGeneratorConfigurationOptions.GenerateUnixTypes | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); - protected static Task ValidateGeneratedXmlLatestWindowsBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null, [CallerFilePath] string filePath = "") => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.Xml, PInvokeGeneratorConfigurationOptions.None | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs, filePath); + protected static Task ValidateGeneratedXmlLatestWindowsBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null, [CallerFilePath] string filePath = "") => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.Xml, PInvokeGeneratorConfigurationOptions.None | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs, filePath); - protected static Task ValidateGeneratedXmlLatestUnixBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.Xml, PInvokeGeneratorConfigurationOptions.GenerateUnixTypes | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); + protected static Task ValidateGeneratedXmlLatestUnixBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.Xml, PInvokeGeneratorConfigurationOptions.GenerateUnixTypes | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); - protected static Task ValidateGeneratedXmlCompatibleWindowsBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.Xml, PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); + protected static Task ValidateGeneratedXmlCompatibleWindowsBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.Xml, PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); - protected static Task ValidateGeneratedXmlCompatibleUnixBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.Xml, PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode | PInvokeGeneratorConfigurationOptions.GenerateUnixTypes | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); + protected static Task ValidateGeneratedXmlCompatibleUnixBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions additionalConfigOptions = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, IReadOnlyDictionary remappedNames = null, IReadOnlyDictionary withAccessSpecifiers = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTransparentStructs = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null, IEnumerable expectedDiagnostics = null, string libraryPath = DefaultLibraryPath, string[] commandlineArgs = null) => ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, PInvokeGeneratorOutputMode.Xml, PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode | PInvokeGeneratorConfigurationOptions.GenerateUnixTypes | additionalConfigOptions, excludedNames, remappedNames, withAccessSpecifiers, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTransparentStructs, withTypes, withUsings, expectedDiagnostics, libraryPath, commandlineArgs); - private static async Task ValidateGeneratedBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorOutputMode outputMode, PInvokeGeneratorConfigurationOptions configOptions, string[] excludedNames, IReadOnlyDictionary remappedNames, IReadOnlyDictionary withAccessSpecifiers, IReadOnlyDictionary> withAttributes, IReadOnlyDictionary withCallConvs, IReadOnlyDictionary withLibraryPaths, string[] withSetLastErrors, IReadOnlyDictionary withTransparentStructs, IReadOnlyDictionary withTypes, IReadOnlyDictionary> withUsings, IEnumerable expectedDiagnostics, string libraryPath, string[] commandlineArgs, [CallerFilePath] string filePath = "") + private static async Task ValidateGeneratedBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorOutputMode outputMode, PInvokeGeneratorConfigurationOptions configOptions, string[] excludedNames, IReadOnlyDictionary remappedNames, IReadOnlyDictionary withAccessSpecifiers, IReadOnlyDictionary> withAttributes, IReadOnlyDictionary withCallConvs, IReadOnlyDictionary withLibraryPaths, string[] withSetLastErrors, IReadOnlyDictionary withTransparentStructs, IReadOnlyDictionary withTypes, IReadOnlyDictionary> withUsings, IEnumerable expectedDiagnostics, string libraryPath, string[] commandlineArgs, [CallerFilePath] string filePath = "") { Assert.True(File.Exists(DefaultInputFileName));