1 /**
2    Function translations.
3  */
4 module dpp.cursor.function_;
5 
6 import dpp.from;
7 
8 string[] translateFunction(in from!"clang".Cursor cursor,
9                            ref from!"dpp.runtime.context".Context context)
10     @safe
11 {
12     import dpp.cursor.dlang: maybeRename, maybePragma;
13     import dpp.cursor.aggregate: maybeRememberStructs;
14     import dpp.type: translate;
15     import clang: Cursor, Type, Language;
16     import std.array: join, array;
17     import std.conv: text;
18     import std.algorithm: any, endsWith;
19     import std.typecons: Yes;
20 
21     assert(
22         cursor.kind == Cursor.Kind.FunctionDecl ||
23         cursor.kind == Cursor.Kind.CXXMethod ||
24         cursor.kind == Cursor.Kind.Constructor ||
25         cursor.kind == Cursor.Kind.Destructor
26     );
27 
28     // FIXME: Ignore move constructors for now
29     if(cursor.kind == Cursor.Kind.Constructor) {
30         auto paramTypes = paramTypes(cursor);
31         if(paramTypes.any!(a => a.kind == Type.Kind.RValueReference))
32             return [];
33     }
34 
35     const indentation = context.indentation;
36     context.log("Function return type (raw):        ", cursor.type.returnType);
37 
38     const returnType = cursor.kind == Cursor.Kind.Constructor || cursor.kind == Cursor.Kind.Destructor
39         ? ""
40         : translate(cursor.returnType, context, Yes.translatingFunction);
41 
42     context.setIndentation(indentation);
43     context.log("Function return type (translated): ", returnType);
44 
45     maybeRememberStructs(paramTypes(cursor), context);
46 
47     // Here we used to check that if there were no parameters and the language is C,
48     // then the correct translation in D would be (...);
49     // However, that's not allowed in D. It just so happens that C production code
50     // exists that doesn't bother with (void), so instead of producing something that
51     // doesn't compile, we compromise and assume the user meant (void)
52 
53     const paramTypes = translateParamTypes(cursor, context).array;
54     const isVariadic = cursor.type.spelling.endsWith("...)");
55     const variadicParams = isVariadic ? "..." : "";
56     const allParams = paramTypes ~ variadicParams;
57 
58     const spelling = () {
59         if(cursor.kind == Cursor.Kind.Constructor) return "this";
60         if(cursor.kind == Cursor.Kind.Destructor) return "~this";
61         return context.rememberLinkable(cursor);
62     }();
63 
64     // const C++ method?
65     const const_ = cursor.type.spelling.endsWith(") const") ? " const" : "";
66 
67     return [
68         maybePragma(cursor, context) ~
69         text(returnType, " ", spelling, "(", allParams.join(", "), ") @nogc nothrow", const_, ";")
70     ];
71 }
72 
73 auto translateParamTypes(in from!"clang".Cursor cursor,
74                          ref from!"dpp.runtime.context".Context context)
75     @safe
76 {
77     import dpp.type: translate;
78     import std.algorithm: map;
79     import std.range: tee;
80     import std.typecons: Yes;
81 
82     return paramTypes(cursor)
83         .tee!((a){ context.log("Function Child: ", a); })
84         .map!(a => translate(a, context, Yes.translatingFunction))
85         ;
86 }
87 
88 private auto paramTypes(in from!"clang".Cursor cursor)
89     @safe
90 {
91     import clang: Cursor;
92     import std.algorithm: map, filter;
93 
94     return cursor
95         .children
96         .filter!(a => a.kind == Cursor.Kind.ParmDecl)
97         .map!(a => a.type)
98         ;
99 }