Skip to content

Commit fc74e90

Browse files
committed
Fix anonymous composites in clang 19.1.7
In some new clang versions, the name (cursor.spelling) of anonymous structs, unions and enums was set to an informative message instead of an empty string. I.e. "enum (unnamed at [declaration_file.h:48]" This fixes the errors that happen due to that in a backwards compatible way, since it still checks for empty spellings. This patch is possibly overeager in checking the cursor.spellings for these new special values, I haven't checked at every single location whether the check is necessary.
1 parent 85ef01e commit fc74e90

File tree

5 files changed

+54
-10
lines changed

5 files changed

+54
-10
lines changed

dstep/translator/Context.d

+16-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import dstep.translator.Output;
2121
import dstep.translator.Translator;
2222
import dstep.translator.TypedefIndex;
2323
import dstep.translator.HeaderIndex;
24+
import dstep.translator.Util;
2425

2526
class Context
2627
{
@@ -194,7 +195,7 @@ class Context
194195

195196
private string translateSpellingImpl(in Cursor cursor)
196197
{
197-
return cursor.spelling == ""
198+
return cursor.spelling.isUnnamed()
198199
? generateAnonymousName(cursor)
199200
: cursor.spelling;
200201
}
@@ -272,7 +273,7 @@ class Context
272273
{
273274
auto typedefp = typedefParent(cursor.canonical);
274275

275-
if (typedefp.isValid && cursor.spelling == "")
276+
if (typedefp.isValid && cursor.spelling.isUnnamed)
276277
{
277278
spelling = typedefp.spelling;
278279
}
@@ -378,8 +379,19 @@ bool variablesInParentScope(Cursor cursor)
378379
*/
379380
bool shouldBeAnonymous(Context context, Cursor cursor)
380381
{
381-
return cursor.type.isAnonymous &&
382-
context.typedefIndex.typedefParent(cursor).isEmpty;
382+
import std.string: startsWith;
383+
384+
/* Unfortunately, libclang isAnonymous doesn't seem to work on enums
385+
* https://stackoverflow.com/a/35184821
386+
* Checking for the string value instead is brittle and might break in a future
387+
* version, but I don't think there's a better way
388+
*/
389+
if (cursor.kind == CXCursorKind.enumDecl)
390+
return cursor.spelling.isUnnamed
391+
&& context.typedefIndex.typedefParent(cursor).isEmpty;
392+
else
393+
return cursor.type.isAnonymous &&
394+
context.typedefIndex.typedefParent(cursor).isEmpty;
383395
}
384396

385397
/**

dstep/translator/Declaration.d

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import clang.Cursor;
1010

1111
import dstep.translator.Translator;
1212
import dstep.translator.Output;
13+
import dstep.translator.Util;
1314

1415
abstract class Declaration
1516
{
@@ -44,6 +45,6 @@ abstract class Declaration
4445
@property string spelling ()
4546
{
4647
auto name = cursor.spelling;
47-
return name.length || parent.isEmpty ? name : translator.context.generateAnonymousName(cursor);
48+
return !name.isUnnamed && (name.length || parent.isEmpty) ? name : translator.context.generateAnonymousName(cursor);
4849
}
4950
}

dstep/translator/Enum.d

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import dstep.translator.Declaration;
1919
import dstep.translator.Output;
2020
import dstep.translator.Translator;
2121
import dstep.translator.Type;
22+
import dstep.translator.Util;
2223

2324
void translateEnumConstantDecl(
2425
Output output,

dstep/translator/Record.d

+14-5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import dstep.translator.Declaration;
1919
import dstep.translator.Output;
2020
import dstep.translator.Translator;
2121
import dstep.translator.Type;
22+
import dstep.translator.Util;
2223

2324
string translateRecordTypeKeyword(in Cursor cursor)
2425
{
@@ -140,7 +141,14 @@ void translateRecordDef(
140141
auto canonical = cursor.canonical;
141142

142143
auto spelling = keepUnnamed ? "" : context.translateTagSpelling(cursor);
143-
spelling = spelling == "" ? spelling : " " ~ spelling;
144+
/* Spaghetti warning: Since unnamed values might in at least clang 19.1
145+
* contain "unnamed enum" or something as the name instead of being empty,
146+
* explicitly set it our variable to the empty string.
147+
*
148+
* Preferably, this would be disentagled and it would be ensured that there
149+
* is only one source of the spelling in this whole function.
150+
*/
151+
spelling = spelling.isUnnamed ? "" : " " ~ spelling;
144152
auto type = translateRecordTypeKeyword(cursor);
145153

146154
output.subscopeStrong(cursor.extent, "%s%s", type, spelling) in {
@@ -195,7 +203,7 @@ void translateRecordDef(
195203

196204
case CXCursorKind.unionDecl:
197205
case CXCursorKind.structDecl:
198-
if (cursor.type.isAnonymous)
206+
if (cursor.type.isAnonymous || cursor.spelling.isUnnamed)
199207
translateAnonymousRecord(output, context, cursor);
200208

201209
break;
@@ -216,7 +224,8 @@ void translateRecordDecl(Output output, Context context, Cursor cursor)
216224
context.markAsDefined(cursor);
217225

218226
auto spelling = context.translateTagSpelling(cursor);
219-
spelling = spelling == "" ? spelling : " " ~ spelling;
227+
//spelling = spelling == "" ? spelling : " " ~ spelling;
228+
spelling = spelling.isUnnamed ? "" : " " ~ spelling;
220229
auto type = translateRecordTypeKeyword(cursor);
221230
output.singleLine(cursor.extent, "%s%s;", type, spelling);
222231
}
@@ -233,7 +242,7 @@ bool shouldSkipRecord(Context context, Cursor cursor)
233242
cursor.kind == CXCursorKind.unionDecl)
234243
{
235244
auto typedefp = context.typedefParent(cursor.canonical);
236-
auto spelling = typedefp.isValid && cursor.spelling == ""
245+
auto spelling = typedefp.isValid && isUnnamed(cursor.spelling)
237246
? typedefp.spelling
238247
: cursor.spelling;
239248

@@ -249,7 +258,7 @@ bool shouldSkipRecordDefinition(Context context, Cursor cursor)
249258
cursor.kind == CXCursorKind.unionDecl)
250259
{
251260
auto typedefp = context.typedefParent(cursor.canonical);
252-
auto spelling = typedefp.isValid && cursor.spelling == ""
261+
auto spelling = typedefp.isValid && cursor.spelling.isUnnamed
253262
? typedefp.spelling
254263
: cursor.spelling;
255264

dstep/translator/Util.d

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module dstep.translator.Util;
2+
3+
/** In recent versions of clang (at least 19.1.7), unnamed (anonymous) types will not
4+
* set the spelling field to an empty string, but instead to a message like
5+
* "enum (unnamed at [declaration_file.h:48]"
6+
* "struct (anonymous at [declaration_file.h:48]"
7+
* Either unnamed or anonymous is used.
8+
* This applies to structs, enums and unions.
9+
*
10+
* structs and unions additionally have the isAnonymous() helper which doesn't for enums.
11+
*
12+
* Usage:
13+
* cursor.spelling.isUnnamed()
14+
*/
15+
bool isUnnamed(string s){
16+
import std.algorithm.searching;
17+
return s == "" ||
18+
s.canFind("(unnamed at") ||
19+
s.canFind("(anonymous at");
20+
}
21+

0 commit comments

Comments
 (0)