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