Skip to content

Commit

Permalink
Merge pull request #344 from atilaneves/fix-libclang
Browse files Browse the repository at this point in the history
Fix libclang17
  • Loading branch information
atilaneves committed May 23, 2024
2 parents d7fda9d + 41ef5a1 commit 739d665
Show file tree
Hide file tree
Showing 20 changed files with 232 additions and 60 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jobs:
- x86_64
clang:
- 12.0.0
- 17.0.6
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion dub.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ targetPath "bin"
targetName "d++"

dependency "libclang" version="~>0.3.1"
dependency "sumtype" version="~>0.7.1"
dependency "sumtype" version="~>1.2.0"

versions "SumTypeNoDefaultCtor"

Expand Down
6 changes: 3 additions & 3 deletions dub.selections.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"fileVersion": 1,
"versions": {
"libclang": "0.3.2",
"sumtype": "0.7.1",
"unit-threaded": "2.1.3"
"libclang": "0.3.3",
"sumtype": "1.2.8",
"unit-threaded": "2.1.9"
}
}
4 changes: 4 additions & 0 deletions source/dpp/clang/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,7 @@ bool hasAnonymousSpelling(in string spelling) @safe pure nothrow {
import std.algorithm : canFind;
return spelling.canFind("(anonymous") || spelling.canFind("(unnamed");
}

bool isSortaAnonymous(in from!"clang".Cursor cursor) @safe pure nothrow {
return cursor.spelling == "" || cursor.isAnonymous;
}
14 changes: 9 additions & 5 deletions source/dpp/expansion/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,16 @@ private from!"clang".TranslationUnit parseTU
if(context.options.parseAsCpp || context.language == Language.Cpp) {
const std = "-std=" ~ context.options.cppStandard;
parseArgs ~= ["-xc++", std];
} else
parseArgs ~= "-xc";
} else {
const std = "-std=" ~ context.options.cStandard;
parseArgs ~= ["-xc", std];
}

return parse(translUnitFileName,
parseArgs,
TranslationUnitFlags.DetailedPreprocessingRecord);
return parse(
translUnitFileName,
parseArgs,
TranslationUnitFlags.DetailedPreprocessingRecord
);
}


