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, isTypeParameter;
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     // FIXME - still not sure I understand isOnlyAggregateChild here
55     const underlyingSpelling = () {
56         if(isOnlyAggregateChild) return context.spellingOrNickname(children[0]);
57         const typeToUse = isTypeParameter(canonicalUnderlyingType)
58             ? nonCanonicalUnderlyingType
59             : canonicalUnderlyingType;
60         return translate(typeToUse, context, No.translatingFunction);
61     }();
62 
63     context.rememberType(typedef_.spelling);
64 
65     context.log("");
66 
67     // If the two spellings are the same, it's a `typedef struct foo { } foo`
68     // situration, and there's no reason to alias to anything, so we return nothing.
69     return typedef_.spelling == underlyingSpelling
70         ? []
71         : [`alias ` ~ maybeRename(typedef_, context) ~ ` = ` ~ underlyingSpelling  ~ `;`];
72 }
73 
74 private string[] translateFunctionTypeDef(in from!"clang".Cursor typedef_,
75                                           ref from!"dpp.runtime.context".Context context)
76     @safe
77 {
78     import dpp.translation.type: translate;
79     import dpp.translation.function_: translateParamTypes;
80     import clang: Cursor, Type;
81     import std.algorithm: map, filter;
82     import std.array: join;
83 
84     const underlyingType = typedef_.underlyingType.canonical;
85     const returnType = underlyingType.kind == Type.Kind.Pointer
86         ? underlyingType.pointee.returnType
87         : underlyingType.returnType;
88     context.log("Function typedef return type: ", returnType);
89     const returnTypeTransl = translate(returnType, context);
90 
91     const params = translateParamTypes(typedef_, context.indent).join(", ");
92     return [`alias ` ~ typedef_.spelling ~ ` = ` ~ returnTypeTransl ~ ` function(` ~ params ~ `) @nogc nothrow;`];
93 
94 }
95 
96 private bool isSomeFunction(in from!"clang".Type type) @safe pure nothrow {
97     import clang: Type;
98 
99     const isFunctionPointer =
100         type.kind == Type.Kind.Pointer &&
101         type.pointee.kind == Type.Kind.FunctionProto;
102     const isFunction = type.kind == Type.Kind.FunctionProto;
103 
104     return isFunctionPointer || isFunction;
105 }
106 
107 private string[] translateTopLevelAnonymous(in from!"clang".Cursor cursor,
108                                             ref from!"dpp.runtime.context".Context context)
109     @safe
110 {
111     import dpp.translation.translation: translate;
112     import clang: Cursor;
113 
114     // the old cursor has no spelling, so construct a new one
115     auto newCursor = Cursor(cursor.cx);
116 
117     // the type spelling will be the name of the struct, union, or enum
118     newCursor.spelling = cursor.type.spelling;
119 
120     // delegate to whoever knows what they're doing
121     return translate(newCursor, context);
122 }