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 59 auto translation = context.aggregateIsRemembered(children[0]) ? [] : translate(children[0], context); 60 auto regularTranslation = translateRegular(cursor, context, children); 61 62 return translation ~ regularTranslation; 63 64 } 65 66 return noName 67 ? translateTopLevelAnonymous(children[0], context) 68 : translateRegular(cursor, context, children); 69 } 70 71 72 private bool isTopLevelAnonymous(in from!"clang".Cursor[] children) 73 @safe nothrow 74 { 75 import clang: Cursor; 76 return 77 children.length == 1 && // so we can inspect it 78 children[0].spelling == "" && // anonymous 79 children[0].lexicalParent.kind == Cursor.Kind.TranslationUnit // top-level 80 ; 81 } 82 83 // non-anonymous non-function typedef 84 private string[] translateRegular(in from!"clang".Cursor cursor, 85 ref from!"dpp.runtime.context".Context context, 86 in from!"clang".Cursor[] children) 87 @safe 88 { 89 import dpp.translation.type: translate, removeDppDecorators; 90 import dpp.translation.aggregate: isAggregateC; 91 import dpp.translation.dlang: maybeRename; 92 import clang: Type; 93 import std.typecons: No; 94 95 auto underlyingSpelling = () { 96 switch(cursor.spelling) { 97 default: 98 // The cursor will have a type with spelling despite not having a spelling itself. 99 // We use the nickname we've given it in D if it's the case. 100 const isAnonymousEnum = 101 children.length == 1 && 102 isAggregateC(children[0]) && 103 children[0].spelling == "" && 104 children[0].type.kind == Type.Kind.Enum 105 ; 106 107 return isAnonymousEnum 108 ? context.spellingOrNickname(children[0]) 109 : translate(cursor.underlyingType, context, No.translatingFunction) 110 .removeDppDecorators; 111 112 // possible issues on 32-bit 113 case "int32_t": return "int"; 114 case "uint32_t": return "uint"; 115 case "in64_t": return "long"; 116 case "uint64_t": return "ulong"; 117 case "nullptr_t": return "typeof(null)"; 118 } 119 }(); 120 121 context.rememberType(cursor.spelling); 122 123 context.log(""); 124 125 // This is to prevent trying to translate `typedef struct Struct Struct;` which 126 // makes no sense in D. 127 return cursor.spelling == underlyingSpelling 128 ? [] 129 : [getComment(cursor), `alias ` ~ maybeRename(cursor, context) ~ ` = ` ~ underlyingSpelling ~ `;`]; 130 } 131 132 133 private string[] translateFunction(in from!"clang".Cursor typedef_, 134 ref from!"dpp.runtime.context".Context context) 135 @safe 136 { 137 import dpp.translation.type: translate; 138 import dpp.translation.function_: translateParamTypes; 139 import clang: Cursor, Type; 140 import std.algorithm: map, filter; 141 import std.array: join; 142 143 const underlyingType = typedef_.underlyingType.canonical; 144 const returnType = underlyingType.kind == Type.Kind.Pointer 145 ? underlyingType.pointee.returnType 146 : underlyingType.returnType; 147 context.log("Function typedef return type: ", returnType); 148 const returnTypeTransl = translate(returnType, context); 149 150 const functionType = typedef_.underlyingType.canonical.kind == Type.Kind.Pointer 151 ? typedef_.underlyingType.canonical.pointee 152 : typedef_.underlyingType.canonical; 153 154 const params = translateParamTypes(typedef_, functionType, context.indent).join(", "); 155 return [`alias ` ~ typedef_.spelling ~ ` = ` ~ returnTypeTransl ~ ` function(` ~ params ~ `);`]; 156 157 } 158 159 private bool isSomeFunction(in from!"clang".Type type) @safe pure nothrow { 160 import clang: Type; 161 162 const isFunctionPointer = 163 type.kind == Type.Kind.Pointer && 164 type.pointee.kind == Type.Kind.FunctionProto; 165 const isFunction = type.kind == Type.Kind.FunctionProto; 166 167 return isFunctionPointer || isFunction; 168 } 169 170 private string[] translateTopLevelAnonymous(in from!"clang".Cursor cursor, 171 ref from!"dpp.runtime.context".Context context) 172 @safe 173 { 174 import dpp.translation.translation: translate; 175 import clang: Cursor; 176 177 // the old cursor has no spelling, so construct a new one 178 auto newCursor = Cursor(cursor.cx); 179 180 // the type spelling will be the name of the struct, union, or enum 181 newCursor.setSpelling(cursor.type.spelling); 182 183 // delegate to whoever knows what they're doing 184 return translate(newCursor, context); 185 }