Expand Down
8 changes: 7 additions & 1 deletion source/dpp/runtime/context.d
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ struct Context {
void rememberAggregate(in Cursor cursor) @safe pure {
const spelling = resolveSpelling(cursor);
rememberType(spelling);

}

bool aggregateIsRemembered(in Cursor cursor) @safe pure {
Expand Down Expand Up @@ -370,7 +371,8 @@ struct Context {

/// return the spelling if it exists, or our made-up nickname for it if not
string spellingOrNickname(in Cursor cursor) @safe pure {
if (cursor.spelling == "" || cursor.isAnonymous)
import dpp.clang: isSortaAnonymous;
if (cursor.isSortaAnonymous)
return nickName(cursor);

return spelling(cursor.spelling);
Expand Down Expand Up @@ -420,6 +422,10 @@ struct Context {
}

void rememberType(in string type) @safe pure nothrow {
import std.algorithm: canFind;

if(_types.canFind(type)) return;

_types ~= type;
}

Expand Down
2 changes: 2 additions & 0 deletions source/dpp/runtime/options.d
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct Options {
string[string] prebuiltHeaders;
bool alwaysScopedEnums;
string cppStandard = "c++17";
string cStandard = "c99";
string[] clangOptions;
bool noSystemHeaders;
string cppPath;
Expand Down Expand Up @@ -175,6 +176,7 @@ struct Options {
&detailedUntranslatable,
"scoped-enums", "Don't redeclare enums to mimic C", &alwaysScopedEnums,
"c++-standard", "The C++ language standard (e.g. \"c++14\")", &cppStandard,
"c-standard", "The C language standard (e.g. \"c90\")", &cStandard,
"clang-option", "Pass option to libclang", &clangOptions,
"no-sys-headers", "Don't include system headers by default", &noSystemHeaders,
"cpp-path", "Path to the C preprocessor executable", &cppPath,
Expand Down
6 changes: 4 additions & 2 deletions source/dpp/translation/aggregate.d
Original file line number Diff line number Diff line change
Expand Up @@ -530,9 +530,11 @@ private string[] maybeC11AnonymousRecords(in from!"clang".Cursor cursor,
{
import dpp.translation.type: translate, hasAnonymousSpelling;
import clang: Cursor, Type;
import std.algorithm: any, filter;
import std.algorithm: any, filter, canFind;

if(member.type.kind != Type.Kind.Record || member.spelling != "") return [];
const isAnonymous = member.spelling == "" || member.spelling.canFind("(anonymous");

if(member.type.kind != Type.Kind.Record || !isAnonymous) return [];

// Either a field or an array of the type we expect
static bool isFieldOfRightType(in Cursor member, in Cursor child) {
Expand Down
4 changes: 3 additions & 1 deletion source/dpp/translation/macro_.d
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,9 @@ private auto fixCasts(R)(

// If the cursor is a macro function return its parameters
Token[] macroFunctionParams() {
assert(cursor.tokens[0].kind == Token.Kind.Identifier);
import std.conv: text;
assert(cursor.tokens[0].kind == Token.Kind.Identifier || cursor.tokens[0].kind == Token.Kind.Keyword,
cursor.tokens[0].kind.text);
assert(cursor.tokens[1] == Token(Token.Kind.Punctuation, "("));
enum fromParen = 2;
const closeParenIndex = cursor.tokens[fromParen .. $].countUntil(Token(Token.Kind.Punctuation, ")")) + fromParen;
Expand Down
5 changes: 3 additions & 2 deletions source/dpp/translation/translation.d
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,19 @@ private bool skipTopLevel(in from!"clang".Cursor cursor,
@safe
{
import dpp.translation.aggregate: isAggregateC;
import dpp.clang: isSortaAnonymous;
import clang: Cursor;
import std.algorithm: startsWith, canFind;

if(context.isFromIgnoredPath(cursor))
return true;

// We want to ignore anonymous structs and unions but not enums. See #54
if(cursor.spelling == "" && cursor.kind == Cursor.Kind.EnumDecl)
if((cursor.isSortaAnonymous) && cursor.kind == Cursor.Kind.EnumDecl)
return false;

// don't bother translating top-level anonymous aggregates
if(isAggregateC(cursor) && cursor.spelling == "")
if(isAggregateC(cursor) && cursor.isSortaAnonymous)
return true;

if(context.options.ignoreMacros && cursor.kind == Cursor.Kind.MacroDefinition)
Expand Down
10 changes: 7 additions & 3 deletions source/dpp/translation/typedef_.d
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,14 @@ string[] translateNonFunction(in from!"clang".Cursor cursor,
private bool isTopLevelAnonymous(in from!"clang".Cursor[] children)
@safe nothrow
{
import dpp.clang: isSortaAnonymous;
import clang: Cursor;

return
children.length == 1 && // so we can inspect it
children[0].spelling == "" && // anonymous
children[0].lexicalParent.kind == Cursor.Kind.TranslationUnit // top-level
children[0].isSortaAnonymous && // anonymous
children[0].lexicalParent.kind == Cursor.Kind.TranslationUnit && // top-level
children[0].kind != Cursor.Kind.ParmDecl // a lot more should be here
;
}

Expand All @@ -89,6 +92,7 @@ private string[] translateRegular(in from!"clang".Cursor cursor,
import dpp.translation.type: translate, removeDppDecorators;
import dpp.translation.aggregate: isAggregateC;
import dpp.translation.dlang: maybeRename;
import dpp.clang: isSortaAnonymous;
import clang: Type;
import std.typecons: No;

Expand All @@ -100,7 +104,7 @@ private string[] translateRegular(in from!"clang".Cursor cursor,
const isAnonymousEnum =
children.length == 1 &&
isAggregateC(children[0]) &&
children[0].spelling == "" &&
children[0].isSortaAnonymous &&
children[0].type.kind == Type.Kind.Enum
;

Expand Down
124 changes: 121 additions & 3 deletions tests/contract/aggregates.d
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,11 @@ auto contract_typedef_before(TestMode mode, CursorType)(auto ref CursorType tu)
tu.children.length.should == 2;

const structDecl = tu.children[0];
structDecl.shouldMatch(Cursor.Kind.StructDecl, "");
try
structDecl.shouldMatch(Cursor.Kind.StructDecl, "");
catch(Exception _)
structDecl.shouldMatch(Cursor.Kind.StructDecl, "Struct"); // libclang17

structDecl.type.shouldMatch(Type.Kind.Record, "Struct");
printChildren(structDecl);

Expand All @@ -342,7 +346,11 @@ auto contract_typedef_before(TestMode mode, CursorType)(auto ref CursorType tu)
typedef_.type.shouldMatch(Type.Kind.Typedef, "Struct");
printChildren(typedef_);
typedef_.children.length.should == 1;
typedef_.children[0].shouldMatch(Cursor.Kind.StructDecl, "");
try
typedef_.children[0].shouldMatch(Cursor.Kind.StructDecl, "");
catch(Exception _)
typedef_.children[0].shouldMatch(Cursor.Kind.StructDecl, "Struct"); //libclang17

typedef_.children[0].type.shouldMatch(Type.Kind.Record, "Struct");
}

Expand All @@ -363,7 +371,12 @@ auto contract_typedef_before(TestMode mode, CursorType)(auto ref CursorType tu)
tu.children.length.should == 2;

const structDecl = tu.children[0];
structDecl.shouldMatch(Cursor.Kind.StructDecl, "");
try
structDecl.shouldMatch(Cursor.Kind.StructDecl, "");
catch(Exception _) { //libclang17
structDecl.kind.should == Cursor.Kind.StructDecl;
"unnamed".should.be in structDecl.spelling;
}
structDecl.type.kind.should == Type.Kind.Record;
try
"anonymous at".should.be in structDecl.type.spelling;
Expand All @@ -380,3 +393,108 @@ auto contract_typedef_before(TestMode mode, CursorType)(auto ref CursorType tu)
typedef_.shouldMatch(Cursor.Kind.TypedefDecl, "Struct");
typedef_.type.shouldMatch(Type.Kind.Typedef, "Struct");
}

@("struct.var.anonymous")
@safe unittest {
const tu = parse(
C(`struct { int i; } var;`),
);

tu.children.length.should == 2;

{
const structDecl= tu.children[0];
try
structDecl.shouldMatch(Cursor.Kind.StructDecl, "");
catch(Exception _) { // libclang17
structDecl.kind.should == Cursor.Kind.StructDecl;
"unnamed".should.be in structDecl.spelling;
}

printChildren(structDecl);
structDecl.children.length.should == 1;

const fieldDecl = structDecl.children[0];
fieldDecl.shouldMatch(Cursor.Kind.FieldDecl, "i");
fieldDecl.children.length.should == 0;
}

{
const varDecl = tu.children[1];
varDecl.shouldMatch(Cursor.Kind.VarDecl, "var");
varDecl.children.length.should == 1;

const structDecl = varDecl.children[0];
try
structDecl.shouldMatch(Cursor.Kind.StructDecl, "");
catch(Exception _) { // libclang17
structDecl.kind.should == Cursor.Kind.StructDecl;
"unnamed".should.be in structDecl.spelling;
}
structDecl.children.length.should == 1;

const fieldDecl = structDecl.children[0];
fieldDecl.shouldMatch(Cursor.Kind.FieldDecl, "i");
fieldDecl.children.length.should == 0;
}
}

@("enum.var.anonymous")
@safe unittest {
const tu = parse(
C(
q{
enum {
one = 1,
two = 2,
} numbers;
}
),
);

tu.children.length.should == 2;

{
const enumDecl= tu.children[0];
try
enumDecl.shouldMatch(Cursor.Kind.EnumDecl, "");
catch(Exception _) { // libclang17
enumDecl.kind.should == Cursor.Kind.EnumDecl;
"unnamed".should.be in enumDecl.spelling;
}

printChildren(enumDecl);
enumDecl.children.length.should == 2;

const oneDecl = enumDecl.children[0];
oneDecl.shouldMatch(Cursor.Kind.EnumConstantDecl, "one");
oneDecl.children.length.should == 1;

const twoDecl = enumDecl.children[1];
twoDecl.shouldMatch(Cursor.Kind.EnumConstantDecl, "two");
twoDecl.children.length.should == 1;
}

{
const varDecl = tu.children[1];
varDecl.shouldMatch(Cursor.Kind.VarDecl, "numbers");
varDecl.children.length.should == 1;

const enumDecl = varDecl.children[0];
try
enumDecl.shouldMatch(Cursor.Kind.EnumDecl, "");
catch(Exception _) { // libclang17
enumDecl.kind.should == Cursor.Kind.EnumDecl;
"unnamed".should.be in enumDecl.spelling;
}
enumDecl.children.length.should == 2;

const oneDecl = enumDecl.children[0];
oneDecl.shouldMatch(Cursor.Kind.EnumConstantDecl, "one");
oneDecl.children.length.should == 1;

const twoDecl = enumDecl.children[1];
twoDecl.shouldMatch(Cursor.Kind.EnumConstantDecl, "two");
twoDecl.children.length.should == 1;
}
}
31 changes: 24 additions & 7 deletions tests/contract/inheritance.d
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ import contract;

const baseSpec = derived.child(0);
baseSpec.kind.should == Cursor.Kind.CXXBaseSpecifier;
baseSpec.spelling.should == "struct Base";
baseSpec.type.kind.should == Type.Kind.Record;
baseSpec.spelling.should.be in ["struct Base", "Base"];
baseSpec.type.kind.should.be in [Type.Kind.Record, Type.Kind.Elaborated];
baseSpec.type.spelling.should == "Base";

printChildren(baseSpec);
Expand Down Expand Up @@ -109,7 +109,8 @@ import contract;
const baseSpec = derived.child(0);
baseSpec.kind.should == Cursor.Kind.CXXBaseSpecifier;
baseSpec.spelling.should == "Base<int>";
baseSpec.type.kind.should == Type.Kind.Unexposed; // because it's a template
// because it's a template
baseSpec.type.kind.should.be in [Type.Kind.Unexposed, Type.Kind.Elaborated /*libclang17*/];
baseSpec.type.spelling.should == "Base<int>";
// Here's where the weirdness starts. We try and get back to the original
// ClassTemplate cursor here via the baseSpec type, but instead we get a
Expand Down Expand Up @@ -165,8 +166,16 @@ import contract;
derived.children.length.should == 3;

const baseSpec0 = derived.child(0);
baseSpec0.shouldMatch(Cursor.Kind.CXXBaseSpecifier, "struct Base0");
baseSpec0.type.shouldMatch(Type.Kind.Record, "Base0");
try
baseSpec0.shouldMatch(Cursor.Kind.CXXBaseSpecifier, "struct Base0");
catch(Exception _) // libclang17
baseSpec0.shouldMatch(Cursor.Kind.CXXBaseSpecifier, "Base0");

try
baseSpec0.type.shouldMatch(Type.Kind.Record, "Base0");
catch(Exception _) //libclang17
baseSpec0.type.shouldMatch(Type.Kind.Elaborated, "Base0");

printChildren(baseSpec0);
baseSpec0.children.length.should == 1;

Expand All @@ -176,8 +185,16 @@ import contract;
typeRef0.children.length.should == 0;

const baseSpec1 = derived.child(1);
baseSpec1.shouldMatch(Cursor.Kind.CXXBaseSpecifier, "struct Base1");
baseSpec1.type.shouldMatch(Type.Kind.Record, "Base1");
try
baseSpec1.shouldMatch(Cursor.Kind.CXXBaseSpecifier, "struct Base1");
catch(Exception _) // libclang17
baseSpec1.shouldMatch(Cursor.Kind.CXXBaseSpecifier, "Base1");

try
baseSpec1.type.shouldMatch(Type.Kind.Record, "Base1");
catch(Exception _)
baseSpec1.type.shouldMatch(Type.Kind.Elaborated, "Base1");

printChildren(baseSpec1);
baseSpec1.children.length.should == 1;

Expand Down
Loading

0 comments on commit 739d665

Please sign in to comment.