1 /**
2  * Copyright: Copyright (c) 2016 Wojciech Szęszoł. All rights reserved.
3  * Authors: Wojciech Szęszoł
4  * Version: Initial created: Mar 21, 2016
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dstep.translator.Context;
8 
9 import clang.c.Index;
10 import clang.Cursor;
11 import clang.Index;
12 import clang.SourceRange;
13 import clang.TranslationUnit;
14 
15 import dstep.translator.CommentIndex;
16 import dstep.translator.IncludeHandler;
17 import dstep.translator.MacroDefinition;
18 import dstep.translator.MacroIndex;
19 import dstep.translator.Options;
20 import dstep.translator.Output;
21 import dstep.translator.Translator;
22 import dstep.translator.TypedefIndex;
23 import dstep.translator.HeaderIndex;
24 
25 class Context
26 {
27     public MacroIndex macroIndex;
28     public TranslationUnit translUnit;
29 
30     private string[Cursor] anonymousNames;
31     private bool[Cursor] alreadyDefined_;
32     private IncludeHandler includeHandler_;
33     private CommentIndex commentIndex_ = null;
34     private TypedefIndex typedefIndex_ = null;
35     private HeaderIndex headerIndex_ = null;
36     private Translator translator_ = null;
37     private Output globalScope_ = null;
38     private Cursor[string] typeNames_;
39     private Cursor[string] constNames_;
40     private string[Cursor] translatedSpellings;
41 
42     Options options;
43 
44     const string source;
45 
46     public this(TranslationUnit translUnit, Options options, Translator translator = null)
47     {
48         this.translUnit = translUnit;
49         macroIndex = new MacroIndex(translUnit);
50         includeHandler_ = new IncludeHandler(headerIndex, options);
51 
52         foreach (item; options.globalImports)
53             includeHandler_.addImport(item);
54 
55         this.options = options;
56 
57         if (options.enableComments)
58         {
59             auto location = macroIndex.includeGuardLocation;
60 
61             if (location[0])
62                 commentIndex_ = new CommentIndex(
63                     translUnit,
64                     location[1]);
65             else
66                 commentIndex_ = new CommentIndex(translUnit);
67         }
68 
69         typedefIndex_ = new TypedefIndex(translUnit, options.isWantedCursorForTypedefs);
70 
71         if (translator !is null)
72             translator_ = translator;
73         else
74             translator_ = new Translator(translUnit, options);
75 
76         globalScope_ = new Output();
77         collectGlobalNames(translUnit, typeNames_, constNames_);
78         source = translUnit.source;
79     }
80 
81     static Context fromString(string source, Options options = Options.init)
82     {
83         import clang.Compiler : Compiler;
84 
85         Compiler compiler;
86 
87         auto translationUnit = TranslationUnit.parseString(
88             Index(false, false),
89             source,
90             compiler.internalFlags,
91             compiler.internalHeaders);
92 
93         return new Context(translationUnit, options);
94     }
95 
96     public string getAnonymousName (Cursor cursor)
97     {
98         if (auto name = cursor in anonymousNames)
99             return *name;
100 
101         return "";
102     }
103 
104     public string generateAnonymousName (Cursor cursor)
105     {
106         import std.format : format;
107         import std.range : empty;
108 
109         auto name = getAnonymousName(cursor);
110 
111         if (name.empty)
112         {
113             name = format("_Anonymous_%d", anonymousNames.length);
114             anonymousNames[cursor] = name;
115         }
116 
117         return name;
118     }
119 
120     public string spelling (Cursor cursor)
121     {
122         auto ptr = cursor in anonymousNames;
123         return ptr !is null ? *ptr : cursor.spelling;
124     }
125 
126     public IncludeHandler includeHandler()
127     {
128         return includeHandler_;
129     }
130 
131     public CommentIndex commentIndex()
132     {
133         return commentIndex_;
134     }
135 
136     public Cursor[string] typeNames()
137     {
138         return typeNames_;
139     }
140 
141     public Cursor[string] constNames()
142     {
143         return constNames_;
144     }
145 
146     public TypedefIndex typedefIndex()
147     {
148         return typedefIndex_;
149     }
150 
151     public HeaderIndex headerIndex()
152     {
153         if (headerIndex_ is null)
154             headerIndex_ = new HeaderIndex(this.translUnit);
155 
156         return headerIndex_;
157     }
158 
159     public bool alreadyDefined(in Cursor cursor)
160     {
161         return (cursor in alreadyDefined_) !is null;
162     }
163 
164     public void markAsDefined(in Cursor cursor)
165     {
166         alreadyDefined_[cursor] = true;
167     }
168 
169     public Cursor typedefParent(in Cursor cursor)
170     {
171         return typedefIndex_.typedefParent(cursor);
172     }
173 
174     public bool isInsideTypedef(in Cursor cursor)
175     {
176         assert(cursor.kind == CXCursorKind.enumDecl
177             || cursor.kind == CXCursorKind.structDecl
178             || cursor.kind == CXCursorKind.unionDecl);
179 
180         if (auto typedef_ = typedefIndex_.typedefParent(cursor))
181         {
182             auto inner = cursor.extent;
183             auto outer = typedef_.extent;
184             return contains(outer, inner);
185         }
186         else
187         {
188             return false;
189         }
190     }
191 
192     private string translateSpellingImpl(in Cursor cursor)
193     {
194         return cursor.spelling == ""
195             ? generateAnonymousName(cursor)
196             : cursor.spelling;
197     }
198 
199     public string translateSpelling(in Cursor cursor)
200     {
201         if (auto spelling = (cursor in translatedSpellings))
202         {
203             return *spelling;
204         }
205         else
206         {
207             auto spelling = translateSpellingImpl(cursor);
208             translatedSpellings[cursor] = spelling;
209             return spelling;
210         }
211     }
212 
213     public void defineSpellingTranslation(in Cursor cursor, string spelling)
214     {
215         translatedSpellings[cursor] = spelling;
216     }
217 
218     private void printCollisionWarning(
219         string spelling,
220         Cursor cursor,
221         Cursor collision)
222     {
223         import std.format : format;
224         import std.stdio : writeln;
225 
226         if (options.printDiagnostics)
227         {
228             auto message = format(
229                 "%s: warning: a type renamed to '%s' due to the " ~
230                 "collision with the symbol declared in %s",
231                 cursor.location.toColonSeparatedString,
232                 spelling,
233                 collision.location.toColonSeparatedString);
234 
235             writeln(message);
236         }
237     }
238 
239     private void throwCollisionError(
240         string spelling,
241         Cursor cursor,
242         Cursor collision) {
243         import std.format : format;
244 
245         throw new TranslationException(
246             format(
247                 "%s: error: a type name '%s' " ~
248                 "collides with the symbol declared in %s",
249                 cursor.location.toColonSeparatedString,
250                 spelling,
251                 collision.location.toColonSeparatedString));
252     }
253 
254     public string translateTagSpelling(Cursor cursor)
255     {
256         if (auto cached = (cursor.canonical in translatedSpellings))
257         {
258             return *cached;
259         }
260 
261         string spelling;
262 
263         if (cursor.spelling == "stat"
264             && headerIndex.searchKnownModules(cursor) == "core.sys.posix.sys.stat")
265         {
266             spelling = "stat_t";
267         }
268         else
269         {
270             auto typedefp = typedefParent(cursor.canonical);
271 
272             if (typedefp.isValid && cursor.spelling == "")
273             {
274                 spelling = typedefp.spelling;
275             }
276             else
277             {
278                 string tentative = translateSpellingImpl(cursor);
279 
280                 spelling = tentative;
281 
282                 if (options.collisionAction != CollisionAction.ignore)
283                 {
284                     auto collision = spelling in macroIndex.globalCursors;
285 
286                     while (collision &&
287                         collision.canonical != cursor.canonical &&
288                         collision.canonical != typedefParent(cursor.canonical))
289                     {
290                         if (options.collisionAction == CollisionAction.abort)
291                             throwCollisionError(spelling, cursor, *collision);
292 
293                         spelling ~= "_";
294                         printCollisionWarning(spelling, cursor, *collision);
295                         collision = spelling in macroIndex.globalCursors;
296                     }
297                 }
298             }
299         }
300 
301         translatedSpellings[cursor.canonical] = spelling;
302         return spelling;
303     }
304 
305     public Translator translator()
306     {
307         return translator_;
308     }
309 
310     public Output globalScope()
311     {
312         return globalScope_;
313     }
314 }
315 
316 string[] cursorScope(Context context, Cursor cursor)
317 {
318     string[] result;
319 
320     void cursorScope(Context context, Cursor cursor, ref string[] result)
321     {
322         string spelling;
323 
324         switch (cursor.kind)
325         {
326             case CXCursorKind.structDecl:
327             case CXCursorKind.unionDecl:
328             case CXCursorKind.enumDecl:
329                 cursorScope(context, cursor.lexicalParent, result);
330                 spelling = context.spelling(cursor);
331                 break;
332 
333             default:
334                 return;
335         }
336 
337         result ~= spelling;
338     }
339 
340     cursorScope(context, cursor, result);
341 
342     return result;
343 }
344 
345 string cursorScopeString(Context context, Cursor cursor)
346 {
347     import std.array : join;
348     return join(cursorScope(context, cursor), ".");
349 }
350 
351 /**
352  * Returns true, if there is a variable, of type represented by cursor, among
353  * children of its parent.
354  */
355 bool variablesInParentScope(Cursor cursor)
356 {
357     import std.algorithm.iteration : filter;
358 
359     auto parent = cursor.semanticParent;
360     auto canonical = cursor.canonical;
361 
362     bool predicate(Cursor a)
363     {
364         return (
365             a.kind == CXCursorKind.fieldDecl ||
366             a.kind == CXCursorKind.varDecl) &&
367             a.type.undecorated.declaration.canonical == canonical;
368     }
369 
370     return !filter!(predicate)(parent.children).empty;
371 }
372 
373 /**
374   * Returns true, if cursor can be translated as anonymous.
375   */
376 bool shouldBeAnonymous(Context context, Cursor cursor)
377 {
378     return cursor.type.isAnonymous &&
379         context.typedefIndex.typedefParent(cursor).isEmpty;
380 }
381 
382 /**
383  * Returns true, if cursor is in the global scope.
384  */
385 bool isGlobal(Cursor cursor)
386 {
387     return cursor.semanticParent.kind == CXCursorKind.translationUnit;
388 }
389 
390 /**
391  * Returns true, if cursor is in the global scope.
392  */
393 bool isGlobalLexically(Cursor cursor)
394 {
395     return cursor.lexicalParent.kind == CXCursorKind.translationUnit;
396 }
397 
398 /**
399  * The collectGlobalTypes function scans the whole AST of the translation unit and produces
400  * a set of the type names in global scope.
401  *
402  * The type names are required for the parsing of C code (e.g. macro definition bodies),
403  * as C grammar isn't context free.
404  */
405 void collectGlobalNames(
406     TranslationUnit translUnit,
407     ref Cursor[string] types,
408     ref Cursor[string] consts)
409 {
410     void collectEnumMembers(
411         Cursor parent,
412         ref Cursor[string] consts)
413     {
414         foreach (cursor; parent.all)
415         {
416             switch (cursor.kind)
417             {
418                 case CXCursorKind.enumConstantDecl:
419                     consts[cursor.spelling] = cursor;
420                     break;
421 
422                 default:
423                     break;
424             }
425         }
426     }
427 
428     void collectGlobalTypes(
429         Cursor parent,
430         ref Cursor[string] types,
431         ref Cursor[string] consts)
432     {
433         foreach (cursor, _; parent.all)
434         {
435             switch (cursor.kind)
436             {
437                 case CXCursorKind.typedefDecl:
438                     types[cursor.spelling] = cursor;
439                     break;
440 
441                 case CXCursorKind.structDecl:
442                     types["struct " ~ cursor.spelling] = cursor;
443                     break;
444 
445                 case CXCursorKind.unionDecl:
446                     types["union " ~ cursor.spelling] = cursor;
447                     break;
448 
449                 case CXCursorKind.enumDecl:
450                     types["enum " ~ cursor.spelling] = cursor;
451                     collectEnumMembers(cursor, consts);
452                     break;
453 
454                 default:
455                     break;
456             }
457         }
458     }
459 
460     collectGlobalTypes(translUnit.cursor, types, consts);
461 }