1 /**
2    typedef translations
3  */
4 module dpp.cursor.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.type: translate;
13     import dpp.cursor.aggregate: isAggregateC;
14     import clang: Cursor, Type;
15     import std.conv: text;
16     import std.typecons: No;
17     import std.algorithm: filter;
18     import std.array: array;
19 
20     const children = () @trusted {
21         return typedef_
22         .children
23         .filter!(a => !a.isInvalid)
24         .filter!(a => a.kind != Cursor.Kind.FirstAttr)
25         .array;
26     }();
27 
28     const nonCanonicalUnderlyingType = typedef_.underlyingType;
29     const underlyingType = nonCanonicalUnderlyingType.canonical;
30 
31     context.log("Children: ", children);
32     context.log("          Underlying type: ", nonCanonicalUnderlyingType);
33     context.log("Canonical underlying type: ", underlyingType);
34 
35     if(isSomeFunction(underlyingType))
36         return translateFunctionTypeDef(typedef_, context.indent);
37 
38     const isOnlyAggregateChild = children.length == 1 && isAggregateC(children[0]);
39     const isTopLevelAnonymous =
40         isOnlyAggregateChild &&
41         children[0].spelling == "" && // anonymous
42         children[0].lexicalParent.kind == Cursor.Kind.TranslationUnit; // top-level
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     if(isTopLevelAnonymous) return translateTopLevelAnonymous(children[0], context);
49 
50     // FIXME - still not sure I understand this
51     const underlyingSpelling = isOnlyAggregateChild
52         ? context.spellingOrNickname(children[0])
53         : translate(underlyingType, context, No.translatingFunction);
54 
55     // If the two spellings are the same, it's a `typedef struct foo { } foo`
56     // situration, and there's no reason to alias to anything, so we return nothing.
57     return typedef_.spelling == underlyingSpelling
58         ? []
59         : [`alias ` ~ typedef_.spelling ~ ` = ` ~ underlyingSpelling  ~ `;`];
60 }
61 
62 private string[] translateFunctionTypeDef(in from!"clang".Cursor typedef_,
63                                           ref from!"dpp.runtime.context".Context context)
64     @safe
65 {
66     import dpp.type: translate;
67     import dpp.cursor.function_: translateParamTypes;
68     import clang: Cursor, Type;
69     import std.algorithm: map, filter;
70     import std.array: join;
71 
72     const underlyingType = typedef_.underlyingType.canonical;
73     const returnType = underlyingType.kind == Type.Kind.Pointer
74         ? underlyingType.pointee.returnType
75         : underlyingType.returnType;
76     context.log("Function typedef return type: ", returnType);
77     const returnTypeTransl = translate(returnType, context);
78 
79     const params = translateParamTypes(typedef_, context.indent).join(", ");
80     return [`alias ` ~ typedef_.spelling ~ ` = ` ~ returnTypeTransl ~ ` function(` ~ params ~ `);`];
81 
82 }
83 
84 private bool isSomeFunction(in from!"clang".Type type) @safe @nogc pure nothrow {
85     import clang: Type;
86 
87     const isFunctionPointer =
88         type.kind == Type.Kind.Pointer &&
89         type.pointee.kind == Type.Kind.FunctionProto;
90     const isFunction = type.kind == Type.Kind.FunctionProto;
91 
92     return isFunctionPointer || isFunction;
93 }
94 
95 private string[] translateTopLevelAnonymous(in from!"clang".Cursor cursor,
96                                             ref from!"dpp.runtime.context".Context context)
97     @safe
98 {
99     import dpp.cursor.translation: translate;
100     import clang: Cursor;
101 
102     // the old cursor has no spelling, so construct a new one
103     auto newCursor = Cursor(cursor.cx);
104 
105     // the type spelling will be the name of the struct, union, or enum
106     newCursor.spelling = cursor.type.spelling;
107 
108     // delegate to whoever knows what they're doing
109     return translate(newCursor, context);
110 }