diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 841aeb15..d3b9bb84 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -435,6 +435,7 @@ public void Close() GenerateNativeBitfieldAttribute(this, stream, leaveStreamOpen); GenerateNativeInheritanceAttribute(this, stream, leaveStreamOpen); GenerateNativeTypeNameAttribute(this, stream, leaveStreamOpen); + GenerateNativeAnnotationAttribute(this, stream, leaveStreamOpen); GenerateSetsLastSystemErrorAttribute(this, stream, leaveStreamOpen); GenerateVtblIndexAttribute(this, stream, leaveStreamOpen); GenerateTransparentStructs(this, stream, leaveStreamOpen); @@ -795,6 +796,96 @@ static void GenerateNativeTypeNameAttribute(PInvokeGenerator generator, Stream? } } + static void GenerateNativeAnnotationAttribute(PInvokeGenerator generator, Stream? stream, bool leaveStreamOpen) + { + const string AttributeName = "NativeAnnotationAttribute"; + var config = generator.Config; + + var ns = generator.GetNamespace(AttributeName); + if (config.ExcludedNames.Contains(AttributeName) || config.ExcludedNames.Contains($"{ns}.{AttributeName}")) + { + return; + } + + if (stream is null) + { + var outputPath = Path.Combine(config.OutputLocation, $"{AttributeName}.cs"); + stream = generator._outputStreamFactory(outputPath); + } + + using var sw = new StreamWriter(stream, s_defaultStreamWriterEncoding, DefaultStreamWriterBufferSize, leaveStreamOpen); + sw.NewLine = "\n"; + + if (!string.IsNullOrEmpty(config.HeaderText)) + { + sw.WriteLine(config.HeaderText); + } + + var indentString = " "; + + sw.WriteLine("using System;"); + sw.WriteLine("using System.Diagnostics;"); + sw.WriteLine(); + + sw.Write("namespace "); + sw.Write(ns); + + if (generator.Config.GenerateFileScopedNamespaces) + { + sw.WriteLine(';'); + sw.WriteLine(); + indentString = ""; + } + else + { + sw.WriteLine(); + sw.WriteLine('{'); + } + + sw.Write(indentString); + sw.WriteLine("/// Defines the annotation found in a native declaration."); + sw.Write(indentString); + sw.WriteLine("[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]"); + sw.Write(indentString); + sw.WriteLine("[Conditional(\"DEBUG\")]"); + sw.Write(indentString); + sw.WriteLine($"internal sealed partial class {AttributeName} : Attribute"); + sw.Write(indentString); + sw.WriteLine('{'); + sw.Write(indentString); + sw.WriteLine(" private readonly string _annotation;"); + sw.WriteLine(); + sw.Write(indentString); + sw.WriteLine($" /// Initializes a new instance of the class."); + sw.Write(indentString); + sw.WriteLine(" /// The annotation that was used in the native declaration."); + sw.Write(indentString); + sw.WriteLine($" public {AttributeName}(string annotation)"); + sw.Write(indentString); + sw.WriteLine(" {"); + sw.Write(indentString); + sw.WriteLine(" _annotation = annotation;"); + sw.Write(indentString); + sw.WriteLine(" }"); + sw.WriteLine(); + sw.Write(indentString); + sw.WriteLine(" /// Gets the annotation that was used in the native declaration."); + sw.Write(indentString); + sw.WriteLine(" public string Annotation => _annotation;"); + sw.Write(indentString); + sw.WriteLine('}'); + + if (!generator.Config.GenerateFileScopedNamespaces) + { + sw.WriteLine('}'); + } + + if (!leaveStreamOpen) + { + stream = null; + } + } + static void GenerateSetsLastSystemErrorAttribute(PInvokeGenerator generator, Stream? stream, bool leaveStreamOpen) { var config = generator.Config; @@ -6724,6 +6815,13 @@ private void WithAttributes(NamedDecl namedDecl, bool onlySupportedOSPlatform = break; } + case CX_AttrKind_Annotate: + { + var annotationText = attr.Spelling; + outputBuilder.WriteCustomAttribute($"""NativeAnnotation("{annotationText}")"""); + break; + } + case CX_AttrKind_Format: case CX_AttrKind_FormatArg: case CX_AttrKind_MSNoVTable: