1 /** 2 typedef translations 3 */ 4 module dpp.translation.typedef_; 5 6 7 import dpp.from; 8 9 10 string[] translateTypedef(in from!"clang".Cursor cursor, 11 ref from!"dpp.runtime.context".Context context) 12 @safe 13 { 14 // canonical because otherwise tests fail on Travis's version of libclang 15 return isSomeFunction(cursor.underlyingType.canonical) 16 ? translateFunction(cursor, context.indent) 17 : translateNonFunction(cursor, context); 18 } 19 20 21 string[] translateNonFunction(in from!"clang".Cursor cursor, 22 ref from!"dpp.runtime.context".Context context) 23 @safe 24 { 25 import clang: Cursor, Type; 26 import std.algorithm: filter; 27 import std.array: array; 28 29 auto childrenRange = cursor 30 .children 31 .filter!(a => !a.isInvalid) 32 // only interested in the actual type we're aliasing 33 .filter!(a => a.type.kind != Type.Kind.Invalid) 34 ; 35 36 // who knows why this is @system 37 const children = () @trusted { return childrenRange.array; }(); 38 39 // children might contain 0, 1, or more entries due to libclang particularities 40 context.log("Children: ", children); 41 context.log("Underlying type: ", cursor.underlyingType); 42 43 // If the child is a top-level anonymous struct, it's pointless to alias 44 // it and give the struct a silly name, instead just define a struct with 45 // the typedef name instead. e.g. 46 // `typedef struct { int dummy; } Foo` -> `struct Foo { int dummy; }` 47 // However, this isn't true for enums since an anonymous enum can be declared 48 // with no typedef. See #54. 49 const noName = isTopLevelAnonymous(children) && children[0].kind != Cursor.Kind.EnumDecl; 50 51 return noName 52 ? translateTopLevelAnonymous(children[0], context) 53 : translateRegular(cursor, context, children); 54 } 55 56 57 private bool isTopLevelAnonymous(in from!"clang".Cursor[] children) 58 @safe nothrow 59 { 60 import clang: Cursor; 61 return 62 children.length == 1 && // so we can inspect it 63 children[0].spelling == "" && // anonymous 64 children[0].lexicalParent.kind == Cursor.Kind.TranslationUnit // top-level 65 ; 66 } 67 68 // non-anonymous non-function typedef 69 private string[] translateRegular(in from!"clang".Cursor cursor, 70 ref from!"dpp.runtime.context".Context context, 71 in from!"clang".Cursor[] children) 72 @safe 73 { 74 import dpp.translation.type: translate; 75 import dpp.translation.aggregate: isAggregateC; 76 import dpp.translation.dlang: maybeRename; 77 import std.typecons: No; 78 79 const underlyingSpelling = () { 80 switch(cursor.spelling) { 81 default: 82 // The cursor will have a type with spelling despite not having spelling itself. 83 // We use the nickname we've given it in D if it's the case. 84 const isAnonymousAggregate = 85 children.length == 1 && 86 isAggregateC(children[0]) && 87 children[0].spelling == ""; 88 89 return isAnonymousAggregate 90 ? context.spellingOrNickname(children[0]) 91 : translate(cursor.underlyingType, context, No.translatingFunction); 92 93 // possible issues on 32-bit 94 case "int32_t": return "int"; 95 case "uint32_t": return "uint"; 96 case "in64_t": return "long"; 97 case "uint64_t": return "ulong"; 98 case "nullptr_t": return "typeof(null)"; 99 } 100 }(); 101 102 context.rememberType(cursor.spelling); 103 104 context.log(""); 105 106 // This is to prevent trying to translate `typedef struct Struct Struct;` which 107 // makes no sense in D. 108 return cursor.spelling == underlyingSpelling 109 ? [] 110 : [`alias ` ~ maybeRename(cursor, context) ~ ` = ` ~ underlyingSpelling ~ `;`]; 111 } 112 113 114 private string[] translateFunction(in from!"clang".Cursor typedef_, 115 ref from!"dpp.runtime.context".Context context) 116 @safe 117 { 118 import dpp.translation.type: translate; 119 import dpp.translation.function_: translateParamTypes; 120 import clang: Cursor, Type; 121 import std.algorithm: map, filter; 122 import std.array: join; 123 124 const underlyingType = typedef_.underlyingType.canonical; 125 const returnType = underlyingType.kind == Type.Kind.Pointer 126 ? underlyingType.pointee.returnType 127 : underlyingType.returnType; 128 context.log("Function typedef return type: ", returnType); 129 const returnTypeTransl = translate(returnType, context); 130 131 const functionType = typedef_.underlyingType.canonical.kind == Type.Kind.Pointer 132 ? typedef_.underlyingType.canonical.pointee 133 : typedef_.underlyingType.canonical; 134 135 const params = translateParamTypes(typedef_, functionType, context.indent).join(", "); 136 return [`alias ` ~ typedef_.spelling ~ ` = ` ~ returnTypeTransl ~ ` function(` ~ params ~ `);`]; 137 138 } 139 140 private bool isSomeFunction(in from!"clang".Type type) @safe pure nothrow { 141 import clang: Type; 142 143 const isFunctionPointer = 144 type.kind == Type.Kind.Pointer && 145 type.pointee.kind == Type.Kind.FunctionProto; 146 const isFunction = type.kind == Type.Kind.FunctionProto; 147 148 return isFunctionPointer || isFunction; 149 } 150 151 private string[] translateTopLevelAnonymous(in from!"clang".Cursor cursor, 152 ref from!"dpp.runtime.context".Context context) 153 @safe 154 { 155 import dpp.translation.translation: translate; 156 import clang: Cursor; 157 158 // the old cursor has no spelling, so construct a new one 159 auto newCursor = Cursor(cursor.cx); 160 161 // the type spelling will be the name of the struct, union, or enum 162 newCursor.spelling = cursor.type.spelling; 163 164 // delegate to whoever knows what they're doing 165 return translate(newCursor, context); 166 }