1 /** 2 Date: 2015-2016, Joakim Brännström 3 License: MPL-2, Mozilla Public License 2.0 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 Version: Initial created: Jan 30, 2012 7 Copyright (c) 2012 Jacob Carlborg. All rights reserved. 8 9 # Interaction flow 10 Pass1, implicit anonymous struct and unions. 11 Pass2, struct or union decl who has no name. 12 Pass3, anonymous instantiated types. 13 Pass4, generic, last decision point for deriving data from the cursor. 14 PassType, derive type from the cursors type. 15 16 # Location information 17 The "source" location is only, always the definition. 18 Declarations are "using" locations. 19 20 ## CS101 refresher. 21 A definition is the information needed to create an instance of the type. 22 23 A declaration is a subset of the information that makes it possible to use in most scenarios. 24 The most telling example of an useful declaration is a function declaration, "void foo();". 25 Useful most everywhere. 26 But during linking it must be defined _somewhere_ or a linker error will ensue. 27 28 # Future optimization 29 - Skip the primitive types by having them prepoulated in the Container. 30 - Minimize the amoung of data that is propagated by changing TypeResults to 31 ensure only unique USR's exist in it. 32 - Skip pass4+ if the USR already exist in the container. 33 */ 34 module cpptooling.analyzer.clang.type; 35 36 import std.algorithm : among; 37 import std.conv : to; 38 import std..string : format; 39 import std.traits; 40 import std.typecons : Flag, Yes, No, Nullable, Tuple; 41 import logger = std.experimental.logger; 42 43 import clang.c.Index : CXTypeKind, CXCursorKind; 44 import clang.Cursor : Cursor; 45 import clang.Type : Type; 46 47 public import cpptooling.data.kind_type; 48 import cpptooling.analyzer.clang.cursor_logger : logNode; 49 import cpptooling.analyzer.clang.type_logger : logType; 50 import cpptooling.data : SimpleFmt, TypeId, TypeIdLR; 51 import cpptooling.data : Location, LocationTag; 52 import cpptooling.data.symbol : Container, USRType; 53 54 private string nextSequence() @safe { 55 import std.conv : text; 56 import cpptooling.utility.global_unique : nextNumber; 57 58 return text(nextNumber); 59 } 60 61 /// Returns: Filter node to only return those that are a typeref. 62 private auto filterByTypeRef(T)(auto ref T in_) { 63 import std.algorithm : filter; 64 65 return in_.filter!(a => a.isTypeRef); 66 } 67 68 /// 69 private bool isTypeRef(Cursor c) { 70 // CXCursorKind.CXCursor_TypeRef is the first node, thus >=... 71 // it is not in any other way "special". 72 73 return c.kind >= CXCursorKind.typeRef && c.kind <= CXCursorKind.lastRef; 74 } 75 76 /** Iteratively try to construct a USR that is reproducable from the cursor. 77 * 78 * Only use when c.usr may return the empty string. 79 * 80 * Fallback case, using location to make it unique. 81 * 82 * strategy 1 83 * try and derive a location from the lexical parent. 84 * strategy 2 85 * handled by putBacktrackLocation when loc_.kind is null_. 86 * putBacktrackLocation will then use nextSequence to generate a _for sure_ 87 * unique ID. 88 */ 89 private void makeFallbackUSR(Writer)(scope Writer w, ref const(Cursor) c, in uint this_indent) @safe { 90 // strategy 1 91 auto loc_ = backtrackLocation(c); 92 93 // strategy 2 94 putBacktrackLocation(w, c, loc_); 95 } 96 97 /// ditto 98 /// Returns: fallback USR from the cursor. 99 private USRType makeFallbackUSR(ref const(Cursor) c, in uint this_indent) @safe 100 out (result) { 101 import dextool.logger; 102 103 trace(result, this_indent); 104 assert(result.length > 0); 105 } 106 body { 107 import std.array : appender; 108 109 auto app = appender!string(); 110 makeFallbackUSR((const(char)[] s) { app.put(s); }, c, this_indent); 111 112 return USRType(app.data); 113 } 114 115 /// Make a USR, never failing. 116 USRType makeEnsuredUSR(const(Cursor) c, in uint this_indent) @safe 117 out (result) { 118 import dextool.logger; 119 120 trace(result, this_indent); 121 assert(result.length > 0); 122 } 123 body { 124 import std.array : appender; 125 126 auto usr = USRType(c.usr); 127 if (usr.length > 0) { 128 return usr; 129 } 130 131 auto app = appender!string(); 132 makeFallbackUSR((const(char)[] s) { app.put(s); }, c, this_indent); 133 app.put("§"); 134 app.put(nextSequence); 135 136 return USRType(app.data); 137 } 138 139 private void assertTypeResult(const ref TypeResults results) { 140 import std.range : chain, only; 141 142 foreach (const ref result; chain(only(results.primary), results.extra)) { 143 assert(result.type.toStringDecl("x").length > 0); 144 assert(result.type.kind.usr.length > 0); 145 if (result.type.kind.info.kind != TypeKind.Info.Kind.primitive 146 && result.location.kind != LocationTag.Kind.noloc) { 147 assert(result.location.file.length > 0); 148 } 149 } 150 } 151 152 struct BacktrackLocation { 153 static import clang.SourceLocation; 154 import taggedalgebraic : TaggedAlgebraic; 155 import cpptooling.data.type : Location; 156 157 union TagType { 158 typeof(null) null_; 159 cpptooling.data.type.Location loc; 160 } 161 162 alias Tag = TaggedAlgebraic!TagType; 163 164 Tag tag; 165 166 /// Number of nodes backtracked through until a valid was found 167 int backtracked; 168 } 169 170 /** Lexical backtrack from the argument cursor to first cursor with a valid 171 * location. 172 * 173 * using a for loop to ensure it is NOT an infinite loop. 174 * hoping 100 is enough for all type of nesting to reach at least translation 175 * unit. 176 * 177 * Return: Location and nr of backtracks needed. 178 */ 179 private BacktrackLocation backtrackLocation(ref const(Cursor) c) @safe { 180 import cpptooling.data.type : Location; 181 182 BacktrackLocation rval; 183 184 Cursor parent = c; 185 for (rval.backtracked = 0; rval.tag.kind == BacktrackLocation.Tag.Kind.null_ 186 && rval.backtracked < 100; ++rval.backtracked) { 187 auto loc = parent.location; 188 auto spell = loc.spelling; 189 if (spell.file is null) { 190 // do nothing 191 } else if (spell.file.name.length != 0) { 192 rval.tag = Location(spell.file.name, spell.line, spell.column); 193 } else if (parent.isTranslationUnit) { 194 rval.tag = Location(spell.file.name, spell.line, spell.column); 195 break; 196 } 197 198 parent = () @trusted{ return parent.lexicalParent; }(); 199 } 200 201 return rval; 202 } 203 204 /// TODO consider if .offset should be used too. But may make it harder to 205 /// reverse engineer a location. 206 private void putBacktrackLocation(Writer)(scope Writer app, ref const(Cursor) c, 207 BacktrackLocation back_loc) @safe { 208 import std.range.primitives : put; 209 210 static import cpptooling.data.type; 211 212 // using a suffix that do NOT exist in the clang USR standard. 213 // TODO lookup the algorithm for clang USR to see if $ is valid. 214 enum marker = '§'; 215 216 final switch (back_loc.tag.kind) with (BacktrackLocation.Tag) { 217 case Kind.loc: 218 auto loc = cast(cpptooling.data.type.Location) back_loc.tag; 219 app.put(loc.toString); 220 break; 221 case Kind.null_: 222 app.put(nextSequence); 223 break; 224 } 225 226 put(app, marker); 227 put(app, back_loc.backtracked.to!string); 228 if (c.isValid) { 229 put(app, () @trusted{ return c.spelling; }()); 230 } 231 } 232 233 LocationTag makeLocation(ref const(Cursor) c) @safe 234 out (result) { 235 import std.utf : validate; 236 237 validate(result.file); 238 } 239 body { 240 import std.array : appender; 241 242 auto loc = c.location.spelling; 243 auto rval = Location(loc.file.name, loc.line, loc.column); 244 245 if (rval.file.length > 0) { 246 return LocationTag(rval); 247 } 248 249 auto loc_ = backtrackLocation(c); 250 251 if (loc_.tag.kind == BacktrackLocation.Tag.Kind.null_) { 252 return LocationTag(null); 253 } 254 255 auto app = appender!string(); 256 putBacktrackLocation((const(char)[] s) { app.put(s); }, c, loc_); 257 258 rval = Location(app.data, loc.line, loc.column); 259 260 return LocationTag(rval); 261 } 262 263 TypeAttr makeTypeAttr(ref Type type, ref const(Cursor) c) { 264 TypeAttr attr; 265 266 attr.isConst = cast(Flag!"isConst") type.isConst; 267 attr.isRef = cast(Flag!"isRef")(type.kind == CXTypeKind.lValueReference); 268 attr.isPtr = cast(Flag!"isPtr")(type.kind == CXTypeKind.pointer); 269 attr.isArray = cast(Flag!"isArray") type.isArray; 270 attr.isDefinition = cast(Flag!"isDefinition") c.isDefinition; 271 272 return attr; 273 } 274 275 TypeKindAttr makeTypeKindAttr(ref Type type, ref const(Cursor) c) { 276 TypeKindAttr tka; 277 tka.attr = makeTypeAttr(type, c); 278 279 return tka; 280 } 281 282 TypeKindAttr makeTypeKindAttr(ref Type type, ref TypeKind tk, ref const(Cursor) c) { 283 auto tka = makeTypeKindAttr(type, c); 284 tka.kind = tk; 285 286 return tka; 287 } 288 289 /** Deduct the type the node represents. 290 * 291 * pass 1, implicit anonymous structs and unions. 292 * pass 2, implicit types aka no spelling exist for them. 293 * pass 3, instansiated anonymous types and typedef of anonymous. 294 * pass 4, normal nodes, typedefs and references. 295 * passType, collect type information. The final result in most cases. 296 * 297 * TODO add "in" to parameter c. 298 * 299 * Params: 300 * c = cursor to retrieve from. 301 * container = container holding type symbols. 302 * indent = ? 303 */ 304 Nullable!TypeResults retrieveType(ref const(Cursor) c, 305 ref const(Container) container, in uint indent = 0) 306 in { 307 logNode(c, indent); 308 309 // unable to derive anything useful from a typeref when based on nothing else. 310 // __va_list is an examle (found in stdarg.h). 311 if (indent == 0 && isRefNode(c.kind)) { 312 assert(false); 313 } 314 } 315 out (result) { 316 logTypeResult(result, indent); 317 318 // ensure no invalid data is returned 319 if (!result.isNull && indent == 0) { 320 assertTypeResult(result.get); 321 } 322 } 323 body { 324 import std.range; 325 326 Nullable!TypeResults rval; 327 328 // bail early 329 if (c.kind.among(CXCursorKind.macroDefinition)) { 330 return rval; 331 } 332 333 foreach (pass; only(&pass1, &pass2, &pass3)) { 334 auto r = pass(c, indent + 1); 335 if (!r.isNull) { 336 rval = TypeResults(r.get, null); 337 return rval; 338 } 339 } 340 341 rval = pass4(c, container, indent + 1); 342 return rval; 343 } 344 345 /** Pass 1, implicit anonymous types for struct and union. 346 * 347 * TODO merge with pass2. Code duplication 348 */ 349 private Nullable!TypeResult pass1(ref const(Cursor) c, uint indent) 350 in { 351 logNode(c, indent); 352 } 353 body { 354 Nullable!TypeResult rval; 355 356 if (!c.isAnonymous) { 357 return rval; 358 } 359 360 switch (c.kind) with (CXCursorKind) { 361 case structDecl: 362 goto case; 363 case unionDecl: 364 auto type = c.type; 365 rval = TypeResult(); 366 rval.type = makeTypeKindAttr(type, c); 367 368 string spell = type.spelling; 369 rval.type.kind.info = TypeKind.RecordInfo(SimpleFmt(TypeId(spell))); 370 rval.type.kind.usr = USRType(c.usr); 371 rval.location = makeLocation(c); 372 break; 373 default: 374 } 375 376 return rval; 377 } 378 379 /** Pass 2, detect anonymous types who has "no name". 380 * 381 * Only struct, enum, union can possibly have this attribute. 382 * The types name. 383 * 384 * TODO consider using the identifier as the spelling. 385 * 386 * Example: 387 * --- 388 * struct (implicit name) { <-- and spelling is "" 389 * } Struct; 390 * 391 * union (implicit name) { <-- and spelling is "" 392 * } Union; 393 * 394 * typedef enum { 395 * X <--- this one 396 * } Enum; <--- not this one, covered by "other" pass 397 * --- 398 */ 399 private Nullable!TypeResult pass2(ref const(Cursor) c, uint indent) 400 in { 401 logNode(c, indent); 402 } 403 body { 404 Nullable!TypeResult rval; 405 406 if (c.spelling.length != 0) { 407 return rval; 408 } 409 410 switch (c.kind) with (CXCursorKind) { 411 case structDecl: 412 goto case; 413 case unionDecl: 414 auto type = c.type; 415 rval = TypeResult(makeTypeKindAttr(type, c), LocationTag.init); 416 417 rval.type.kind.info = TypeKind.RecordInfo(SimpleFmt(TypeId(nextSequence))); 418 rval.type.kind.usr = USRType(c.usr); 419 rval.location = makeLocation(c); 420 break; 421 case enumDecl: 422 auto type = c.type; 423 rval = TypeResult(makeTypeKindAttr(type, c), LocationTag.init); 424 425 rval.type.kind.info = TypeKind.SimpleInfo(SimpleFmt(TypeId(nextSequence))); 426 rval.type.kind.usr = USRType(c.usr); 427 rval.location = makeLocation(c); 428 break; 429 default: 430 } 431 432 return rval; 433 } 434 435 /** Detect anonymous types that have an instansiation. 436 * 437 * Continuation of Pass 2. 438 * Kept separate from Pass 3 to keep the passes logically "small". 439 * Less cognitive load to understand what the passes do. 440 * 441 * Examle: 442 * --- 443 * struct { 444 * } Struct; 445 * --- 446 */ 447 private Nullable!TypeResult pass3(ref const(Cursor) c, uint indent) 448 in { 449 logNode(c, indent); 450 } 451 body { 452 Nullable!TypeResult rval; 453 454 switch (c.kind) with (CXCursorKind) { 455 case fieldDecl: 456 goto case; 457 case varDecl: 458 import std.range : takeOne; 459 460 foreach (child; c.children.takeOne) { 461 rval = pass2(child, indent + 1); 462 } 463 break; 464 default: 465 } 466 467 return rval; 468 } 469 470 /** 471 */ 472 private Nullable!TypeResults pass4(ref const(Cursor) c, 473 ref const(Container) container, in uint this_indent) 474 in { 475 logNode(c, this_indent); 476 } 477 out (result) { 478 logTypeResult(result, this_indent); 479 } 480 body { 481 auto indent = this_indent + 1; 482 Nullable!TypeResults rval; 483 484 switch (c.kind) with (CXCursorKind) { 485 case typedefDecl: 486 rval = retrieveTypeDef(c, container, indent); 487 break; 488 489 case typeAliasDecl: 490 rval = retrieveTypeAlias(c, container, indent); 491 break; 492 493 case fieldDecl: 494 case varDecl: 495 rval = retrieveInstanceDecl(c, container, indent); 496 break; 497 498 case parmDecl: 499 rval = retrieveParam(c, container, indent); 500 break; 501 502 case templateTypeParameter: 503 rval = retrieveTemplateParam(c, container, indent); 504 break; 505 506 case classTemplatePartialSpecialization: 507 case classTemplate: 508 rval = retrieveClassTemplate(c, container, indent); 509 break; 510 511 case structDecl: 512 case unionDecl: 513 case classDecl: 514 case enumDecl: 515 auto type = c.type; 516 rval = passType(c, type, container, indent); 517 break; 518 519 case cxxMethod: 520 case functionDecl: 521 rval = retrieveFunc(c, container, indent); 522 break; 523 524 case constructor: 525 auto type = c.type; 526 rval = typeToCtor(c, type, container, indent); 527 break; 528 529 case destructor: 530 auto type = c.type; 531 rval = typeToDtor(c, type, indent); 532 break; 533 534 case integerLiteral: 535 auto type = c.type; 536 rval = passType(c, type, container, indent); 537 break; 538 539 case cxxBaseSpecifier: 540 rval = retrieveClassBaseSpecifier(c, container, indent); 541 break; 542 543 case typeRef: 544 case templateRef: 545 case namespaceRef: 546 case memberRef: 547 case labelRef: 548 auto refc = c.referenced; 549 rval = retrieveType(refc, container, indent); 550 break; 551 552 case noDeclFound: 553 // nothing to do 554 break; 555 556 case unexposedDecl: 557 rval = retrieveUnexposed(c, container, indent); 558 if (rval.isNull) { 559 logger.trace("Not implemented type retrieval for node ", c.usr); 560 } 561 break; 562 563 default: 564 // skip for now, may implement in the future 565 logger.trace("Not implemented type retrieval for node ", c.usr); 566 } 567 568 return rval; 569 } 570 571 //TODO add comment, I don't understand what the function is intended to do from 572 // the function name. 573 private bool isUnexposedDeclWithUSR(CXCursorKind kind) { 574 switch (kind) with (CXCursorKind) { 575 case typedefDecl: 576 case templateTypeParameter: 577 case classTemplate: 578 case structDecl: 579 case unionDecl: 580 case classDecl: 581 case enumDecl: 582 return true; 583 default: 584 return false; 585 } 586 } 587 588 private bool canConvertNodeDeclToType(CXCursorKind kind) { 589 switch (kind) with (CXCursorKind) { 590 case typedefDecl: 591 case templateTypeParameter: 592 case classTemplate: 593 case structDecl: 594 case unionDecl: 595 case classDecl: 596 case enumDecl: 597 case cxxMethod: 598 case functionDecl: 599 case constructor: 600 case destructor: 601 case integerLiteral: 602 return true; 603 default: 604 return false; 605 } 606 } 607 608 private bool isRefNode(CXCursorKind kind) { 609 switch (kind) with (CXCursorKind) { 610 case typeRef: 611 case cxxBaseSpecifier: 612 case templateRef: 613 case namespaceRef: 614 case memberRef: 615 case labelRef: 616 return true; 617 default: 618 return false; 619 } 620 } 621 622 private Nullable!TypeResults retrieveUnexposed(ref const(Cursor) c, 623 ref const(Container) container, in uint this_indent) 624 in { 625 logNode(c, this_indent); 626 assert(c.kind == CXCursorKind.unexposedDecl); 627 } 628 out (result) { 629 logTypeResult(result, this_indent); 630 } 631 body { 632 import std.range : takeOne; 633 634 auto indent = this_indent + 1; 635 Nullable!TypeResults rval; 636 637 foreach (child; c.children.takeOne) { 638 switch (child.kind) with (CXCursorKind) { 639 case cxxMethod: 640 case functionDecl: 641 rval = pass4(child, container, indent); 642 if (!rval.isNull && rval.primary.type.kind.info.kind != TypeKind.Info.Kind.func) { 643 // cases like typeof(x) y; 644 // fix in the future 645 rval.nullify; 646 } 647 break; 648 649 default: 650 } 651 } 652 653 return rval; 654 } 655 656 private Nullable!TypeResults passType(ref const(Cursor) c, ref Type type, 657 ref const(Container) container, in uint this_indent) 658 in { 659 logNode(c, this_indent); 660 logType(type, this_indent); 661 662 //TODO investigate if the below assumption is as it should be. 663 // Not suposed to be handled here. 664 // A typedef cursor shall have been detected and then handled by inspecting the child. 665 // MAYBE move primitive type detection here. 666 //assert(type.kind != CXTypeKind.CXType_Typedef); 667 } 668 out (result) { 669 logTypeResult(result, this_indent); 670 } 671 body { 672 import std.range : takeOne; 673 674 auto indent = 1 + this_indent; 675 Nullable!TypeResults rval; 676 677 switch (type.kind) with (CXTypeKind) { 678 case functionNoProto: 679 case functionProto: 680 rval = typeToFuncProto(c, type, container, indent); 681 break; 682 683 case blockPointer: 684 rval = typeToFuncPtr(c, type, container, indent); 685 break; 686 687 // handle ref and ptr the same way 688 case lValueReference: 689 case pointer: 690 //TODO fix architecture so this check isn't needed. 691 //Should be possible to merge typeToFunPtr and typeToPointer 692 if (type.isFunctionPointerType) { 693 rval = typeToFuncPtr(c, type, container, indent); 694 } else { 695 rval = typeToPointer(c, type, container, indent); 696 } 697 break; 698 699 case constantArray: 700 case incompleteArray: 701 rval = typeToArray(c, type, container, indent); 702 break; 703 704 case record: 705 rval = typeToRecord(c, type, indent); 706 break; 707 708 case typedef_: 709 // unable to represent a typedef as a typedef. 710 // Falling back on representing as a Simple. 711 // Note: the usr from the cursor is null. 712 rval = typeToFallBackTypeDef(c, type, indent); 713 break; 714 715 case unexposed: 716 debug { 717 logger.trace("Unexposed, investigate if any other action should be taken"); 718 } 719 if (!c.kind.among(CXCursorKind.functionDecl, CXCursorKind.cxxMethod)) { 720 // see retrieveUnexposed for why 721 rval = typeToSimple(c, type, indent); 722 } else if (type.isFunctionType) { 723 rval = typeToFuncProto(c, type, container, indent); 724 } 725 break; 726 727 default: 728 rval = typeToSimple(c, type, indent); 729 } 730 731 return rval; 732 } 733 734 /** Create a representation of a typeRef for the cursor. 735 */ 736 private TypeResults typeToTypeRef(ref const(Cursor) c, ref Type type, 737 USRType type_ref, USRType canonical_ref, in uint this_indent) 738 in { 739 logNode(c, this_indent); 740 logType(type, this_indent); 741 } 742 out (result) { 743 logTypeResult(result, this_indent); 744 } 745 body { 746 const uint indent = this_indent + 1; 747 string spell = type.spelling; 748 749 // ugly hack 750 if (type.isConst && spell.length > 6 && spell[0 .. 6] == "const ") { 751 spell = spell[6 .. $]; 752 } 753 754 TypeKind.TypeRefInfo info; 755 info.fmt = SimpleFmt(TypeId(spell)); 756 info.typeRef = type_ref; 757 info.canonicalRef = canonical_ref; 758 759 TypeResults rval; 760 rval.primary.type.attr = makeTypeAttr(type, c); 761 rval.primary.type.kind.info = info; 762 763 // a typedef like __va_list has a null usr 764 if (c.usr.length == 0) { 765 rval.primary.type.kind.usr = makeFallbackUSR(c, indent); 766 } else { 767 rval.primary.type.kind.usr = c.usr; 768 } 769 770 rval.primary.location = makeLocation(c); 771 772 return rval; 773 } 774 775 /** Use fallback strategy for typedef's via Simple. 776 * 777 * A typedef referencing a type that it isn't possible to derive information 778 * from to correctly represent (pointer, record, primitive etc). 779 * 780 * The fall back strategy is in that case to represent the type textually as a Simple. 781 * The TypeKind->typeRef then references this simple type. 782 */ 783 private TypeResults typeToFallBackTypeDef(ref const(Cursor) c, ref Type type, in uint this_indent) 784 in { 785 logNode(c, this_indent); 786 logType(type, this_indent); 787 } 788 out (result) { 789 logTypeResult(result, this_indent); 790 } 791 body { 792 string spell = type.spelling; 793 794 // ugly hack to remove const 795 if (type.isConst && spell.length > 6 && spell[0 .. 6] == "const ") { 796 spell = spell[6 .. $]; 797 } 798 799 auto rval = makeTypeKindAttr(type, c); 800 801 auto info = TypeKind.SimpleInfo(SimpleFmt(TypeId(spell))); 802 rval.kind.info = info; 803 804 // a typedef like __va_list has a null usr 805 if (c.usr.length == 0) { 806 rval.kind.usr = makeFallbackUSR(c, this_indent + 1); 807 } else { 808 rval.kind.usr = c.usr; 809 } 810 811 auto loc = makeLocation(c); 812 813 return TypeResults(TypeResult(rval, loc), null); 814 } 815 816 private TypeResults typeToSimple(ref const(Cursor) c, ref Type type, in uint this_indent) 817 in { 818 logNode(c, this_indent); 819 logType(type, this_indent); 820 } 821 out (result) { 822 logTypeResult(result, this_indent); 823 } 824 body { 825 auto rval = makeTypeKindAttr(type, c); 826 LocationTag loc; 827 828 auto maybe_primitive = translateCursorType(type.kind); 829 830 if (maybe_primitive.isNull) { 831 string spell = type.spelling; 832 rval.kind.info = TypeKind.SimpleInfo(SimpleFmt(TypeId(spell))); 833 834 rval.kind.usr = c.usr; 835 if (rval.kind.usr.length == 0) { 836 rval.kind.usr = makeFallbackUSR(c, this_indent + 1); 837 } 838 loc = makeLocation(c); 839 } else { 840 string spell = maybe_primitive.get; 841 rval.kind.info = TypeKind.PrimitiveInfo(SimpleFmt(TypeId(spell))); 842 843 rval.kind.usr = USRType(maybe_primitive.get); 844 loc = LocationTag(null); 845 } 846 847 return TypeResults(TypeResult(rval, loc), null); 848 } 849 850 /// A function proto signature? 851 /// Workaround by checking if the return type is valid. 852 private bool isFuncProtoTypedef(ref const(Cursor) c) { 853 auto result_t = c.type.func.resultType; 854 return result_t.isValid; 855 } 856 857 private TypeResults typeToTypedef(ref const(Cursor) c, ref Type type, USRType typeRef, 858 USRType canonicalRef, ref const(Container) container, in uint this_indent) 859 in { 860 logNode(c, this_indent); 861 logType(type, this_indent); 862 assert(type.kind == CXTypeKind.typedef_); 863 } 864 out (result) { 865 logTypeResult(result, this_indent); 866 } 867 body { 868 /// Make a string that represent the type. 869 static string makeSpelling(ref const(Cursor) c, ref Type type) { 870 import std.array : array; 871 import std.algorithm : canFind, map, joiner; 872 import std.range : retro, chain, only; 873 import std.utf : byChar; 874 875 string spell = type.spelling; 876 877 if (type.isConst && spell.length > 6 && spell[0 .. 6] == "const ") { 878 spell = spell[6 .. $]; 879 } 880 881 if (!spell.canFind("::")) { 882 // if it isn't contained in a namespace then perform a backtracking of 883 // the scope to ensure it isn't needed. Implicit struct or enums need 884 // this check. 885 // Example: typedef struct {} Struct; 886 887 import cpptooling.analyzer.clang.cursor_backtrack : backtrackScopeRange; 888 889 // dfmt off 890 spell = cast(string) chain(only(spell), backtrackScopeRange(c).map!(a => a.spelling)) 891 .array() 892 .retro 893 .joiner("::") 894 .byChar 895 .array(); 896 // dfmt on 897 } 898 899 return spell; 900 } 901 902 auto spell = makeSpelling(c, type); 903 904 TypeKind.TypeRefInfo info; 905 info.fmt = SimpleFmt(TypeId(spell)); 906 info.typeRef = typeRef; 907 info.canonicalRef = canonicalRef; 908 909 TypeResults rval; 910 rval.primary.type.attr = makeTypeAttr(type, c); 911 rval.primary.type.kind.info = info; 912 913 // a typedef like __va_list has a null usr 914 if (c.usr.length == 0) { 915 rval.primary.type.kind.usr = makeFallbackUSR(c, this_indent + 1); 916 } else { 917 rval.primary.type.kind.usr = c.usr; 918 } 919 920 rval.primary.location = makeLocation(c); 921 922 return rval; 923 } 924 925 /** Make a Record from a declaration or definition. 926 */ 927 private TypeResults typeToRecord(ref const(Cursor) c, ref Type type, in uint indent) 928 in { 929 logNode(c, indent); 930 logType(type, indent); 931 assert(type.kind == CXTypeKind.record); 932 } 933 out (result) { 934 logTypeResult(result, indent); 935 } 936 body { 937 string spell = type.spelling; 938 939 // ugly hack needed when canonicalType has been used to get the type of a 940 // cursor 941 if (type.isConst && spell.length > 6 && spell[0 .. 6] == "const ") { 942 spell = spell[6 .. $]; 943 } 944 945 TypeKind.RecordInfo info; 946 info.fmt = SimpleFmt(TypeId(spell)); 947 948 auto rval = makeTypeKindAttr(type, c); 949 rval.kind.info = info; 950 rval.kind.usr = c.usr; 951 auto loc = makeLocation(c); 952 953 if (rval.kind.usr.length == 0) { 954 rval.kind.usr = makeFallbackUSR(c, indent + 1); 955 } 956 957 return TypeResults(TypeResult(rval, loc), null); 958 } 959 960 /** Represent a pointer type hierarchy. 961 * 962 * Returns: TypeResults.primary.attr is the pointed at attribute. 963 */ 964 private TypeResults typeToPointer(ref const(Cursor) c, ref Type type, 965 ref const(Container) container, const uint this_indent) 966 in { 967 logNode(c, this_indent); 968 logType(type, this_indent); 969 assert(type.kind.among(CXTypeKind.pointer, CXTypeKind.lValueReference)); 970 } 971 out (result) { 972 logTypeResult(result, this_indent); 973 with (TypeKind.Info.Kind) { 974 assert(result.primary.type.kind.info.kind.among(funcPtr, pointer)); 975 } 976 } 977 body { 978 import cpptooling.data : PtrFmt, Left, Right; 979 980 immutable indent = this_indent + 1; 981 982 static auto getPointee(ref const(Cursor) c, ref Type type, 983 ref const(Container) container, in uint indent) { 984 auto pointee = type.pointeeType; 985 auto c_pointee = pointee.declaration; 986 987 debug { 988 logNode(c_pointee, indent); 989 logType(pointee, indent); 990 } 991 992 TypeResults rval; 993 994 // find the underlying type information 995 if (c_pointee.kind == CXCursorKind.typedefDecl) { 996 rval = retrieveType(c_pointee, container, indent).get; 997 } else if (pointee.kind == CXTypeKind.unexposed) { 998 pointee = type.canonicalType; 999 while (pointee.kind.among(CXTypeKind.pointer, CXTypeKind.lValueReference)) { 1000 pointee = pointee.pointeeType; 1001 } 1002 rval = passType(c, pointee, container, indent).get; 1003 1004 if (rval.primary.type.kind.info.kind == TypeKind.Info.Kind.record 1005 && c_pointee.kind.isUnexposedDeclWithUSR) { 1006 // if the current pointers type is for a declaration use this 1007 // usr instead of the one from pointee. 1008 // Solves the case of a forward declared class in a namespace. 1009 // The retrieved data is only correct if it is from the 1010 // canonical type but the USR is wrong. 1011 string usr = c_pointee.usr; 1012 rval.primary.type.kind.usr = usr; 1013 1014 // TODO investigate if a usr null checking is needed. 1015 // I think it is unnecessary but unsure at this point. 1016 // It is possible to run a full scan of google mock and all 1017 // internal tests without this check. 1018 // If this hasn't been changed for 6 month remove this comment. 1019 // Written at 2016-07-01, remove by 2017-02-01. 1020 } 1021 } else if (c_pointee.kind == CXCursorKind.noDeclFound) { 1022 while (pointee.kind.among(CXTypeKind.pointer, CXTypeKind.lValueReference)) { 1023 pointee = pointee.pointeeType; 1024 } 1025 1026 auto c_decl = pointee.declaration; 1027 1028 if (c_decl.kind == CXCursorKind.noDeclFound) { 1029 // primitive types do not have a declaration cursor. 1030 // find the underlying primitive type. 1031 rval = passType(c, pointee, container, indent).get; 1032 } else { 1033 rval = retrieveType(c_decl, container, indent).get; 1034 } 1035 } else { 1036 rval = retrieveType(c_pointee, container, indent).get; 1037 } 1038 1039 return rval; 1040 } 1041 1042 auto pointee = getPointee(c, type, container, indent); 1043 1044 auto attrs = retrievePointeeAttr(type, indent); 1045 1046 TypeKind.PointerInfo info; 1047 info.pointee = pointee.primary.type.kind.usr; 1048 info.attrs = attrs.ptrs; 1049 1050 switch (pointee.primary.type.kind.info.kind) with (TypeKind.Info) { 1051 case Kind.array: 1052 auto type_id = pointee.primary.type.kind.splitTypeId(indent); 1053 info.fmt = PtrFmt(TypeIdLR(Left(type_id.left ~ "("), Right(")" ~ type_id.right))); 1054 break; 1055 default: 1056 info.fmt = PtrFmt(pointee.primary.type.kind.splitTypeId(indent)); 1057 } 1058 1059 TypeResults rval; 1060 rval.primary.type.kind.info = info; 1061 // somehow pointee.primary.attr is wrong, somehow. Don't undestand why. 1062 // TODO remove this hack 1063 rval.primary.type.attr = attrs.base; 1064 // a pointer is always itselfs definition because they are always unique 1065 rval.primary.type.attr.isDefinition = Yes.isDefinition; 1066 1067 // must be unique even when analyzing many translation units. 1068 // Could maybe work if static/anonymous namespace influenced the USR. 1069 rval.primary.type.kind.usr = makeFallbackUSR(c, indent); 1070 rval.primary.location = makeLocation(c); 1071 1072 rval.extra = [pointee.primary] ~ pointee.extra; 1073 1074 return rval; 1075 } 1076 1077 /** Represent a function pointer type. 1078 * 1079 * Return: correct formatting and attributes for a function pointer. 1080 */ 1081 private TypeResults typeToFuncPtr(ref const(Cursor) c, ref Type type, 1082 ref const(Container) container, const uint this_indent) 1083 in { 1084 logNode(c, this_indent); 1085 logType(type, this_indent); 1086 assert(type.kind.among(CXTypeKind.pointer, CXTypeKind.lValueReference)); 1087 assert(type.isFunctionPointerType); 1088 } 1089 out (result) { 1090 logTypeResult(result, this_indent); 1091 with (TypeKind.Info.Kind) { 1092 // allow catching the logical error in debug build 1093 assert(!result.primary.type.kind.info.kind.among(ctor, dtor, record, simple, array)); 1094 } 1095 } 1096 body { 1097 import cpptooling.data : FuncPtrFmt, Left, Right; 1098 1099 immutable indent = this_indent + 1; 1100 1101 // find the underlying function prototype 1102 auto pointee_type = type; 1103 while (pointee_type.kind.among(CXTypeKind.pointer, CXTypeKind.lValueReference)) { 1104 pointee_type = pointee_type.pointeeType; 1105 } 1106 debug { 1107 logType(pointee_type, indent); 1108 } 1109 1110 auto attrs = retrievePointeeAttr(type, indent); 1111 auto pointee = typeToFuncProto(c, pointee_type, container, indent + 1); 1112 1113 TypeKind.FuncPtrInfo info; 1114 info.pointee = pointee.primary.type.kind.usr; 1115 info.attrs = attrs.ptrs; 1116 info.fmt = () { 1117 auto tid = pointee.primary.type.kind.splitTypeId(indent); 1118 return FuncPtrFmt(TypeIdLR(Left(tid.left ~ "("), Right(")" ~ tid.right))); 1119 }(); 1120 1121 TypeResults rval; 1122 rval.primary.type.kind.info = info; 1123 rval.primary.type.kind.usr = makeFallbackUSR(c, indent); 1124 rval.primary.location = makeLocation(c); 1125 // somehow pointee.primary.attr is wrong, somehow. Don't undestand why. 1126 // TODO remove this hack 1127 rval.primary.type.attr = attrs.base; 1128 1129 rval.extra = [pointee.primary] ~ pointee.extra; 1130 1131 return rval; 1132 } 1133 1134 private TypeResults typeToFuncProto(InfoT = TypeKind.FuncInfo)(ref const(Cursor) c, 1135 ref Type type, ref const(Container) container, in uint this_indent) 1136 if (is(InfoT == TypeKind.FuncInfo) || is(InfoT == TypeKind.FuncSignatureInfo)) 1137 in { 1138 logNode(c, this_indent); 1139 logType(type, this_indent); 1140 assert(type.isFunctionType || type.isTypedef || type.kind == CXTypeKind.functionNoProto); 1141 } 1142 out (result) { 1143 logTypeResult(result, this_indent); 1144 } 1145 body { 1146 import std.array : array; 1147 import std.algorithm : map; 1148 import std..string : strip; 1149 import cpptooling.data : FuncFmt, Left, Right, FuncSignatureFmt; 1150 1151 const auto indent = this_indent + 1; 1152 1153 // TODO redesign. This is brittle and ugly. 1154 // return by value instead of splitting two ways like this. 1155 TypeKindAttr retrieveReturn(ref TypeResults rval) { 1156 auto result_type = type.func.resultType; 1157 auto result_decl = result_type.declaration; 1158 debug { 1159 logNode(result_decl, indent); 1160 logType(result_type, indent); 1161 } 1162 1163 auto this_node = passType(result_decl, result_type, container, indent + 1).get; 1164 1165 if (result_decl.kind == CXCursorKind.noDeclFound) { 1166 rval = this_node; 1167 } else { 1168 rval = retrieveType(result_decl, container, indent + 1).get; 1169 1170 // use the attributes derived from this node because it is not 1171 // preserved in the result_decl. This is a problem when the return 1172 // type is a typedef. The const attribute isn't preserved. 1173 rval.primary.type.attr = this_node.primary.type.attr; 1174 1175 rval.extra ~= this_node.primary; 1176 rval.extra ~= this_node.extra; 1177 } 1178 1179 return rval.primary.type; 1180 } 1181 1182 TypeResults rval; 1183 TypeResults return_rval; 1184 1185 auto return_t = retrieveReturn(return_rval); 1186 auto params = extractParams(c, type, container, indent); 1187 TypeResult primary; 1188 primary.type = makeTypeKindAttr(type, c); 1189 1190 // a C++ member function must be queried for constness via a different API 1191 primary.type.attr.isConst = cast(Flag!"isConst") c.func.isConst; 1192 1193 InfoT info; 1194 static if (is(InfoT == TypeKind.FuncInfo)) { 1195 info.fmt = FuncFmt(TypeIdLR(Left(return_t.toStringDecl.strip), 1196 Right("(" ~ params.params.joinParamId ~ ")"))); 1197 } else { 1198 info.fmt = FuncSignatureFmt(TypeIdLR(Left(return_t.toStringDecl.strip), 1199 Right("(" ~ params.params.joinParamId ~ ")"))); 1200 } 1201 info.return_ = return_t.kind.usr; 1202 info.returnAttr = return_t.attr; 1203 info.params = params.params.map!(a => FuncInfoParam(a.result.type.kind.usr, 1204 a.result.type.attr, a.id, a.isVariadic)).array(); 1205 1206 primary.type.kind.info = info; 1207 primary.location = makeLocation(c); 1208 1209 primary.type.kind.usr = c.usr; 1210 if (primary.type.kind.usr.length == 0) { 1211 primary.type.kind.usr = makeFallbackUSR(c, indent); 1212 } else if (c.kind.among(CXCursorKind.varDecl, CXCursorKind.fieldDecl, 1213 CXCursorKind.templateTypeParameter, CXCursorKind.parmDecl)) { 1214 // TODO consider how the knowledge of the field could be "moved" out of 1215 // this function. 1216 // Instances must result in a unique USR. Otherwise it is impossible to 1217 // differentiate between the type and field. 1218 primary.type.kind.usr = makeFallbackUSR(c, indent); 1219 } 1220 1221 rval.primary = primary; 1222 rval.extra ~= params.params.map!(a => a.result).array() ~ params.extra; 1223 rval.extra ~= return_rval.primary; 1224 rval.extra ~= return_rval.extra; 1225 1226 return rval; 1227 } 1228 1229 private TypeResults typeToCtor(ref const(Cursor) c, ref Type type, 1230 ref const(Container) container, in uint indent) 1231 in { 1232 logNode(c, indent); 1233 logType(type, indent); 1234 assert(c.kind == CXCursorKind.constructor); 1235 } 1236 out (result) { 1237 logTypeResult(result, indent); 1238 } 1239 body { 1240 import std.algorithm : map; 1241 import std.array : array; 1242 import cpptooling.data : CtorFmt; 1243 1244 TypeResults rval; 1245 auto params = extractParams(c, type, container, indent); 1246 TypeResult primary; 1247 primary.type = makeTypeKindAttr(type, c); 1248 1249 TypeKind.CtorInfo info; 1250 info.fmt = CtorFmt(TypeId(format("(%s)", params.params.joinParamId()))); 1251 info.params = params.params.map!(a => FuncInfoParam(a.result.type.kind.usr, 1252 a.result.type.attr, a.id, a.isVariadic)).array(); 1253 info.id = c.spelling; 1254 1255 primary.type.kind.info = info; 1256 primary.type.kind.usr = c.usr; 1257 primary.location = makeLocation(c); 1258 1259 rval.primary = primary; 1260 rval.extra ~= params.params.map!(a => a.result).array() ~ params.extra; 1261 1262 return rval; 1263 } 1264 1265 private TypeResults typeToDtor(ref const(Cursor) c, ref Type type, in uint indent) 1266 in { 1267 logNode(c, indent); 1268 logType(type, indent); 1269 assert(c.kind == CXCursorKind.destructor); 1270 } 1271 out (result) { 1272 logTypeResult(result, indent); 1273 } 1274 body { 1275 TypeResults rval; 1276 auto primary = makeTypeKindAttr(type, c); 1277 1278 TypeKind.DtorInfo info; 1279 info.id = c.spelling[1 .. $]; // remove the leading ~ 1280 1281 primary.kind.info = info; 1282 primary.kind.usr = c.usr; 1283 1284 rval.primary.location = makeLocation(c); 1285 rval.primary.type = primary; 1286 1287 return rval; 1288 } 1289 1290 //TODO change the array to an appender, less GC pressure 1291 private alias PointerTypeAttr = Tuple!(TypeAttr[], "ptrs", TypeAttr, "base"); 1292 1293 /** Retrieve the attributes of the pointers until base condition. 1294 * 1295 * [$] is the value pointed at. 1296 * 1297 * Params: 1298 * underlying = the value type, injected at correct position. 1299 * type = a pointer or reference type. 1300 * indent = indent for the log strings. 1301 * Return: An array of attributes for the pointers. 1302 */ 1303 private PointerTypeAttr retrievePointeeAttr(ref Type type, in uint this_indent) 1304 in { 1305 logType(type, this_indent); 1306 } 1307 out (result) { 1308 import std.range : chain, only; 1309 1310 foreach (r; chain(only(result.base), result.ptrs)) { 1311 logTypeAttr(r, this_indent); 1312 } 1313 } 1314 body { 1315 auto indent = this_indent + 1; 1316 PointerTypeAttr rval; 1317 auto decl_c = type.declaration; 1318 1319 if (type.kind.among(CXTypeKind.pointer, CXTypeKind.lValueReference)) { 1320 // recursive 1321 auto pointee = type.pointeeType; 1322 rval = retrievePointeeAttr(pointee, indent); 1323 // current appended so right most ptr is at position 0. 1324 rval.ptrs ~= makeTypeAttr(type, decl_c); 1325 } else { 1326 // Base condition. 1327 rval.base = makeTypeAttr(type, decl_c); 1328 } 1329 1330 return rval; 1331 } 1332 1333 /// TODO this function is horrible. Refactor 1334 private TypeResults typeToArray(ref const(Cursor) c, ref Type type, 1335 ref const(Container) container, const uint this_indent) 1336 in { 1337 logNode(c, this_indent); 1338 logType(type, this_indent); 1339 } 1340 out (result) { 1341 logTypeResult(result, this_indent); 1342 assert(result.primary.type.kind.info.kind == TypeKind.Info.Kind.array); 1343 } 1344 body { 1345 import std.format : format; 1346 import cpptooling.data : ArrayFmt, LocationTag, Location; 1347 1348 immutable indent = this_indent + 1; 1349 1350 static void gatherIndexesToElement(Type start, ref ArrayInfoIndex[] indexes, ref Type element) { 1351 Type curr = start; 1352 1353 while (curr.kind.among(CXTypeKind.constantArray, CXTypeKind.incompleteArray)) { 1354 auto arr = curr.array; 1355 1356 switch (curr.kind) with (CXTypeKind) { 1357 case constantArray: 1358 indexes ~= ArrayInfoIndex(arr.size); 1359 break; 1360 case incompleteArray: 1361 indexes ~= ArrayInfoIndex(); 1362 break; 1363 default: 1364 break; 1365 } 1366 1367 curr = arr.elementType; 1368 } 1369 1370 element = curr; 1371 } 1372 1373 static void determineElement(Type ele_type, ref const(ArrayInfoIndex[]) indexes, 1374 ref const(Cursor) c, ref const(Container) container, ref USRType primary_usr, 1375 ref LocationTag primary_loc, ref TypeResults element, const uint indent) { 1376 auto index_decl = ele_type.declaration; 1377 1378 if (index_decl.kind == CXCursorKind.noDeclFound) { 1379 // on purpuse not checking if it is null before using 1380 element = passType(c, ele_type, container, indent).get; 1381 1382 if (element.primary.type.kind.usr.length != 0) { 1383 primary_usr = element.primary.type.kind.usr; 1384 } else { 1385 primary_usr = makeFallbackUSR(c, indent); 1386 } 1387 primary_loc = element.primary.location; 1388 } else { 1389 // on purpuse not checking if it is null before using 1390 element = retrieveType(index_decl, container, indent).get; 1391 1392 primary_usr = element.primary.type.kind.usr; 1393 primary_loc = element.primary.location; 1394 } 1395 // let the indexing affect the USR as to not collide with none-arrays of 1396 // the same type. 1397 primary_usr = primary_usr ~ indexes.toRepr; 1398 1399 switch (primary_loc.kind) { 1400 case LocationTag.Kind.noloc: 1401 // TODO this is stupid ... fix it. Shouldn't be needed but happens 1402 // when it is an array of primary types. 1403 // Probably the correct fix is the contract in retrieveType to check 1404 // that if it is an array at primary types it do NOT check for length. 1405 primary_loc = makeLocation(c); 1406 break; 1407 default: 1408 } 1409 } 1410 1411 // step 1, find indexing and element type 1412 ArrayInfoIndex[] index_nr; 1413 Type element_type = type; 1414 1415 gatherIndexesToElement(type, index_nr, element_type); 1416 1417 // step 2, determine element 1418 TypeResults element; 1419 USRType primary_usr; 1420 LocationTag primary_loc; 1421 1422 determineElement(element_type, index_nr, c, container, primary_usr, 1423 primary_loc, element, indent); 1424 1425 // step 3, put together the result 1426 1427 TypeKind.ArrayInfo info; 1428 info.element = element.primary.type.kind.usr; 1429 info.indexes = index_nr; 1430 info.fmt = ArrayFmt(element.primary.type.kind.splitTypeId(indent)); 1431 1432 TypeResults rval; 1433 1434 if (!element.primary.type.kind.info.kind.among(TypeKind.Info.Kind.pointer, 1435 TypeKind.Info.Kind.funcPtr)) { 1436 auto elem_t = type.array.elementType; 1437 auto decl_c = elem_t.declaration; 1438 rval.primary.type.attr = makeTypeAttr(elem_t, decl_c); 1439 } else { 1440 rval.primary.type.attr = element.primary.type.attr; 1441 } 1442 1443 rval.primary.type.kind.usr = primary_usr; 1444 rval.primary.location = primary_loc; 1445 rval.primary.type.kind.info = info; 1446 rval.extra ~= [element.primary] ~ element.extra; 1447 1448 return rval; 1449 } 1450 1451 /** Retrieve the type of an instance declaration. 1452 * 1453 * Questions to consider: 1454 * - Is the type a typeref? 1455 * - Is it a function pointer? 1456 * - Is the type a primitive type? 1457 */ 1458 private Nullable!TypeResults retrieveInstanceDecl(ref const(Cursor) c, 1459 ref const(Container) container, in uint this_indent) 1460 in { 1461 logNode(c, this_indent); 1462 with (CXCursorKind) { 1463 assert(c.kind.among(varDecl, fieldDecl, templateTypeParameter, parmDecl)); 1464 } 1465 } 1466 out (result) { 1467 logTypeResult(result, this_indent); 1468 } 1469 body { 1470 import std.range : takeOne; 1471 1472 const auto indent = this_indent + 1; 1473 auto c_type = c.type; 1474 1475 auto handlePointer(ref Nullable!TypeResults rval) { 1476 switch (c_type.kind) with (CXTypeKind) { 1477 // Is it a pointer? 1478 // Then preserve the pointer structure but dig deeper for the 1479 // pointed at type. 1480 case lValueReference: 1481 case pointer: 1482 // must retrieve attributes from the pointed at type thus need a 1483 // more convulated deduction 1484 rval = passType(c, c_type, container, indent); 1485 foreach (tref; c.children.takeOne) { 1486 auto child = retrieveType(tref, container, indent); 1487 if (!child.isNull) { 1488 rval.extra ~= [child.primary] ~ child.extra; 1489 } 1490 } 1491 break; 1492 1493 default: 1494 } 1495 } 1496 1497 auto handleTypedef(ref Nullable!TypeResults rval) { 1498 import std.algorithm : until; 1499 import cpptooling.analyzer.clang.cursor_visitor : visitBreathFirst; 1500 1501 // example of tree analyzed: 1502 // VarDecl -> TypedefDecl 1503 // VarDecl -> TypeRef -> TypedefDecl 1504 foreach (child; c.visitBreathFirst.until!(a => a.depth == 3)) { 1505 if (child.kind == CXCursorKind.typeRef) { 1506 rval = retrieveType(child, container, indent); 1507 break; 1508 } else if (child.kind == CXCursorKind.typedefDecl) { 1509 rval = retrieveType(child, container, indent); 1510 break; 1511 } 1512 } 1513 1514 if (!rval.isNull) { 1515 // depend on the underlying cursor 1516 auto old_def = rval.primary.type.attr.isDefinition; 1517 1518 rval.primary.type.attr = makeTypeAttr(c_type, c); 1519 1520 rval.primary.type.attr.isDefinition = old_def; 1521 } 1522 } 1523 1524 auto handleTypeWithDecl(ref Nullable!TypeResults rval) { 1525 auto c_type_decl = c_type.declaration; 1526 if (c_type_decl.isValid) { 1527 auto type = c_type_decl.type; 1528 rval = passType(c_type_decl, type, container, indent); 1529 } 1530 } 1531 1532 auto handleArray(ref Nullable!TypeResults rval) { 1533 // must check for array:nes before Typedef because of the case when it 1534 // is an array of typedef's 1535 if (c_type.isArray) { 1536 rval = typeToArray(c, c_type, container, indent); 1537 } 1538 } 1539 1540 auto fallback(ref Nullable!TypeResults rval) { 1541 rval = passType(c, c_type, container, indent); 1542 } 1543 1544 auto ensureUSR(ref Nullable!TypeResults rval) { 1545 if (!rval.isNull && rval.primary.type.kind.usr.length == 0) { 1546 rval.primary.type.kind.usr = makeFallbackUSR(c, this_indent); 1547 } 1548 } 1549 1550 Nullable!TypeResults rval; 1551 foreach (idx, f; [&handlePointer, &handleArray, &handleTypedef, 1552 &handleTypeWithDecl, &fallback]) { 1553 debug { 1554 import std.conv : to; 1555 import dextool.logger : trace; 1556 1557 trace(idx.to!string(), this_indent); 1558 } 1559 1560 f(rval); 1561 if (!rval.isNull) { 1562 break; 1563 } 1564 } 1565 1566 ensureUSR(rval); 1567 1568 return rval; 1569 } 1570 1571 private Nullable!TypeResults retrieveTypeAlias(ref const(Cursor) c, 1572 ref const(Container) container, in uint this_indent) 1573 in { 1574 logNode(c, this_indent); 1575 assert(c.kind == CXCursorKind.typeAliasDecl); 1576 } 1577 out (result) { 1578 logTypeResult(result, this_indent); 1579 } 1580 body { 1581 const uint indent = this_indent + 1; 1582 1583 Nullable!TypeResults rval; 1584 1585 foreach (child; c.children) { 1586 if (child.kind != CXCursorKind.typeRef) { 1587 continue; 1588 } 1589 1590 auto tref = pass4(child, container, indent); 1591 1592 auto type = c.type; 1593 // duplicated code from retrieveTypeDef -> handleTyperef 1594 // TODO consider if this can be harmonized with Typedef. 1595 // Maybe this is a special case? 1596 // Shouldn't be per se locked to a TypeDefDecl but rather the concept 1597 // of a type that is an alias for another. 1598 if (tref.primary.type.kind.info.kind == TypeKind.Info.Kind.typeRef) { 1599 rval = typeToTypedef(c, type, tref.primary.type.kind.usr, 1600 tref.primary.type.kind.info.canonicalRef, container, indent); 1601 } else { 1602 rval = typeToTypedef(c, type, tref.primary.type.kind.usr, 1603 tref.primary.type.kind.usr, container, indent); 1604 } 1605 rval.extra = [tref.primary] ~ tref.extra; 1606 } 1607 1608 return rval; 1609 } 1610 1611 private Nullable!TypeResults retrieveTypeDef(ref const(Cursor) c, 1612 ref const(Container) container, in uint this_indent) 1613 in { 1614 logNode(c, this_indent); 1615 assert(c.kind == CXCursorKind.typedefDecl); 1616 } 1617 out (result) { 1618 logTypeResult(result, this_indent); 1619 } 1620 body { 1621 import std.range : takeOne; 1622 1623 const uint indent = this_indent + 1; 1624 1625 auto handleTyperef(ref Nullable!TypeResults rval) { 1626 if (isFuncProtoTypedef(c)) { 1627 // this case is handled by handleTyperefFuncProto 1628 return; 1629 } 1630 1631 // any TypeRef children and thus need to traverse the tree? 1632 foreach (child; c.children.filterByTypeRef) { 1633 if (!child.kind.among(CXCursorKind.typeRef)) { 1634 break; 1635 } 1636 1637 auto tref = pass4(child, container, indent); 1638 1639 auto type = c.type; 1640 if (tref.primary.type.kind.info.kind == TypeKind.Info.Kind.typeRef) { 1641 rval = typeToTypedef(c, type, tref.primary.type.kind.usr, 1642 tref.primary.type.kind.info.canonicalRef, container, indent); 1643 } else { 1644 rval = typeToTypedef(c, type, tref.primary.type.kind.usr, 1645 tref.primary.type.kind.usr, container, indent); 1646 } 1647 rval.extra = [tref.primary] ~ tref.extra; 1648 } 1649 } 1650 1651 auto handleDecl(ref Nullable!TypeResults rval) { 1652 auto child_ = c.children.takeOne; 1653 if (child_.length == 0 || !child_[0].kind.canConvertNodeDeclToType) { 1654 return; 1655 } 1656 1657 auto c_child = child_[0]; 1658 auto tref = retrieveType(c_child, container, indent); 1659 1660 auto type = c.type; 1661 if (tref.primary.type.kind.info.kind == TypeKind.Info.Kind.typeRef) { 1662 rval = typeToTypedef(c, type, tref.primary.type.kind.usr, 1663 tref.primary.type.kind.info.canonicalRef, container, indent); 1664 } else { 1665 rval = typeToTypedef(c, type, tref.primary.type.kind.usr, 1666 tref.primary.type.kind.usr, container, indent); 1667 } 1668 rval.extra = [tref.primary] ~ tref.extra; 1669 } 1670 1671 auto handleTypeRefToTypeDeclFuncProto(ref Nullable!TypeResults rval) { 1672 static bool isFuncProto(ref const(Cursor) c) { 1673 //TODO consider merging or improving isFuncProtoTypedef with this 1674 if (!isFuncProtoTypedef(c)) { 1675 return false; 1676 } 1677 1678 if (c.children.length == 0) { 1679 return false; 1680 } 1681 1682 auto child_t = c.children[0].type; 1683 if (!child_t.isFunctionType || child_t.isPointer) { 1684 return false; 1685 } 1686 1687 return true; 1688 } 1689 1690 if (!isFuncProto(c)) { 1691 return; 1692 } 1693 1694 auto child = c.children[0]; 1695 auto ref_child = child.referenced; 1696 if (ref_child.kind != CXCursorKind.typedefDecl) { 1697 return; 1698 } 1699 1700 auto tref = retrieveType(ref_child, container, indent); 1701 1702 // TODO consolidate code. Copied from handleDecl 1703 auto type = c.type; 1704 if (tref.primary.type.kind.info.kind == TypeKind.Info.Kind.typeRef) { 1705 rval = typeToTypedef(c, type, tref.primary.type.kind.usr, 1706 tref.primary.type.kind.info.canonicalRef, container, indent); 1707 } else { 1708 rval = typeToTypedef(c, type, tref.primary.type.kind.usr, 1709 tref.primary.type.kind.usr, container, indent); 1710 } 1711 rval.extra = [tref.primary] ~ tref.extra; 1712 } 1713 1714 auto handleFuncProto(ref Nullable!TypeResults rval) { 1715 if (!isFuncProtoTypedef(c)) { 1716 return; 1717 } 1718 1719 auto type = c.type; 1720 auto func = typeToFuncProto!(TypeKind.FuncSignatureInfo)(c, type, container, indent); 1721 1722 rval = typeToTypedef(c, type, func.primary.type.kind.usr, 1723 func.primary.type.kind.usr, container, indent); 1724 rval.extra = [func.primary] ~ func.extra; 1725 } 1726 1727 auto underlying(ref Nullable!TypeResults rval) { 1728 // TODO this function is convoluted and complex. Consider how it can be rewritten. 1729 1730 auto underlying = c.typedefUnderlyingType; 1731 auto underlying_decl_c = underlying.declaration; 1732 1733 Nullable!TypeResults tref; 1734 // assuming that there are typedef nodes that have no declaration. 1735 if (underlying_decl_c.isValid) { 1736 tref = passType(underlying_decl_c, underlying, container, indent); 1737 } else { 1738 tref = passType(c, underlying, container, indent); 1739 // ensure it is unique 1740 tref.primary.type.kind.usr = makeFallbackUSR(c, indent); 1741 } 1742 1743 USRType canonical_usr; 1744 if (tref.primary.type.kind.info.kind == TypeKind.Info.Kind.typeRef) { 1745 canonical_usr = tref.primary.type.kind.info.canonicalRef; 1746 } else { 1747 canonical_usr = tref.primary.type.kind.usr; 1748 } 1749 1750 auto type = c.type; 1751 rval = typeToTypedef(c, type, tref.primary.type.kind.usr, 1752 canonical_usr, container, indent); 1753 rval.extra = [tref.primary] ~ tref.extra; 1754 } 1755 1756 // TODO investigate if this can be removed, aka always covered by underlying. 1757 auto fallback(ref Nullable!TypeResults rval) { 1758 // fallback, unable to represent as a typedef ref'ing a type 1759 auto type = c.type; 1760 rval = passType(c, type, container, indent); 1761 } 1762 1763 typeof(return) rval; 1764 foreach (idx, f; [&handleTypeRefToTypeDeclFuncProto, &handleTyperef, 1765 &handleFuncProto, &handleDecl, &underlying, &fallback]) { 1766 debug { 1767 import std.conv : to; 1768 import dextool.logger : trace; 1769 1770 trace(idx.to!string(), this_indent); 1771 } 1772 f(rval); 1773 if (!rval.isNull) { 1774 break; 1775 } 1776 } 1777 1778 return rval; 1779 } 1780 1781 /** Retrieve the type representation of a FuncDecl or CXXMethod. 1782 * 1783 * case a. A typedef of a function signature. 1784 * When it is instantiated it results in a FunctionDecl with a TypeRef. 1785 * Note in the example that the child node is a TypeRef. 1786 * Using the resultType to distinguish between a typedef function signature and 1787 * a function returning a function ptr. 1788 * 1789 * Example: 1790 * FunctionDecl "tiger" [Keyword "extern", Identifier "func_type", Identifier "tiger"] c:@F@tiger 1791 * TypeRef "func_type" [Identifier "func_type"] 1792 * 1793 * case b. A function with a return type which is a TypeRef to a TypedefDecl. 1794 * The first child node is a TypeRef. 1795 * This case should NOT be confused with case a. 1796 * 1797 * case c. A function declared "the normal way", void foo(); 1798 * 1799 * solve case a. 1800 * Try resolving the type of the first child node. 1801 * If the canonical type is a function, good. Case a. 1802 * Otherwise case b and c. 1803 */ 1804 private Nullable!TypeResults retrieveFunc(ref const(Cursor) c, 1805 ref const(Container) container, const uint this_indent) 1806 in { 1807 logNode(c, this_indent); 1808 assert(c.kind.among(CXCursorKind.functionDecl, CXCursorKind.cxxMethod)); 1809 } 1810 out (result) { 1811 logTypeResult(result, this_indent); 1812 } 1813 body { 1814 import std.algorithm : filter; 1815 import std.range : chain, only; 1816 import cpptooling.data : FuncFmt; 1817 1818 immutable indent = this_indent + 1; 1819 typeof(return) rval; 1820 1821 // distinguish between a child node that is for the return value from those 1822 // cases when it is a function derived from a typedef:ed function signature. 1823 auto result_decl_usr = c.func.resultType.declaration.usr; 1824 1825 foreach (child; c.children.filterByTypeRef.filter!((a) { 1826 auto tmp = a.referenced.usr; 1827 return tmp != result_decl_usr; 1828 })) { 1829 if (child.kind != CXCursorKind.typeRef) { 1830 break; 1831 } 1832 auto retrieved_ref = retrieveType(child, container, indent); 1833 1834 if (retrieved_ref.isNull) { 1835 continue; 1836 } 1837 1838 if (retrieved_ref.primary.type.kind.info.kind == TypeKind.Info.Kind.func) { 1839 // fast path 1840 rval = retrieved_ref; 1841 } else if (retrieved_ref.primary.type.kind.info.kind == TypeKind.Info.Kind.typeRef) { 1842 // check the canonical type 1843 foreach (k; chain(only(retrieved_ref.primary), retrieved_ref.extra)) { 1844 if (k.type.kind.usr != retrieved_ref.primary.type.kind.info.canonicalRef) { 1845 continue; 1846 } 1847 1848 if (k.type.kind.info.kind == TypeKind.Info.Kind.func) { 1849 rval = retrieved_ref; 1850 } else if (k.type.kind.info.kind == TypeKind.Info.Kind.funcSignature) { 1851 // function declaration of a typedef'ed signature 1852 rval = retrieved_ref; 1853 rval.extra ~= rval.primary; 1854 1855 auto prim = k; 1856 auto info = k.type.kind.info; 1857 prim.type.kind.info = TypeKind.FuncInfo(FuncFmt(k.type.kind.splitTypeId(indent)), 1858 info.return_, info.returnAttr, info.params); 1859 prim.location = makeLocation(c); 1860 prim.type.kind.usr = makeFallbackUSR(c, this_indent); 1861 rval.primary = prim; 1862 } 1863 } 1864 } 1865 } 1866 1867 if (rval.isNull) { 1868 auto type = c.type; 1869 rval = passType(c, type, container, indent); 1870 } 1871 1872 return rval; 1873 } 1874 1875 /** Only able to uniquely represent the class template. 1876 * 1877 * TODO Unable to instansiate. 1878 */ 1879 private TypeResults retrieveClassTemplate(ref const(Cursor) c, 1880 ref const(Container) container, in uint indent) 1881 in { 1882 import std.algorithm : among; 1883 1884 logNode(c, indent); 1885 assert(c.kind.among(CXCursorKind.classTemplate, 1886 CXCursorKind.classTemplatePartialSpecialization)); 1887 } 1888 body { 1889 TypeResults rval; 1890 1891 auto type = c.type; 1892 rval.primary.type = makeTypeKindAttr(type, c); 1893 rval.primary.type.kind.info = TypeKind.SimpleInfo(SimpleFmt(TypeId(c.spelling))); 1894 rval.primary.type.kind.usr = c.usr; 1895 rval.primary.location = makeLocation(c); 1896 1897 return rval; 1898 } 1899 1900 private Nullable!TypeResults retrieveClassBaseSpecifier(ref const(Cursor) c, 1901 ref const(Container) container, in uint this_indent) 1902 in { 1903 logNode(c, this_indent); 1904 assert(c.kind == CXCursorKind.cxxBaseSpecifier); 1905 } 1906 body { 1907 import dextool.logger : trace; 1908 1909 auto indent = this_indent + 1; 1910 1911 // when the cursor references a definition. easy 1912 bool tryReferenced(ref Nullable!TypeResults rval) { 1913 trace("", this_indent); 1914 auto c_ref = c.referenced; 1915 1916 if (c_ref.kind == CXCursorKind.noDeclFound) { 1917 return false; 1918 } 1919 1920 rval = retrieveType(c_ref, container, indent); 1921 1922 return true; 1923 } 1924 1925 // no definition exist. e.g in the cases of a template instantiation. 1926 bool reconstructFromCursor(ref Nullable!TypeResults rval) { 1927 trace("", this_indent); 1928 1929 rval = TypeResults(); 1930 1931 auto type = c.type; 1932 rval.primary.type = makeTypeKindAttr(type, c); 1933 1934 rval.primary.type.kind.info = TypeKind.SimpleInfo(SimpleFmt(TypeId(c.spelling))); 1935 rval.primary.type.kind.usr = makeEnsuredUSR(c, indent); 1936 rval.primary.location = makeLocation(c); 1937 1938 return true; 1939 } 1940 1941 Nullable!TypeResults rval; 1942 1943 foreach (idx, f; [&tryReferenced, &reconstructFromCursor]) { 1944 if (f(rval)) { 1945 break; 1946 } 1947 } 1948 1949 return rval; 1950 } 1951 1952 /** Extract the type of a parameter cursor. 1953 * 1954 * TODO if nothing changes remove either retrieveParam or retrieveInstanceDecl, 1955 * code duplication. 1956 */ 1957 private Nullable!TypeResults retrieveParam(ref const(Cursor) c, 1958 ref const(Container) container, in uint this_indent) 1959 in { 1960 logNode(c, this_indent); 1961 // TODO add assert for the types allowed 1962 } 1963 out (result) { 1964 logTypeResult(result, this_indent); 1965 } 1966 body { 1967 return retrieveInstanceDecl(c, container, this_indent + 1); 1968 } 1969 1970 /** Only able to uniquely represent the class template. 1971 * 1972 * TODO Unable to instansiate. 1973 */ 1974 private Nullable!TypeResults retrieveTemplateParam(ref const(Cursor) c, 1975 ref const(Container) container, in uint this_indent) 1976 in { 1977 logNode(c, this_indent); 1978 // TODO add assert for the types allowed 1979 } 1980 body { 1981 import std.range : takeOne; 1982 1983 uint indent = this_indent + 1; 1984 Nullable!TypeResults rval; 1985 1986 if (c.spelling.length == 0) { 1987 //TODO could probably be a random name, the location or something. 1988 // Example when it occurs: 1989 // template <typename/*here*/> class allocator; 1990 return rval; 1991 } 1992 1993 auto type = c.type; 1994 rval = retrieveParam(c, container, indent); 1995 1996 return rval; 1997 } 1998 1999 private alias ExtractParamsResult = Tuple!(TypeResult, "result", string, "id", 2000 Flag!"isVariadic", "isVariadic"); 2001 private alias ExtractParamsResults = Tuple!(ExtractParamsResult[], "params", 2002 TypeResult[], "extra"); 2003 2004 private ExtractParamsResults extractParams(ref const(Cursor) c, ref Type type, 2005 ref const(Container) container, in uint this_indent) 2006 in { 2007 logNode(c, this_indent); 2008 logType(type, this_indent); 2009 assert(type.isFunctionType || type.isTypedef || type.kind == CXTypeKind.functionNoProto); 2010 } 2011 out (result) { 2012 import dextool.logger : trace; 2013 2014 foreach (p; result.params) { 2015 trace(p.result.type.toStringDecl(p.id), this_indent); 2016 } 2017 2018 foreach (e; result.extra) { 2019 logTypeResult(e, this_indent); 2020 } 2021 } 2022 body { 2023 const auto indent = this_indent + 1; 2024 2025 void appendParams(ref const(Cursor) c, ref ExtractParamsResults rval) { 2026 import std.range : enumerate; 2027 2028 foreach (idx, p; c.children.enumerate) { 2029 if (p.kind != CXCursorKind.parmDecl) { 2030 logNode(p, this_indent); 2031 continue; 2032 } 2033 2034 auto tka = retrieveType(p, container, indent); 2035 auto id = p.spelling; 2036 rval.params ~= ExtractParamsResult(tka.primary, id, No.isVariadic); 2037 rval.extra ~= tka.extra; 2038 } 2039 2040 if (type.func.isVariadic) { 2041 import clang.SourceLocation; 2042 2043 TypeResult result; 2044 2045 auto info = TypeKind.SimpleInfo(SimpleFmt(TypeId("..."))); 2046 result.type.kind.info = info; 2047 result.type.kind.usr = "..." ~ c.location.toString(); 2048 result.location = makeLocation(c); 2049 2050 // TODO remove this ugly hack 2051 // space as id to indicate it is empty 2052 rval.params ~= ExtractParamsResult(result, " ", Yes.isVariadic); 2053 } 2054 } 2055 2056 ExtractParamsResults rval; 2057 2058 if (c.kind == CXCursorKind.typeRef) { 2059 auto cref = c.referenced; 2060 appendParams(cref, rval); 2061 } else { 2062 appendParams(c, rval); 2063 } 2064 2065 return rval; 2066 } 2067 2068 /// Join an array slice of PTuples to a parameter string of "type" "id" 2069 private string joinParamId(ExtractParamsResult[] r) { 2070 import std.algorithm : joiner, map, filter; 2071 import std.conv : text; 2072 import std.range : enumerate; 2073 2074 static string getTypeId(ref ExtractParamsResult p, ulong uid) { 2075 if (p.id.length == 0) { 2076 //TODO decide if to autogenerate for unnamed parameters here or later 2077 //return p.tka.toStringDecl("x" ~ text(uid)); 2078 return p.result.type.toStringDecl(""); 2079 } else { 2080 return p.result.type.toStringDecl(p.id); 2081 } 2082 } 2083 2084 // using cache to avoid calling getName twice. 2085 return r.enumerate.map!(a => getTypeId(a.value, a.index)).filter!(a => a.length > 0) 2086 .joiner(", ").text(); 2087 2088 } 2089 2090 private Nullable!string translateCursorType(CXTypeKind kind) 2091 in { 2092 import std.conv : to; 2093 2094 logger.trace(to!string(kind)); 2095 } 2096 out (result) { 2097 logger.trace(!result.isNull, result); 2098 } 2099 body { 2100 Nullable!string r; 2101 2102 with (CXTypeKind) switch (kind) { 2103 case invalid: 2104 break; 2105 case unexposed: 2106 break; 2107 case void_: 2108 r = "void"; 2109 break; 2110 case bool_: 2111 r = "bool"; 2112 break; 2113 case charU: 2114 r = "unsigned char"; 2115 break; 2116 case uChar: 2117 r = "unsigned char"; 2118 break; 2119 case char16: 2120 break; 2121 case char32: 2122 break; 2123 case uShort: 2124 r = "unsigned short"; 2125 break; 2126 case uInt: 2127 r = "unsigned int"; 2128 break; 2129 case uLong: 2130 r = "unsigned long"; 2131 break; 2132 case uLongLong: 2133 r = "unsigned long long"; 2134 break; 2135 case uInt128: 2136 break; 2137 case charS: 2138 r = "char"; 2139 break; 2140 case sChar: 2141 r = "char"; 2142 break; 2143 case wChar: 2144 r = "wchar_t"; 2145 break; 2146 case short_: 2147 r = "short"; 2148 break; 2149 case int_: 2150 r = "int"; 2151 break; 2152 case long_: 2153 r = "long"; 2154 break; 2155 case longLong: 2156 r = "long long"; 2157 break; 2158 case int128: 2159 break; 2160 case float_: 2161 r = "float"; 2162 break; 2163 case double_: 2164 r = "double"; 2165 break; 2166 case longDouble: 2167 r = "long double"; 2168 break; 2169 case nullPtr: 2170 r = "null"; 2171 break; 2172 case overload: 2173 break; 2174 case dependent: 2175 break; 2176 2177 case objCId: 2178 case objCClass: 2179 case objCSel: 2180 break; 2181 2182 case complex: 2183 case pointer: 2184 case blockPointer: 2185 case lValueReference: 2186 case rValueReference: 2187 case record: 2188 case enum_: 2189 case typedef_: 2190 case functionNoProto: 2191 case functionProto: 2192 case vector: 2193 case incompleteArray: 2194 case variableArray: 2195 case dependentSizedArray: 2196 case memberPointer: 2197 case auto_: 2198 2199 /** 2200 * \brief Represents a type that was referred to using an elaborated type keyword. 2201 * 2202 * E.g., struct S, or via a qualified name, e.g., N::M::type, or both. 2203 */ 2204 case elaborated: 2205 break; 2206 2207 default: 2208 logger.trace("Unhandled type kind ", to!string(kind)); 2209 } 2210 2211 return r; 2212 }