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