1 /**
2    Cursor translations
3  */
4 module dpp.translation.translation;
5 
6 import dpp.from;
7 
8 
9 alias Translator = string[] function(
10     in from!"clang".Cursor cursor,
11     ref from!"dpp.runtime.context".Context context,
12 ) @safe;
13 
14 string translateTopLevelCursor(in from!"clang".Cursor cursor,
15                                ref from!"dpp.runtime.context".Context context,
16                                in string file = __FILE__,
17                                in size_t line = __LINE__)
18     @safe
19 {
20     import std.array: join;
21     import std.algorithm: map;
22 
23     return skipTopLevel(cursor, context)
24         ? ""
25         : translate(cursor, context, file, line).map!(a => "    " ~ a).join("\n");
26 }
27 
28 private bool skipTopLevel(in from!"clang".Cursor cursor,
29                           in from!"dpp.runtime.context".Context context)
30     @safe pure
31 {
32     import dpp.translation.aggregate: isAggregateC;
33     import clang: Cursor;
34     import std.algorithm: startsWith, canFind;
35 
36     // We want to ignore anonymous structs and unions but not enums. See #54
37     if(cursor.spelling == "" && cursor.kind == Cursor.Kind.EnumDecl)
38         return false;
39 
40     // don't bother translating top-level anonymous aggregates
41     if(isAggregateC(cursor) && cursor.spelling == "")
42         return true;
43 
44     if(context.options.ignoreMacros && cursor.kind == Cursor.Kind.MacroDefinition)
45         return true;
46 
47     static immutable forbiddenSpellings =
48         [
49             "ulong", "ushort", "uint",
50             "va_list", "__gnuc_va_list",
51             "_IO_2_1_stdin_", "_IO_2_1_stdout_", "_IO_2_1_stderr_",
52         ];
53 
54     return forbiddenSpellings.canFind(cursor.spelling) ||
55         cursor.isPredefined ||
56         cursor.kind == Cursor.Kind.MacroExpansion
57         ;
58 }
59 
60 
61 string[] translate(in from!"clang".Cursor cursor,
62                    ref from!"dpp.runtime.context".Context context,
63                    in string file = __FILE__,
64                    in size_t line = __LINE__)
65     @safe
66 {
67     import dpp.runtime.context: Language;
68     import dpp.translation.exception: UntranslatableException;
69     import std.conv: text;
70     import std.algorithm: canFind, any;
71     import std.array: join;
72 
73     debugCursor(cursor, context);
74 
75     if(context.language == Language.Cpp && ignoredCppCursorSpellings.canFind(cursor.spelling)) {
76         return [];
77     }
78 
79     if(context.options.ignoredCursors.canFind(cursor.spelling)) {
80         return [];
81     }
82 
83     if(cursor.kind !in translators) {
84         if(context.options.hardFail)
85             throw new Exception(text("Cannot translate unknown cursor kind ", cursor.kind),
86                                 file,
87                                 line);
88         else
89             return [];
90     }
91 
92     const indentation = context.indentation;
93     scope(exit) context.setIndentation(indentation);
94     context.indent;
95 
96     try {
97         auto lines = translators[cursor.kind](cursor, context);
98 
99         if(lines.any!untranslatable)
100             throw new UntranslatableException(
101                 text("Not valid D:\n",
102                      "------------\n",
103                      lines.join("\n"),
104                     "\n------------\n",));
105 
106         return lines;
107     } catch(UntranslatableException e) {
108 
109         debug {
110             import std.stdio: stderr;
111             () @trusted {
112                 if(context.options.detailedUntranslatable)
113                     stderr.writeln("\nUntranslatable cursor ", cursor,
114                                    "\nmsg: ", e.msg,
115                                    "\nsourceRange: ", cursor.sourceRange,
116                                    "\nchildren: ", cursor.children,
117                                    "\n");
118                 else
119                     stderr.writeln("Untranslatable cursor ", cursor);
120             }();
121         }
122 
123         if(context.options.hardFail)
124             throw e;
125         else
126             return [];
127 
128     } catch(Exception e) {
129 
130         debug {
131             import std.stdio: stderr;
132             () @trusted {
133                 stderr.writeln("\nCould not translate cursor ", cursor,
134                                "\nmsg: ", e.msg,
135                                "\nsourceRange: ", cursor.sourceRange,
136                                "\nchildren: ", cursor.children, "\n");
137             }();
138         }
139 
140         throw e;
141     }
142 }
143 
144 void debugCursor(in from!"clang".Cursor cursor,
145                  in from!"dpp.runtime.context".Context context)
146     @safe
147 {
148     import clang: Cursor;
149     import std.algorithm: startsWith, canFind;
150 
151     version(unittest) {}
152     else if(!context.debugOutput) return;
153 
154     const isMacro = cursor.kind == Cursor.Kind.MacroDefinition;
155     const isOkMacro =
156         !cursor.spelling.startsWith("__") &&
157         !cursor.spelling.startsWith("_GLIBCXX") &&
158         !["_LP64", "unix", "linux"].canFind(cursor.spelling);
159     const canonical = cursor.isCanonical ? " CAN" : "";
160     const definition = cursor.isDefinition ? " DEF" : "";
161 
162     if(!isMacro || isOkMacro) {
163         context.log(cursor, canonical, definition, " @ ", cursor.sourceRange);
164     }
165 }
166 
167 Translator[from!"clang".Cursor.Kind] translators() @safe {
168     import dpp.translation;
169     import clang: Cursor;
170 
171     static string[] ignore(
172         in Cursor cursor,
173         ref from!"dpp.runtime.context".Context context)
174     {
175         return [];
176     }
177 
178     static string[] translateUnexposed(
179         in Cursor cursor,
180         ref from!"dpp.runtime.context".Context context)
181     {
182         import clang: Type;
183         import std.conv: text;
184 
185         switch(cursor.type.kind) with(Type.Kind) {
186             default:
187                 throw new Exception(text("Unknown unexposed declaration type ", cursor.type));
188             case Invalid:
189                 return [];
190         }
191         assert(0);
192     }
193 
194     static string[] translateAccess(
195         in Cursor cursor,
196         ref from!"dpp.runtime.context".Context context)
197     {
198         import clang: AccessSpecifier;
199 
200         context.accessSpecifier = cursor.accessSpecifier;
201 
202         final switch(cursor.accessSpecifier) with(AccessSpecifier) {
203             case InvalidAccessSpecifier: assert(0);
204             case Public: return ["    public:"];
205             case Protected: return ["    protected:"];
206             case Private: return ["    private:"];
207         }
208 
209         assert(0);
210     }
211 
212     with(Cursor.Kind) {
213         return [
214             ClassDecl:                          &translateClass,
215             StructDecl:                         &translateStruct,
216             UnionDecl:                          &translateUnion,
217             EnumDecl:                           &translateEnum,
218             FunctionDecl:                       &translateFunction,
219             FieldDecl:                          &translateField,
220             TypedefDecl:                        &translateTypedef,
221             MacroDefinition:                    &translateMacro,
222             InclusionDirective:                 &ignore,
223             EnumConstantDecl:                   &translateEnumConstant,
224             VarDecl:                            &translateVariable,
225             UnexposedDecl:                      &translateUnexposed,
226             CXXAccessSpecifier:                 &translateAccess,
227             CXXMethod:                          &translateFunction,
228             Constructor:                        &translateFunction,
229             Destructor:                         &translateFunction,
230             TypeAliasDecl:                      &translateTypedef,
231             ClassTemplate:                      &translateClass,
232             TemplateTypeParameter:              &ignore,
233             NonTypeTemplateParameter:           &ignore,
234             ConversionFunction:                 &translateFunction,
235             Namespace:                          &translateNamespace,
236             VisibilityAttr:                     &ignore, // ???
237             // FirstAttr appears when there are compiler-specific attributes on a type
238             FirstAttr:                          &ignore,
239             ClassTemplatePartialSpecialization: &translateClass,
240             TypeAliasTemplateDecl:              &translateTypeAliasTemplate,
241             FunctionTemplate:                   &translateFunction,
242             // For ParmDecl, see it.cpp.opaque.std::function
243             ParmDecl:                           &ignore,
244         ];
245     }
246 }
247 
248 
249 // if this translated line can't be valid D code
250 bool untranslatable(in string line) @safe pure {
251     import std.algorithm: canFind;
252     return
253         line.canFind(`&)`)
254         || line.canFind("&,")
255         || line.canFind("&...")
256         || line.canFind(" (*)")
257         || line.canFind("variant!")
258         || line.canFind("value _ ")
259         || line.canFind("enable_if_c")
260         || line.canFind(`(this_)_M_t._M_equal_range_tr(`)
261         || line.canFind(`this-`)
262         || line.canFind("_BoundArgs...")
263         || line.canFind("sizeof...")
264         || line.canFind("template<")  // FIXME: mir_slice
265         ;
266 }
267 
268 
269 // blacklist of cursors in the C++ standard library that dpp can't handle
270 private string[] ignoredCppCursorSpellings() @safe pure nothrow {
271     return
272         [
273             "is_function",  // dmd bug
274             "is_const",
275             "is_volatile",
276             "allocator_traits",  // FIXME
277             "pair",  // FIXME
278             "underlying_type",
279             "underlying_type_t",
280             "result_of",
281             "result_of_t",
282             "pointer_traits", // FIXME
283             "iterator_traits",  // FIXME
284             "piecewise_construct", // FIXME
285             "is_rvalue_reference",
286             "remove_reference",
287             "remove_reference_t",
288             "remove_extent",  // FIXME
289             "remove_extent_t",  // FIXME
290             "remove_all_extents",  // FIXME
291             "remove_all_extents_t",  // FIXME
292 
293             // derives from std::iterator, which is untranslatable due to it taking a
294             // reference template parameter
295             "_Bit_iterator_base",
296             "_Bit_iterator",
297             "_Bit_const_iterator",
298             // needs _Bit_iterator and co
299             "_Bvector_base",
300         ];
301 }