From cbd77adb70209b09cc71e5d90ac32f71fae2f6f6 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 12 Jul 2026 19:58:52 -0700 Subject: [PATCH] Promote anonymous-name prefixes to consts with helper predicates The generator checked scattered `__Anonymous*_` magic string prefixes inline across several files. Promote each distinct prefix to a `private const string` grouped near the top of `PInvokeGenerator.cs` and route the produce side (`GetAnonymousName`) through the shared `AnonymousNamePrefix` base so produce and consume share the same literals. Consts added: `AnonymousNamePrefix`, `AnonymousBasePrefix`, `AnonymousEnumPrefix`, `AnonymousFieldDeclPrefix`, `AnonymousRecordPrefix`. Helpers added: `IsAnonymousEnum` (4 call sites) and `IsAnonymousRecord` (2 call sites); single-use `FieldDecl`/`Base` checks use the const directly. Behavior-preserving: the literal string values are unchanged, so generated output is byte-identical. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../PInvokeGenerator.Naming.cs | 10 +++++----- .../PInvokeGenerator.VisitDecl.cs | 4 ++-- .../PInvokeGenerator.VisitStmt.cs | 2 +- .../ClangSharp.PInvokeGenerator/PInvokeGenerator.cs | 12 +++++++++++- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs index 21b25bd6..bd006f6f 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs @@ -456,7 +456,7 @@ private string GetRemappedCursorName(NamedDecl namedDecl, out string nativeTypeN { remappedName = "Dispose"; } - else if ((namedDecl is FieldDecl fieldDecl) && name.StartsWith("__AnonymousFieldDecl_", StringComparison.Ordinal)) + else if ((namedDecl is FieldDecl fieldDecl) && name.StartsWith(AnonymousFieldDeclPrefix, StringComparison.Ordinal)) { if (fieldDecl.Type.AsCXXRecordDecl?.IsAnonymousStructOrUnion == true) { @@ -482,7 +482,7 @@ private string GetRemappedCursorName(NamedDecl namedDecl, out string nativeTypeN } } } - else if ((namedDecl is RecordDecl recordDecl) && name.StartsWith("__AnonymousRecord_", StringComparison.Ordinal)) + else if ((namedDecl is RecordDecl recordDecl) && IsAnonymousRecord(name)) { remappedName = GetRemappedNameForAnonymousRecord(recordDecl); } @@ -614,7 +614,7 @@ private string GetRemappedName(string name, Cursor? cursor, bool tryRemapOperato return AddUsingDirectiveIfNeeded(_outputBuilder, remappedName, skipUsing); } - if ((cursor is CXXBaseSpecifier cxxBaseSpecifier) && remappedName.StartsWith("__AnonymousBase_", StringComparison.Ordinal)) + if ((cursor is CXXBaseSpecifier cxxBaseSpecifier) && remappedName.StartsWith(AnonymousBasePrefix, StringComparison.Ordinal)) { Debug.Assert(_cxxRecordDeclContext is not null); remappedName = "Base"; @@ -668,12 +668,12 @@ private string GetRemappedTypeName(Cursor? cursor, Cursor? context, Type type, o type = arrayType.ElementType; } - if (IsType(cursor, type, out var recordType) && remappedName.StartsWith("__AnonymousRecord_", StringComparison.Ordinal)) + if (IsType(cursor, type, out var recordType) && IsAnonymousRecord(remappedName)) { var recordDecl = recordType.Decl; remappedName = GetRemappedNameForAnonymousRecord(recordDecl); } - else if (IsType(cursor, type, out var enumType) && remappedName.StartsWith("__AnonymousEnum_", StringComparison.Ordinal)) + else if (IsType(cursor, type, out var enumType) && IsAnonymousEnum(remappedName)) { remappedName = GetRemappedTypeName(enumType.Decl, context: null, enumType.Decl.IntegerType, out _, skipUsing); } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 599e60af..18aeaeab 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -296,7 +296,7 @@ private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) { parentName = GetRemappedCursorName(enumDecl); - if (parentName.StartsWith("__AnonymousEnum_", StringComparison.Ordinal)) + if (IsAnonymousEnum(parentName)) { parentName = ""; isAnonymousEnum = true; @@ -365,7 +365,7 @@ private void VisitEnumDecl(EnumDecl enumDecl) var escapedName = EscapeName(name); var isAnonymousEnum = false; - if (name.StartsWith("__AnonymousEnum_", StringComparison.Ordinal)) + if (IsAnonymousEnum(name)) { isAnonymousEnum = true; diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index a8919c77..e2431f1e 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -1026,7 +1026,7 @@ private void VisitDeclRefExpr(DeclRefExpr declRefExpr) if (!_config.DontUseUsingStaticsForEnums) { - if (enumName.StartsWith("__AnonymousEnum_", StringComparison.Ordinal)) + if (IsAnonymousEnum(enumName)) { var className = GetClass(enumName); diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 2bae4a3e..030affd6 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -35,6 +35,12 @@ public sealed partial class PInvokeGenerator : IDisposable { private const int DefaultStreamWriterBufferSize = 1024; + private const string AnonymousNamePrefix = "__Anonymous"; + private const string AnonymousBasePrefix = $"{AnonymousNamePrefix}Base_"; + private const string AnonymousEnumPrefix = $"{AnonymousNamePrefix}Enum_"; + private const string AnonymousFieldDeclPrefix = $"{AnonymousNamePrefix}FieldDecl_"; + private const string AnonymousRecordPrefix = $"{AnonymousNamePrefix}Record_"; + private static readonly Encoding s_defaultStreamWriterEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); private static readonly string[] s_doubleColonSeparator = ["::"]; private static readonly char[] s_doubleQuoteSeparator = ['"']; @@ -1076,9 +1082,13 @@ private static string GetAnonymousName(Cursor cursor, string kind) { cursor.Location.GetFileLocation(out var file, out var line, out var column, out _); var fileName = Path.GetFileNameWithoutExtension(file.Name.ToString()); - return $"__Anonymous{kind}_{fileName}_L{line}_C{column}"; + return $"{AnonymousNamePrefix}{kind}_{fileName}_L{line}_C{column}"; } + private static bool IsAnonymousEnum(string name) => name.StartsWith(AnonymousEnumPrefix, StringComparison.Ordinal); + + private static bool IsAnonymousRecord(string name) => name.StartsWith(AnonymousRecordPrefix, StringComparison.Ordinal); + private string GetArtificialFixedSizedBufferName(FieldDecl fieldDecl) { var name = GetRemappedCursorName(fieldDecl);