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, removeDppDecorators;
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                     .removeDppDecorators;
93 
94         // possible issues on 32-bit
95         case "int32_t":  return "int";
96         case "uint32_t": return "uint";
97         case "in64_t":   return "long";
98         case "uint64_t": return "ulong";
99         case "nullptr_t": return "typeof(null)";
100         }
101     }();
102 
103     context.rememberType(cursor.spelling);
104 
105     context.log("");
106 
107     // This is to prevent trying to translate `typedef struct Struct Struct;` which
108     // makes no sense in D.
109     return cursor.spelling == underlyingSpelling
110         ? []
111         : [`alias ` ~ maybeRename(cursor, context) ~ ` = ` ~ underlyingSpelling  ~ `;`];
112 }
113 
114 
115 private string[] translateFunction(in from!"clang".Cursor typedef_,
116                                    ref from!"dpp.runtime.context".Context context)
117     @safe
118 {
119     import dpp.translation.type: translate;
120     import dpp.translation.function_: translateParamTypes;
121     import clang: Cursor, Type;
122     import std.algorithm: map, filter;
123     import std.array: join;
124 
125     const underlyingType = typedef_.underlyingType.canonical;
126     const returnType = underlyingType.kind == Type.Kind.Pointer
127         ? underlyingType.pointee.returnType
128         : underlyingType.returnType;
129     context.log("Function typedef return type: ", returnType);
130     const returnTypeTransl = translate(returnType, context);
131 
132     const functionType = typedef_.underlyingType.canonical.kind == Type.Kind.Pointer
133         ? typedef_.underlyingType.canonical.pointee
134         : typedef_.underlyingType.canonical;
135 
136     const params = translateParamTypes(typedef_, functionType, context.indent).join(", ");
137     return [`alias ` ~ typedef_.spelling ~ ` = ` ~ returnTypeTransl ~ ` function(` ~ params ~ `);`];
138 
139 }
140 
141 private bool isSomeFunction(in from!"clang".Type type) @safe pure nothrow {
142     import clang: Type;
143 
144     const isFunctionPointer =
145         type.kind == Type.Kind.Pointer &&
146         type.pointee.kind == Type.Kind.FunctionProto;
147     const isFunction = type.kind == Type.Kind.FunctionProto;
148 
149     return isFunctionPointer || isFunction;
150 }
151 
152 private string[] translateTopLevelAnonymous(in from!"clang".Cursor cursor,
153                                             ref from!"dpp.runtime.context".Context context)
154     @safe
155 {
156     import dpp.translation.translation: translate;
157     import clang: Cursor;
158 
159     // the old cursor has no spelling, so construct a new one
160     auto newCursor = Cursor(cursor.cx);
161 
162     // the type spelling will be the name of the struct, union, or enum
163     newCursor.setSpelling(cursor.type.spelling);
164 
165     // delegate to whoever knows what they're doing
166     return translate(newCursor, context);
167 }