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 }