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 }