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