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 }