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