Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix libclang17 #344

Merged
merged 2 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading