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: