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 }