1 /**
2    typedef translations
3  */
4 module dpp.translation.typedef_;
5 
6 import dpp.from;
7 
8 string[] translateTypedef(in from!"clang".Cursor typedef_,
9                           ref from!"dpp.runtime.context".Context context)
10     @safe
11 {
12     import dpp.translation.type: translate;
13     import dpp.translation.aggregate: isAggregateC;
14     import dpp.translation.dlang: maybeRename;
15     import clang: Cursor, Type;
16     import std.conv: text;
17     import std.typecons: No;
18     import std.algorithm: filter, canFind;
19     import std.array: array;
20 
21     const children = () @trusted {
22         return typedef_
23         .children
24         .filter!(a => !a.isInvalid)
25         .filter!(a => a.kind != Cursor.Kind.FirstAttr)
26         .array;
27     }();
28 
29     const nonCanonicalUnderlyingType = typedef_.underlyingType;
30     const canonicalUnderlyingType = nonCanonicalUnderlyingType.canonical;
31 
32     context.log("Children: ", children);
33     context.log("          Underlying type: ", nonCanonicalUnderlyingType);
34     context.log("Canonical underlying type: ", canonicalUnderlyingType);
35 
36     if(isSomeFunction(canonicalUnderlyingType))
37         return translateFunctionTypeDef(typedef_, context.indent);
38 
39     const isOnlyAggregateChild = children.length == 1 && isAggregateC(children[0]);
40     const isTopLevelAnonymous =
41         isOnlyAggregateChild &&
42         children[0].spelling == "" && // anonymous
43         children[0].lexicalParent.kind == Cursor.Kind.TranslationUnit; // top-level
44 
45     // if the child is a top-level anonymous struct, it's pointless to alias
46     // it and give the struct a silly name, instead just define a struct with
47     // the typedef name instead. e.g.
48     // typedef struct { int dummy; } Foo -> struct Foo { int dummy; }
49     // However, this isn't true for enums since an anonymous enum can be declared
50     // with no typedef. See #54.
51     if(isTopLevelAnonymous && children[0].kind != Cursor.Kind.EnumDecl)
52         return translateTopLevelAnonymous(children[0], context);
53 
54     // See contract.typedef_.typedef to a template type parameter
55     const isTypeParameter = canonicalUnderlyingType.spelling.canFind("type-parameter-");
56 
57     // FIXME - still not sure I understand isOnlyAggregateChild here
58     const underlyingSpelling = () {
59         if(isOnlyAggregateChild) return context.spellingOrNickname(children[0]);
60         const typeToUse = isTypeParameter ? nonCanonicalUnderlyingType : canonicalUnderlyingType;
61         return translate(typeToUse, context, No.translatingFunction);
62     }();
63 
64     context.rememberType(typedef_.spelling);
65 
66     context.log("");
67 
68     // If the two spellings are the same, it's a `typedef struct foo { } foo`
69     // situration, and there's no reason to alias to anything, so we return nothing.
70     return typedef_.spelling == underlyingSpelling
71         ? []
72         : [`alias ` ~ maybeRename(typedef_, context) ~ ` = ` ~ underlyingSpelling  ~ `;`];
73 }
74 
75 private string[] translateFunctionTypeDef(in from!"clang".Cursor typedef_,
76                                           ref from!"dpp.runtime.context".Context context)
77     @safe
78 {
79     import dpp.translation.type: translate;
80     import dpp.translation.function_: translateParamTypes;
81     import clang: Cursor, Type;
82     import std.algorithm: map, filter;
83     import std.array: join;
84 
85     const underlyingType = typedef_.underlyingType.canonical;
86     const returnType = underlyingType.kind == Type.Kind.Pointer
87         ? underlyingType.pointee.returnType
88         : underlyingType.returnType;
89     context.log("Function typedef return type: ", returnType);
90     const returnTypeTransl = translate(returnType, context);
91 
92     const params = translateParamTypes(typedef_, context.indent).join(", ");
93     return [`alias ` ~ typedef_.spelling ~ ` = ` ~ returnTypeTransl ~ ` function(` ~ params ~ `) @nogc nothrow;`];
94 
95 }
96 
97 private bool isSomeFunction(in from!"clang".Type type) @safe pure nothrow {
98     import clang: Type;
99 
100     const isFunctionPointer =
101         type.kind == Type.Kind.Pointer &&
102         type.pointee.kind == Type.Kind.FunctionProto;
103     const isFunction = type.kind == Type.Kind.FunctionProto;
104 
105     return isFunctionPointer || isFunction;
106 }
107 
108 private string[] translateTopLevelAnonymous(in from!"clang".Cursor cursor,
109                                             ref from!"dpp.runtime.context".Context context)
110     @safe
111 {
112     import dpp.translation.translation: translate;
113     import clang: Cursor;
114 
115     // the old cursor has no spelling, so construct a new one
116     auto newCursor = Cursor(cursor.cx);
117 
118     // the type spelling will be the name of the struct, union, or enum
119     newCursor.spelling = cursor.type.spelling;
120 
121     // delegate to whoever knows what they're doing
122     return translate(newCursor, context);
123 }