1 module dpp.translation.variable;
2 
3 import dpp.from;
4 import dpp.translation.docs;
5 
6 string[] translateVariable(in from!"clang".Cursor cursor,
7                            ref from!"dpp.runtime.context".Context context)
8     @safe
9     in(cursor.kind == from!"clang".Cursor.Kind.VarDecl)
10     do
11 {
12     import dpp.translation.exception: UntranslatableException;
13     import dpp.translation.dlang: maybePragma;
14     import dpp.translation.translation: translateCursor = translate;
15     import dpp.translation.type: translateType = translate, hasAnonymousSpelling;
16     import dpp.translation.tokens: translateTokens;
17     import clang: Cursor, Type, Token;
18     import std.conv: text;
19     import std.typecons: No;
20     import std.algorithm: canFind, find, map;
21     import std.array: empty, popFront, join, replace;
22 
23     string[] ret;
24 
25     const isAnonymous = cursor.type.hasAnonymousSpelling;
26     // If the type is anonymous, then we need to define it before we declare
27     // ourselves of that type, unless that type is an enum. See #54.
28     if(isAnonymous && cursor.type.canonical.declaration.kind != Cursor.Kind.EnumDecl) {
29         ret ~= translateCursor(cursor.type.canonical.declaration, context);
30     }
31 
32     // variables can be declared multiple times in C but only one in D
33     if(!cursor.isCanonical) return [];
34 
35     // Don't bother if we don't have a definition anywhere - C allows this but D
36     // doesn't. See it.compile.projects.ASN1_ITEM or try #including <openssl/ssl.h>.
37     // There will be a problem with the global variables such as DHparams_it that
38     // have a struct with an unknown type unless one includes <openssl/asn1t.h>
39     // as well. In C, as long as one doesn't try to do anything with the variable,
40     // that's ok. In D, it's not. Essentially:
41     // struct Foo;
42     // extern Foo gFoo;
43     if(isRecordWithoutDefinition(cursor, context)) return [];
44 
45     const spelling = context.rememberLinkable(cursor);
46 
47     // global variable or static member of a struct/class?
48     const static_ = cursor.semanticParent.type.canonical.kind == Type.Kind.Record
49         ? "static  "
50         : "";
51     // e.g. enum foo = 42;
52     const constexpr = cursor.tokens.canFind(Token(Token.Kind.Keyword, "constexpr"));
53 
54     if(constexpr)
55         ret ~= translateConstexpr(spelling, cursor, context);
56     else {
57         const maybeTypeSpelling = translateType(cursor.type, context, No.translatingFunction);
58         // In C it is possible to have an extern void variable
59         const typeSpelling = cursor.type.kind == Type.Kind.Void
60             ? maybeTypeSpelling.replace("void", "void*")
61             : maybeTypeSpelling;
62         ret ~=
63             maybePragma(cursor, context) ~
64             text("extern __gshared ", static_, typeSpelling, " ", spelling, ";");
65     }
66 
67     // attach variable docs
68     ret = getComment(cursor) ~ ret;
69 
70     return ret;
71 }
72 
73 
74 private string[] translateConstexpr(in string spelling,
75                                     in from!"clang".Cursor cursor,
76                                     ref from!"dpp.runtime.context".Context context)
77     @safe
78 {
79     import dpp.translation.tokens: translateTokens;
80     import dpp.translation.exception: UntranslatableException;
81     import dpp.translation.type: translate;
82     import clang: Cursor, Token;
83     import std.algorithm: find, canFind;
84     import std.conv: text;
85     import std.array: empty, popFront;
86     import std.typecons: No;
87 
88     auto tokens = cursor.tokens;
89     tokens = tokens.find!(a => a.kind == Token.Kind.Punctuation && a.spelling == "=");
90 
91     const init = () {
92         // see contract.constexpr.variable.init.braces
93         if(cursor.children.canFind!(c => c.kind == Cursor.Kind.InitListExpr))
94             return " = " ~ translate(cursor.type, context, No.translatingFunction) ~ ".init";
95 
96         if(!tokens.empty) {
97             tokens.popFront;
98             return " = " ~ translateTokens(tokens);
99         }
100 
101         throw new UntranslatableException(
102             text("Could not find assignment in ", cursor.tokens,
103                  "\ncursor: ", cursor, "\n@ ", cursor.sourceRange));
104     }();
105 
106     return [ text("enum ", spelling, init, ";") ];
107 }
108 
109 private bool isRecordWithoutDefinition(
110     in from!"clang".Cursor cursor,
111     ref from!"dpp.runtime.context".Context context)
112     @safe
113 {
114     import clang: Cursor, Type;
115 
116     const canonicalType = cursor.type.canonical;
117 
118     if(canonicalType.kind != Type.Kind.Record)
119         return false;
120 
121     const declaration = canonicalType.declaration;
122     const definition = declaration.definition;
123     const specializedTemplate = declaration.specializedCursorTemplate;
124 
125     context.log("canonicalType: ", canonicalType);
126     context.log("declaration: ", declaration);
127     context.log("definition: ", definition);
128     context.log("specialised cursor template: ", specializedTemplate);
129 
130     return
131         definition.isInvalid &&
132         // See #97
133         specializedTemplate.kind != Cursor.Kind.ClassTemplate
134         ;
135 }