1 /** 2 Date: 2015-2017, 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 Structuraly represents the semantic-centric view of of C/C++ code. 7 8 The guiding principle for this module is: "Correct by construction". 9 * After the data is created it should be "correct". 10 * As far as possible avoid runtime errors. 11 12 Structs was chosen instead of classes to: 13 * ensure allocation on the stack. 14 * lower the GC pressure. 15 * dynamic dispatch isn't needed. 16 * value semantics. 17 18 Design rules for Structural representation. 19 shall: 20 * toString functions shall never append a newline as the last character. 21 * toString(..., FormatSpec!Char fmt) shall have a %u when the struct has a USR. 22 * all c'tor parameters shall be const. 23 * members are declared at the top. 24 Rationale const: (The ':' is not a typo) can affect var members thus all 25 member shall be defined after imports. 26 when applicable: 27 * attributes "@safe" for the struct. 28 * Add mixin for Id when the need arise. 29 30 TODO Implement uniqueness for namespaces and classes via e.g. RedBlackTree's 31 */ 32 module cpptooling.data.representation; 33 34 import std.array : Appender; 35 import std.format : format, FormatSpec; 36 import std.range : isInputRange; 37 import std.traits : Unqual; 38 import std.typecons : Tuple, Flag, Yes, No, Nullable; 39 import std.variant : Algebraic; 40 import logger = std.experimental.logger; 41 42 public import cpptooling.data.type; 43 44 import cpptooling.data.kind_type; 45 import cpptooling.data.symbol.types : USRType; 46 import dextool.hash : makeHash; 47 48 static import cpptooling.data.class_classification; 49 50 version (unittest) { 51 import unit_threaded : Name; 52 import unit_threaded : shouldBeTrue, shouldEqual, shouldBeGreaterThan; 53 import unit_threaded : writelnUt; 54 55 private enum dummyUSR = USRType("dummyUSR"); 56 } 57 58 /// Generate the next globally unique ID. 59 size_t nextUniqueID() @safe nothrow { 60 import cpptooling.utility.global_unique : nextNumber; 61 62 return nextNumber; 63 } 64 65 /** Construct a USR that is ensured to be unique. 66 * 67 * The USR start with a number which is an illegal symbol in C/C++. 68 * Which should result in them never clashing with those from sources derived 69 * from source code. 70 */ 71 USRType makeUniqueUSR() @safe nothrow { 72 import std.conv : text; 73 74 return USRType(text(nextUniqueID)); 75 } 76 77 void funcToString(Writer, Char)(const(CppClass.CppFunc) func, scope Writer w, in Char[] fmt) @trusted { 78 import std.format : formattedWrite; 79 import std.variant : visit; 80 81 //dfmt off 82 func.visit!((const(CppMethod) a) => formattedWrite(w, fmt, a), 83 (const(CppMethodOp) a) => formattedWrite(w, fmt, a), 84 (const(CppCtor) a) => formattedWrite(w, fmt, a), 85 (const(CppDtor) a) => formattedWrite(w, fmt, a)); 86 //dfmt on 87 } 88 89 string funcToString(const(CppClass.CppFunc) func) @safe { 90 import std.exception : assumeUnique; 91 92 char[] buf; 93 buf.reserve(100); 94 funcToString(func, (const(char)[] s) { buf ~= s; }, "%s"); 95 auto trustedUnique(T)(T t) @trusted { 96 return assumeUnique(t); 97 } 98 99 return trustedUnique(buf); 100 } 101 102 string methodNameToString(const(CppClass.CppFunc) func) @trusted { 103 import std.variant : visit; 104 105 //dfmt off 106 return func.visit!((const(CppMethod) a) => a.name, 107 (const(CppMethodOp) a) => a.name, 108 (const(CppCtor) a) => a.name, 109 (const(CppDtor) a) => a.name); 110 //dfmt on 111 } 112 113 /// Convert a CxParam to a string. 114 string paramTypeToString(CxParam p, string id = "") @trusted { 115 import std.variant : visit; 116 117 // dfmt off 118 return p.visit!( 119 (TypeKindVariable tk) { return tk.type.toStringDecl(id); }, 120 (TypeKindAttr t) { return t.toStringDecl; }, 121 (VariadicType a) { return "..."; } 122 ); 123 // dfmt on 124 } 125 126 /// Convert a CxParam to a string. 127 string paramNameToString(CxParam p, string id = "") @trusted { 128 import std.variant : visit; 129 130 // dfmt off 131 return p.visit!( 132 (TypeKindVariable tk) { return tk.name; }, 133 (TypeKindAttr t) { return id; }, 134 (VariadicType a) { return "..."; } 135 ); 136 // dfmt on 137 } 138 139 /// Standard implementation of toString using the toString that take an 140 /// OutputRange. 141 private string standardToString() { 142 return q{ 143 string toString()() const { 144 import std.format : FormatSpec; 145 import std.exception : assumeUnique; 146 147 char[] buf; 148 buf.reserve(100); 149 auto fmt = FormatSpec!char("%s"); 150 toString((const(char)[] s) { buf ~= s; }, fmt); 151 auto trustedUnique(T)(T t) @trusted { 152 return assumeUnique(t); 153 } 154 155 return trustedUnique(buf); 156 } 157 }; 158 } 159 160 /// Expects a toString function where it is mixed in. 161 /// base value for hash is 0 to force deterministic hashes. Use the pointer for 162 /// unique between objects. 163 private template mixinUniqueId(IDType) if (is(IDType == size_t) || is(IDType == string)) { 164 //TODO add check to see that this do NOT already have id_. 165 166 private IDType id_; 167 168 @safe: 169 170 static if (is(IDType == size_t)) { 171 private void setUniqueId(string identifier) @safe pure nothrow { 172 this.id_ = makeHash(identifier); 173 } 174 } else static if (is(IDType == string)) { 175 private void setUniqueId(Char)(Char[] identifier) @safe pure nothrow { 176 this.id_ = identifier.idup; 177 } 178 } else { 179 static assert(false, "IDType must be either size_t or string"); 180 } 181 182 IDType id() @safe pure const nothrow { 183 return id_; 184 } 185 186 int opCmp(T : typeof(this))(auto ref const T rhs) const { 187 return this.id_ < rhs.id(); 188 } 189 190 bool opEquals(T : typeof(this))(auto ref const T rhs) const { 191 return this.id_ == rhs.id(); 192 } 193 194 static if (__VERSION__ < 2084) { 195 size_t toHash() @trusted pure nothrow const scope { 196 return id_.hashOf; 197 } 198 } else { 199 size_t toHash() @safe pure nothrow const @nogc scope { 200 return id_.hashOf; 201 } 202 } 203 204 void unsafeForceID(IDType id) { 205 this.id_ = id; 206 } 207 } 208 209 private template mixinCommentHelper() { 210 private string[] comments_; 211 212 /** Add a comment. 213 * 214 * Params: 215 * txt = a oneline comment, must NOT end with newline 216 */ 217 auto ref comment(string txt) @safe pure nothrow { 218 comments_ ~= txt; 219 return this; 220 } 221 222 auto comments() @safe pure nothrow const @nogc { 223 return comments_; 224 } 225 226 private void helperPutComments(Writer)(scope Writer w) const { 227 import std.ascii : newline; 228 import std.range.primitives : put; 229 230 foreach (l; comments_) { 231 put(w, "// "); 232 put(w, l); 233 put(w, newline); 234 } 235 } 236 } 237 238 /// Convert a CxParam to a string. 239 string toInternal(CxParam p) @trusted { 240 import std.variant : visit; 241 242 // dfmt off 243 return p.visit!( 244 (TypeKindVariable tk) {return tk.type.toStringDecl(tk.name);}, 245 (TypeKindAttr t) { return t.toStringDecl; }, 246 (VariadicType a) { return "..."; } 247 ); 248 // dfmt on 249 } 250 251 /// Convert a TypeKindVariable to a string. 252 string toInternal(TypeKindVariable tk) @trusted { 253 return tk.type.toStringDecl(tk.name); 254 } 255 256 /// Join a range of CxParams to a string separated by ", ". 257 string joinParams(const(CxParam)[] r) @safe { 258 import std.algorithm : joiner, map; 259 import std.conv : text; 260 import std.range : enumerate; 261 262 static string getTypeName(const CxParam p, ulong uid) @trusted { 263 import std.variant : visit; 264 265 // dfmt off 266 auto x = p.visit!( 267 (const TypeKindVariable t) {return t.type.toStringDecl(t.name);}, 268 (const TypeKindAttr t) { return t.toStringDecl("x" ~ text(uid)); }, 269 (const VariadicType a) { return "..."; } 270 ); 271 // dfmt on 272 return x; 273 } 274 275 // dfmt off 276 return r 277 .enumerate 278 .map!(a => getTypeName(a.value, a.index)) 279 .joiner(", ") 280 .text(); 281 // dfmt on 282 } 283 284 /// Join a range of CxParams by extracting the parameter names. 285 string joinParamNames(T)(T r) @safe if (isInputRange!T) { 286 import std.algorithm : joiner, map, filter; 287 import std.conv : text; 288 import std.range : enumerate; 289 290 static string getName(const CxParam p, ulong uid) @trusted { 291 import std.variant : visit; 292 293 // dfmt off 294 return p.visit!( 295 (const TypeKindVariable tk) {return tk.name;}, 296 (const TypeKindAttr t) { return "x" ~ text(uid); }, 297 (const VariadicType a) { return ""; } 298 ); 299 // dfmt on 300 } 301 302 // using cache to avoid getName is called twice. 303 // dfmt off 304 return r 305 .enumerate 306 .map!(a => getName(a.value, a.index)) 307 .filter!(a => a.length > 0) 308 .joiner(", ").text(); 309 // dfmt on 310 } 311 312 /// Join a range of CxParams to a string of the parameter types separated by ", ". 313 string joinParamTypes(const(CxParam)[] r) @safe { 314 import std.algorithm : joiner, map; 315 import std.conv : text; 316 import std.range : enumerate; 317 318 // dfmt off 319 return r 320 .enumerate 321 .map!(a => getType(a.value)) 322 .joiner(", ") 323 .text(); 324 // dfmt on 325 } 326 327 /// Get the name of a C++ method. 328 string getName()(ref const(CppClass.CppFunc) method) @trusted { 329 import std.variant : visit; 330 331 // dfmt off 332 return method.visit!( 333 (const CppMethod m) => m.name, 334 (const CppMethodOp m) => "", 335 (const CppCtor m) => m.name, 336 (const CppDtor m) => m.name); 337 // dfmt on 338 } 339 340 /// Get the name of a parameter or the default. 341 string getName(const CxParam p, string default_) @safe { 342 static string getName(const CxParam p, string default_) @trusted { 343 import std.variant : visit; 344 345 // dfmt off 346 return p.visit!( 347 (const TypeKindVariable tk) {return tk.name;}, 348 (const TypeKindAttr t) { return default_; }, 349 (const VariadicType a) { return default_; } 350 ); 351 // dfmt on 352 } 353 354 return getName(p, default_); 355 } 356 357 /// Get the parameter type as a string. 358 string getType(const CxParam p) @trusted { 359 import std.variant : visit; 360 361 return p.visit!((const TypeKindVariable t) { return t.type.toStringDecl; }, 362 (const TypeKindAttr t) { return t.toStringDecl; }, (const VariadicType a) { 363 return "..."; 364 }); 365 } 366 367 /// Make a variadic parameter. 368 CxParam makeCxParam() @trusted { 369 return CxParam(VariadicType.yes); 370 } 371 372 /// CxParam created by analyzing a TypeKindVariable. 373 /// A empty variable name means it is of the algebraic type TypeKind. 374 CxParam makeCxParam(TypeKindVariable tk) @trusted { 375 if (tk.name.length == 0) 376 return CxParam(tk.type); 377 return CxParam(tk); 378 } 379 380 struct UnpackParamResult { 381 TypeKindAttr type; 382 bool isVariadic; 383 } 384 385 /// Unpack a CxParam. 386 UnpackParamResult unpackParam(ref const(CxParam) p) @safe { 387 import std.variant : visit; 388 389 UnpackParamResult rval; 390 391 // dfmt off 392 () @trusted { 393 p.visit!((const TypeKindVariable v) => rval.type = v.type, 394 (const TypeKindAttr v) => rval.type = v, 395 (const VariadicType v) { rval.isVariadic = true; return rval.type; }); 396 }(); 397 // dfmt on 398 399 return rval; 400 } 401 402 private void assertVisit(ref const(CxParam) p) @trusted { 403 import std.variant : visit; 404 405 // dfmt off 406 p.visit!( 407 (const TypeKindVariable v) { assert(v.name.length > 0); 408 assert(v.type.toStringDecl.length > 0);}, 409 (const TypeKindAttr v) { assert(v.toStringDecl.length > 0); }, 410 (const VariadicType v) {}); 411 // dfmt on 412 } 413 414 struct CxGlobalVariable { 415 mixin mixinUniqueId!size_t; 416 417 private TypeKindVariable variable; 418 419 Nullable!USRType usr; 420 Nullable!Language language; 421 422 invariant { 423 assert(usr.isNull || usr.get.length > 0); 424 } 425 426 /** 427 * do NOT use the usr from var.type.kind.usr, it is for the type not the 428 * instance. 429 */ 430 this(USRType usr, TypeKindVariable var) @safe pure nothrow { 431 this.usr = usr; 432 this.variable = var; 433 434 if (var.name.length != 0) { 435 // Prefer using the name because it is also the c/c++ identifier. 436 // The same name in a namespace would mean a collition. Breakin the 437 // one definition rule. 438 setUniqueId(var.name); 439 } else { 440 setUniqueId(usr); 441 } 442 } 443 444 this(USRType usr, TypeKindAttr type, CppVariable name) @safe pure nothrow { 445 this(usr, TypeKindVariable(type, name)); 446 } 447 448 const: 449 450 mixin(standardToString); 451 452 /// If formatSpec is "%u" then the USR will be put as a comment. 453 void toString(Writer, Char)(scope Writer sink, FormatSpec!Char fmt) 454 in { 455 import std.algorithm : among; 456 457 // see switch stmt in body for explanation. 458 assert(!variable.type.kind.info.kind.among(TypeKind.Info.Kind.ctor, 459 TypeKind.Info.Kind.dtor)); 460 } 461 body { 462 import std.algorithm : map, copy; 463 import std.ascii : newline; 464 import std.format : formattedWrite; 465 import std.range : put; 466 import cpptooling.data : TypeKind; 467 468 final switch (variable.type.kind.info.kind) with (TypeKind.Info) { 469 case Kind.record: 470 case Kind.func: 471 case Kind.funcPtr: 472 case Kind.funcSignature: 473 case Kind.primitive: 474 case Kind.simple: 475 case Kind.typeRef: 476 case Kind.array: 477 case Kind.pointer: 478 formattedWrite(sink, "%s;", variable.type.toStringDecl(variable.name)); 479 if (!usr.isNull && fmt.spec == 'u') { 480 put(sink, " // "); 481 put(sink, usr); 482 } 483 break; 484 case Kind.ctor: 485 logger.error("Assumption broken. A global variable with the type of a Constructor"); 486 break; 487 case Kind.dtor: 488 logger.error("Assumption broken. A global variable with the type of a Destructor"); 489 break; 490 case Kind.null_: 491 logger.error("Type of global variable is null. Identifier ", 492 variable.name); 493 break; 494 } 495 } 496 497 @safe pure nothrow: 498 499 auto type() { 500 return variable.type; 501 } 502 503 auto name() { 504 return variable.name; 505 } 506 507 auto typeName() { 508 return variable; 509 } 510 } 511 512 struct CppMethodGeneric { 513 template Parameters() { 514 void put(const CxParam p) { 515 params_ ~= p; 516 } 517 518 auto paramRange() const @nogc @safe pure nothrow { 519 return params_; 520 } 521 522 private CxParam[] params_; 523 } 524 525 /** Common properties for c'tor, d'tor, methods and operators. 526 * 527 * Defines the needed variables. 528 * Expecting them to be set in c'tors. 529 */ 530 template BaseProperties() { 531 import std.typecons : Nullable; 532 533 const pure @nogc nothrow { 534 bool isVirtual() { 535 import std.algorithm : among; 536 537 with (MemberVirtualType) { 538 return classification_.get.among(Virtual, Pure) != 0; 539 } 540 } 541 542 bool isPure() { 543 with (MemberVirtualType) { 544 return classification_.get == Pure; 545 } 546 } 547 548 MemberVirtualType classification() { 549 return classification_.get; 550 } 551 552 CppAccess accessType() { 553 return accessType_; 554 } 555 556 CppMethodName name() { 557 return name_.get; 558 } 559 } 560 561 private Nullable!MemberVirtualType classification_; 562 private CppAccess accessType_; 563 private Nullable!CppMethodName name_; 564 } 565 566 /** Properties used by methods and operators. 567 * 568 * Defines the needed variables. 569 * Expecting them to be set in c'tors. 570 */ 571 template MethodProperties() { 572 const pure @nogc nothrow { 573 bool isConst() { 574 return isConst_; 575 } 576 577 CxReturnType returnType() { 578 return returnType_; 579 } 580 } 581 582 private bool isConst_; 583 private CxReturnType returnType_; 584 } 585 586 /// Helper for converting virtual type to string 587 template StringHelperVirtual() { 588 static string helperVirtualPre(MemberVirtualType pre) @safe pure nothrow @nogc { 589 switch (pre) { 590 case MemberVirtualType.Virtual: 591 case MemberVirtualType.Pure: 592 return "virtual "; 593 default: 594 return ""; 595 } 596 } 597 598 static string helperVirtualPost(MemberVirtualType post) @safe pure nothrow @nogc { 599 switch (post) { 600 case MemberVirtualType.Pure: 601 return " = 0"; 602 default: 603 return ""; 604 } 605 } 606 607 static string helperConst(bool is_const) @safe pure nothrow @nogc { 608 final switch (is_const) { 609 case true: 610 return " const"; 611 case false: 612 return ""; 613 } 614 } 615 } 616 } 617 618 /// Information about free functions. 619 /// TODO: rename to CxFreeFunction 620 struct CFunction { 621 mixin mixinUniqueId!size_t; 622 623 Nullable!USRType usr; 624 Nullable!Language language; 625 626 private { 627 CFunctionName name_; 628 CxParam[] params; 629 CxReturnType returnType_; 630 VariadicType isVariadic_; 631 StorageClass storageClass_; 632 } 633 634 invariant() { 635 if (!usr.isNull) { 636 assert(usr.get.length > 0); 637 assert(name_.length > 0); 638 assert(returnType_.toStringDecl.length > 0); 639 640 foreach (p; params) { 641 assertVisit(p); 642 } 643 } 644 } 645 646 /// C function representation. 647 this(const USRType usr, const CFunctionName name, const CxParam[] params_, 648 const CxReturnType return_type, const VariadicType is_variadic, 649 const StorageClass storage_class) @safe { 650 this.usr = usr; 651 this.name_ = name; 652 this.returnType_ = return_type; 653 this.isVariadic_ = is_variadic; 654 this.storageClass_ = storage_class; 655 656 this.params = params_.dup; 657 658 setUniqueId(format("%s(%s)", name, params.joinParamTypes)); 659 } 660 661 /// Function with no parameters. 662 this(USRType usr, const CFunctionName name, const CxReturnType return_type) @safe { 663 this(usr, name, CxParam[].init, return_type, VariadicType.no, StorageClass.None); 664 } 665 666 /// Function with no parameters and returning void. 667 this(USRType usr, const CFunctionName name) @safe { 668 auto void_ = CxReturnType(makeSimple("void")); 669 this(usr, name, CxParam[].init, void_, VariadicType.no, StorageClass.None); 670 } 671 672 void toString(Writer, Char)(scope Writer sink, FormatSpec!Char fmt) const { 673 import std.conv : to; 674 import std.format : formattedWrite; 675 import std.range : put; 676 677 formattedWrite(sink, "%s %s(%s); // %s", returnType.toStringDecl, name, 678 paramRange.joinParams, to!string(storageClass)); 679 680 if (!usr.isNull && fmt.spec == 'u') { 681 put(sink, " "); 682 put(sink, usr); 683 } 684 } 685 686 @safe const: 687 688 mixin(standardToString); 689 690 nothrow pure @nogc: 691 692 /// A range over the parameters of the function. 693 auto paramRange() { 694 return params; 695 } 696 697 CxReturnType returnType() { 698 return returnType_; 699 } 700 701 auto name() { 702 return name_; 703 } 704 705 StorageClass storageClass() { 706 return storageClass_; 707 } 708 709 /// If the function is variadic, aka have a parameter with "...". 710 bool isVariadic() { 711 return VariadicType.yes == isVariadic_; 712 } 713 } 714 715 /** Represent a C++ constructor. 716 * 717 * The construction of CppCtor is simplified in the example. 718 * Example: 719 * ---- 720 * class A { 721 * public: 722 * A(); // CppCtor("A", null, Public); 723 * A(int x); // CppCtor("A", ["int x"], Public); 724 * }; 725 * ---- 726 */ 727 struct CppCtor { 728 Nullable!USRType usr; 729 730 private { 731 CppAccess accessType_; 732 Nullable!CppMethodName name_; 733 } 734 735 invariant() { 736 if (!name_.isNull) { 737 assert(usr.isNull || usr.get.length > 0); 738 assert(name_.get.length > 0); 739 foreach (p; params_) { 740 assertVisit(p); 741 } 742 } 743 } 744 745 this(const USRType usr, const CppMethodName name, const CxParam[] params, const CppAccess access) @safe { 746 this.usr = usr; 747 this.name_ = name; 748 this.accessType_ = access; 749 this.params_ = params.dup; 750 751 setUniqueId(format("%s(%s)", name_, paramRange.joinParamTypes)); 752 } 753 754 void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const { 755 import std.format : formattedWrite; 756 import std.range.primitives : put; 757 758 helperPutComments(w); 759 formattedWrite(w, "%s(%s)", name_, paramRange.joinParams); 760 put(w, ";"); 761 if (!usr.isNull && fmt.spec == 'u') { 762 formattedWrite(w, " // %s", usr); 763 } 764 } 765 766 @safe: 767 mixin mixinCommentHelper; 768 mixin mixinUniqueId!size_t; 769 mixin CppMethodGeneric.Parameters; 770 771 const: 772 mixin(standardToString); 773 774 auto accessType() { 775 return accessType_; 776 } 777 778 auto name() { 779 return name_; 780 } 781 } 782 783 struct CppDtor { 784 // TODO remove the Nullable, if possible. 785 Nullable!USRType usr; 786 787 invariant() { 788 if (!name_.isNull) { 789 assert(usr.isNull || usr.get.length > 0); 790 assert(name_.get.length > 0); 791 assert(classification_ != MemberVirtualType.Unknown); 792 } 793 } 794 795 this(const USRType usr, const CppMethodName name, const CppAccess access, 796 const CppVirtualMethod virtual) @safe { 797 this.usr = usr; 798 this.classification_ = virtual; 799 this.accessType_ = access; 800 this.name_ = name; 801 802 setUniqueId(name_.get); 803 } 804 805 void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const { 806 import std.format : formattedWrite; 807 808 helperPutComments(w); 809 formattedWrite(w, "%s%s();", helperVirtualPre(classification_.get), name_); 810 if (!usr.isNull && fmt.spec == 'u') { 811 formattedWrite(w, " // %s", usr); 812 } 813 } 814 815 @safe: 816 mixin mixinCommentHelper; 817 mixin mixinUniqueId!size_t; 818 mixin CppMethodGeneric.BaseProperties; 819 mixin CppMethodGeneric.StringHelperVirtual; 820 mixin(standardToString); 821 } 822 823 struct CppMethod { 824 Nullable!USRType usr; 825 826 invariant { 827 if (!name_.isNull) { 828 assert(usr.isNull || usr.get.length > 0); 829 assert(name_.get.length > 0); 830 assert(returnType_.toStringDecl.length > 0); 831 assert(classification_ != MemberVirtualType.Unknown); 832 foreach (p; params_) { 833 assertVisit(p); 834 } 835 } 836 } 837 838 this(const USRType usr, const CppMethodName name, const CxParam[] params, const CxReturnType return_type, 839 const CppAccess access, const CppConstMethod const_, const CppVirtualMethod virtual) @safe { 840 this.usr = usr; 841 this.classification_ = virtual; 842 this.accessType_ = access; 843 this.name_ = name; 844 this.returnType_ = return_type; 845 this.isConst_ = const_; 846 847 this.params_ = params.dup; 848 849 setUniqueId(format("%s(%s)", name, paramRange.joinParamTypes)); 850 } 851 852 /// Function with no parameters. 853 this(USRType usr, const CppMethodName name, const CxReturnType return_type, 854 const CppAccess access, const CppConstMethod const_, const CppVirtualMethod virtual) @safe { 855 this(usr, name, CxParam[].init, return_type, access, const_, virtual); 856 } 857 858 /// Function with no parameters and returning void. 859 this(USRType usr, const CppMethodName name, const CppAccess access, const CppConstMethod const_ = CppConstMethod(false), 860 const CppVirtualMethod virtual = CppVirtualMethod(MemberVirtualType.Normal)) @safe { 861 auto void_ = CxReturnType(makeSimple("void")); 862 this(usr, name, CxParam[].init, void_, access, const_, virtual); 863 } 864 865 void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) @safe const { 866 import std.format : formattedWrite; 867 import std.range.primitives : put; 868 869 helperPutComments(w); 870 put(w, helperVirtualPre(classification_.get)); 871 put(w, returnType_.toStringDecl); 872 put(w, " "); 873 put(w, name_); 874 formattedWrite(w, "(%s)", paramRange.joinParams); 875 put(w, helperConst(isConst)); 876 put(w, helperVirtualPost(classification_.get)); 877 put(w, ";"); 878 879 if (!usr.isNull && fmt.spec == 'u') { 880 put(w, " // "); 881 put(w, usr); 882 } 883 } 884 885 @safe: 886 mixin mixinCommentHelper; 887 mixin mixinUniqueId!size_t; 888 mixin CppMethodGeneric.Parameters; 889 mixin CppMethodGeneric.StringHelperVirtual; 890 mixin CppMethodGeneric.BaseProperties; 891 mixin CppMethodGeneric.MethodProperties; 892 mixin(standardToString); 893 } 894 895 struct CppMethodOp { 896 Nullable!USRType usr; 897 898 invariant() { 899 if (!name_.isNull) { 900 assert(name_.get.length > 0); 901 assert(returnType_.toStringDecl.length > 0); 902 assert(classification_ != MemberVirtualType.Unknown); 903 904 foreach (p; params_) { 905 assertVisit(p); 906 } 907 } 908 } 909 910 this(const USRType usr, const CppMethodName name, const CxParam[] params, const CxReturnType return_type, 911 const CppAccess access, const CppConstMethod const_, const CppVirtualMethod virtual) @safe { 912 this.usr = usr; 913 this.classification_ = virtual; 914 this.accessType_ = access; 915 this.name_ = name; 916 this.isConst_ = const_; 917 this.returnType_ = return_type; 918 919 this.params_ = params.dup; 920 921 setUniqueId(format("%s(%s)", name, paramRange.joinParamTypes)); 922 } 923 924 /// Operator with no parameters. 925 this(const USRType usr, const CppMethodName name, const CxReturnType return_type, 926 const CppAccess access, const CppConstMethod const_, const CppVirtualMethod virtual) @safe { 927 this(usr, name, CxParam[].init, return_type, access, const_, virtual); 928 } 929 930 /// Operator with no parameters and returning void. 931 this(const USRType usr, const CppMethodName name, const CppAccess access, 932 const CppConstMethod const_ = CppConstMethod(false), 933 const CppVirtualMethod virtual = CppVirtualMethod(MemberVirtualType.Normal)) @safe { 934 auto void_ = CxReturnType(makeSimple("void")); 935 this(usr, name, CxParam[].init, void_, access, const_, virtual); 936 } 937 938 void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const { 939 import std.format : formattedWrite; 940 import std.range.primitives : put; 941 942 helperPutComments(w); 943 put(w, helperVirtualPre(classification_.get)); 944 put(w, returnType_.toStringDecl); 945 put(w, " "); 946 put(w, name_); 947 formattedWrite(w, "(%s)", paramRange.joinParams); 948 put(w, helperConst(isConst)); 949 put(w, helperVirtualPost(classification_.get)); 950 put(w, ";"); 951 952 if (!usr.isNull && fmt.spec == 'u') { 953 put(w, " // "); 954 put(w, usr); 955 } 956 } 957 958 @safe: 959 mixin mixinCommentHelper; 960 mixin mixinUniqueId!size_t; 961 mixin CppMethodGeneric.Parameters; 962 mixin CppMethodGeneric.StringHelperVirtual; 963 mixin CppMethodGeneric.BaseProperties; 964 mixin CppMethodGeneric.MethodProperties; 965 966 const: 967 968 mixin(standardToString); 969 970 /// The operator type, aka in C++ the part after "operator" 971 auto op() 972 in { 973 assert(name_.get.length > 8); 974 } 975 body { 976 return CppMethodName((cast(string) name_.get)[8 .. $]); 977 } 978 } 979 980 struct CppInherit { 981 import cpptooling.data.symbol.types : FullyQualifiedNameType; 982 983 Nullable!USRType usr; 984 985 private { 986 CppAccess access_; 987 CppClassName name_; 988 CppNsStack ns; 989 } 990 991 invariant { 992 assert(name_.length > 0); 993 foreach (n; ns) { 994 assert(n.length > 0); 995 } 996 } 997 998 this(CppClassName name, CppAccess access) @safe { 999 this.name_ = name; 1000 this.access_ = access; 1001 } 1002 1003 void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const { 1004 import std.conv : to; 1005 import std.format : formattedWrite; 1006 import std.range.primitives : put; 1007 import std.string : toLower; 1008 1009 put(w, (cast(string) access_).toLower); 1010 put(w, " "); 1011 1012 foreach (a; ns) { 1013 formattedWrite(w, "%s::", a); 1014 } 1015 put(w, cast(string) name_); 1016 } 1017 1018 @safe: 1019 void put(CppNs ns) { 1020 this.ns ~= ns; 1021 } 1022 1023 auto nsRange() @nogc @safe pure nothrow inout { 1024 return ns; 1025 } 1026 1027 const: 1028 1029 mixin(standardToString); 1030 1031 auto name() { 1032 return this.name_; 1033 } 1034 1035 auto access() { 1036 return access_; 1037 } 1038 1039 FullyQualifiedNameType fullyQualifiedName() const { 1040 //TODO optimize by only calculating once. 1041 import std.algorithm : map, joiner; 1042 import std.range : chain, only; 1043 import std.conv : text; 1044 1045 // dfmt off 1046 auto r = chain(ns.payload.map!(a => cast(string) a), 1047 only(cast(string) name_)) 1048 .joiner("::") 1049 .text(); 1050 return FullyQualifiedNameType(r); 1051 // dfmt on 1052 } 1053 } 1054 1055 struct CppClass { 1056 import std.variant : Algebraic; 1057 import cpptooling.data.symbol.types : FullyQualifiedNameType; 1058 1059 static import cpptooling.data.class_classification; 1060 1061 alias CppFunc = Algebraic!(CppMethod, CppMethodOp, CppCtor, CppDtor); 1062 1063 Nullable!USRType usr; 1064 1065 private { 1066 CppClassName name_; 1067 CppInherit[] inherits_; 1068 CppNsStack reside_in_ns; 1069 1070 cpptooling.data.class_classification.State classification_; 1071 1072 CppFunc[] methods_pub; 1073 CppFunc[] methods_prot; 1074 CppFunc[] methods_priv; 1075 1076 CppClass[] classes_pub; 1077 CppClass[] classes_prot; 1078 CppClass[] classes_priv; 1079 1080 TypeKindVariable[] members_pub; 1081 TypeKindVariable[] members_prot; 1082 TypeKindVariable[] members_priv; 1083 } 1084 1085 this(const CppClassName name, const CppInherit[] inherits, const CppNsStack ns) @safe 1086 out { 1087 assert(name_.length > 0); 1088 } 1089 body { 1090 import std.array : array; 1091 import std.algorithm : map, each; 1092 1093 this.name_ = name; 1094 this.reside_in_ns = CppNsStack(ns.dup); 1095 1096 this.inherits_ = inherits.map!((a) { 1097 auto r = CppInherit(a.name, a.access); 1098 a.nsRange.each!(b => r.put(b)); 1099 return r; 1100 }).array(); 1101 1102 setUniqueId(fullyQualifiedName); 1103 } 1104 1105 //TODO remove 1106 this(const CppClassName name, const CppInherit[] inherits) @safe 1107 out { 1108 assert(name_.length > 0); 1109 } 1110 body { 1111 this(name, inherits, CppNsStack.init); 1112 } 1113 1114 //TODO remove 1115 this(const CppClassName name) @safe 1116 out { 1117 assert(name_.length > 0); 1118 } 1119 body { 1120 this(name, CppInherit[].init, CppNsStack.init); 1121 } 1122 1123 // TODO remove @safe. it isn't a requirement that the user provided Writer is @safe. 1124 void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const @safe { 1125 import std.algorithm : copy, joiner, map, each; 1126 import std.ascii : newline; 1127 import std.conv : to; 1128 import std.format : formattedWrite; 1129 import std.range : takeOne, put, save; 1130 1131 helperPutComments(w); 1132 formattedWrite(w, "class %s", name_); 1133 1134 // inheritance 1135 if (inherits_.length > 0) { 1136 formattedWrite(w, " : %s", inherits_[0]); 1137 foreach (a; inherits_[1 .. $]) { 1138 formattedWrite(w, ", %s", a); 1139 } 1140 } 1141 formattedWrite(w, " { // %s%s", to!string(classification_), newline); 1142 1143 // debug help 1144 if (!usr.isNull && fmt.spec == 'u') { 1145 put(w, " // "); 1146 put(w, usr); 1147 put(w, newline); 1148 } 1149 1150 // methods 1151 void dumpMethods(R)(ref R range, string visibility) @safe { 1152 if (range.length > 0) { 1153 formattedWrite(w, "%s\n", visibility); 1154 } 1155 1156 auto tmp_fmt = ['%', fmt.spec]; 1157 foreach (ref a; range) { 1158 put(w, " "); 1159 a.funcToString(w, tmp_fmt); 1160 put(w, newline); 1161 } 1162 } 1163 1164 dumpMethods(methods_pub, "public:"); 1165 dumpMethods(methods_prot, "protected:"); 1166 dumpMethods(methods_priv, "private:"); 1167 1168 // members 1169 void dumpMembers(R)(ref R range, string visibility) { 1170 if (range.length > 0) { 1171 formattedWrite(w, "%s\n", visibility); 1172 } 1173 1174 foreach (ref a; range) { 1175 formattedWrite(w, " %s;\n", toInternal(a)); 1176 } 1177 } 1178 1179 dumpMembers(members_pub, "public:"); 1180 dumpMembers(members_prot, "protected:"); 1181 dumpMembers(members_priv, "private:"); 1182 1183 // inner classes 1184 void dumpClasses(R)(ref R range, string visibility) { 1185 if (range.length > 0) { 1186 formattedWrite(w, "%s\n", visibility); 1187 } 1188 1189 foreach (a; range) { 1190 a.toString(w, fmt); 1191 put(w, newline); 1192 } 1193 } 1194 1195 dumpClasses(classes_pub, "public:"); 1196 dumpClasses(classes_prot, "protected:"); 1197 dumpClasses(classes_priv, "private:"); 1198 1199 // end 1200 put(w, "}; //Class:"); 1201 reside_in_ns.payload.map!(a => cast(string) a).joiner("::").copy(w); 1202 reside_in_ns.payload.takeOne.map!(a => "::").copy(w); 1203 put(w, name_); 1204 } 1205 1206 void put(T)(T func) 1207 if (is(Unqual!T == CppMethod) || is(Unqual!T == CppCtor) 1208 || is(Unqual!T == CppDtor) || is(Unqual!T == CppMethodOp)) { 1209 static if (is(Unqual!T == T)) { 1210 auto f = () @trusted { return CppFunc(func); }(); 1211 } else { 1212 // TODO remove this hack. It is unsafe. 1213 auto f = () @trusted { 1214 Unqual!T tmp; 1215 tmp = cast(Unqual!T) func; 1216 return CppFunc(tmp); 1217 }(); 1218 } 1219 1220 final switch (func.accessType) { 1221 case AccessType.Public: 1222 methods_pub ~= f; 1223 break; 1224 case AccessType.Protected: 1225 methods_prot ~= f; 1226 break; 1227 case AccessType.Private: 1228 methods_priv ~= f; 1229 break; 1230 } 1231 1232 classification_ = cpptooling.data.class_classification.classifyClass(classification_, 1233 f, cast(Flag!"hasMember")(memberRange.length > 0)); 1234 } 1235 1236 @safe: 1237 mixin mixinUniqueId!size_t; 1238 mixin mixinCommentHelper; 1239 1240 mixin(standardToString); 1241 1242 void put(CppFunc f) { 1243 static void internalPut(T)(ref T class_, CppFunc f) @trusted { 1244 import std.variant : visit; 1245 1246 // dfmt off 1247 f.visit!((CppMethod a) => class_.put(a), 1248 (CppMethodOp a) => class_.put(a), 1249 (CppCtor a) => class_.put(a), 1250 (CppDtor a) => class_.put(a)); 1251 // dfmt on 1252 } 1253 1254 internalPut(this, f); 1255 } 1256 1257 void put(T)(T class_, AccessType accessType) @trusted if (is(T == CppClass)) { 1258 final switch (accessType) { 1259 case AccessType.Public: 1260 classes_pub ~= class_; 1261 break; 1262 case AccessType.Protected: 1263 classes_prot ~= class_; 1264 break; 1265 case AccessType.Private: 1266 classes_priv ~= class_; 1267 break; 1268 } 1269 } 1270 1271 void put(T)(T member_, AccessType accessType) @trusted 1272 if (is(T == TypeKindVariable)) { 1273 final switch (accessType) { 1274 case AccessType.Public: 1275 members_pub ~= member_; 1276 break; 1277 case AccessType.Protected: 1278 members_prot ~= member_; 1279 break; 1280 case AccessType.Private: 1281 members_priv ~= member_; 1282 break; 1283 } 1284 } 1285 1286 void put(CppInherit inh) { 1287 inherits_ ~= inh; 1288 } 1289 1290 const: 1291 1292 auto inheritRange() @nogc { 1293 return inherits_; 1294 } 1295 1296 auto methodRange() @nogc { 1297 import std.range : chain; 1298 1299 return chain(methods_pub, methods_prot, methods_priv); 1300 } 1301 1302 auto methodPublicRange() @nogc { 1303 return methods_pub; 1304 } 1305 1306 auto methodProtectedRange() @nogc { 1307 return methods_prot; 1308 } 1309 1310 auto methodPrivateRange() @nogc { 1311 return methods_priv; 1312 } 1313 1314 auto classRange() @nogc { 1315 import std.range : chain; 1316 1317 return chain(classes_pub, classes_prot, classes_priv); 1318 } 1319 1320 auto classPublicRange() @nogc { 1321 return classes_pub; 1322 } 1323 1324 auto classProtectedRange() @nogc { 1325 return classes_prot; 1326 } 1327 1328 auto classPrivateRange() @nogc { 1329 return classes_priv; 1330 } 1331 1332 auto memberRange() @nogc { 1333 import std.range : chain; 1334 1335 return chain(members_pub, members_prot, members_priv); 1336 } 1337 1338 auto memberPublicRange() @nogc { 1339 return members_pub; 1340 } 1341 1342 auto memberProtectedRange() @nogc { 1343 return members_prot; 1344 } 1345 1346 auto memberPrivateRange() @nogc { 1347 return members_priv; 1348 } 1349 1350 /** Traverse stack from top to bottom. 1351 * The implementation of the stack is such that new elements are appended 1352 * to the end. Therefor the range normal direction is from the end of the 1353 * array to the beginning. 1354 */ 1355 auto nsNestingRange() @nogc { 1356 import std.range : retro; 1357 1358 return reside_in_ns.payload.retro; 1359 } 1360 1361 auto commentRange() @nogc { 1362 return comments; 1363 } 1364 1365 invariant() { 1366 //assert(usr.isNull || usr.length > 0); 1367 foreach (i; inherits_) { 1368 assert(i.name.length > 0); 1369 } 1370 } 1371 1372 bool isVirtual() { 1373 import std.algorithm : among; 1374 1375 with (cpptooling.data.class_classification.State) { 1376 return classification_.among(Virtual, VirtualDtor, Abstract, Pure) != 0; 1377 } 1378 } 1379 1380 bool isAbstract() { 1381 with (cpptooling.data.class_classification.State) { 1382 return classification_ == Abstract; 1383 } 1384 } 1385 1386 bool isPure() { 1387 import std.algorithm : among; 1388 1389 with (cpptooling.data.class_classification.State) { 1390 return classification_.among(VirtualDtor, Pure) != 0; 1391 } 1392 } 1393 1394 auto classification() { 1395 return classification_; 1396 } 1397 1398 auto name() { 1399 return name_; 1400 } 1401 1402 auto inherits() { 1403 return inherits_; 1404 } 1405 1406 auto resideInNs() { 1407 return reside_in_ns; 1408 } 1409 1410 FullyQualifiedNameType fullyQualifiedName() { 1411 //TODO optimize by only calculating once. 1412 1413 import std.array : array; 1414 import std.algorithm : map, joiner; 1415 import std.range : takeOne, only, chain, takeOne; 1416 import std.utf : byChar; 1417 1418 // dfmt off 1419 auto fqn = chain( 1420 reside_in_ns.payload.map!(a => cast(string) a).joiner("::"), 1421 reside_in_ns.payload.takeOne.map!(a => "::").joiner(), 1422 only(cast(string) name_).joiner() 1423 ); 1424 return FullyQualifiedNameType(fqn.byChar.array().idup); 1425 // dfmt on 1426 } 1427 } 1428 1429 /// Dictates how the namespaces are merged. 1430 enum MergeMode { 1431 /// Merge everything except nested namespaces. 1432 shallow, 1433 /// Merge everything. 1434 full 1435 } 1436 1437 @safe struct CppNamespace { 1438 import std.container : RedBlackTree; 1439 import cpptooling.data.symbol.types : FullyQualifiedNameType; 1440 1441 mixin mixinUniqueId!size_t; 1442 1443 private { 1444 CppNs name_; 1445 1446 CppNsStack stack; 1447 1448 CppClass[] classes; 1449 CppNamespace[] namespaces; 1450 RedBlackTree!(SortByString!CxGlobalVariable, "a.id < b.id") globals; 1451 RedBlackTree!(SortByString!CFunction, "a.id < b.id") funcs; 1452 } 1453 1454 static auto makeAnonymous() nothrow { 1455 auto rval = CppNamespace(CppNsStack.init); 1456 rval.setUniqueId(makeUniqueUSR); 1457 return rval; 1458 } 1459 1460 /// A namespace without any nesting. 1461 static auto make(CppNs name) nothrow { 1462 auto rval = CppNamespace(CppNsStack([name])); 1463 return rval; 1464 } 1465 1466 this(const CppNsStack stack) nothrow { 1467 import std.algorithm : joiner; 1468 import std.container : make; 1469 import std.digest.crc : crc32Of; 1470 import std.utf : byChar; 1471 1472 this.stack = CppNsStack(stack.dup); 1473 this.globals = make!(typeof(this.globals)); 1474 this.funcs = make!(typeof(this.funcs)); 1475 1476 if (stack.length > 0) { 1477 this.name_ = stack[$ - 1]; 1478 1479 try { 1480 ubyte[4] hash = () @trusted { 1481 return this.stack.joiner.byChar.crc32Of(); 1482 }(); 1483 this.id_ = ((hash[0] << 24) | (hash[1] << 16) | (hash[2] << 8) | hash[3]); 1484 } catch (Exception ex) { 1485 this.setUniqueId(makeUniqueUSR); 1486 } 1487 } else { 1488 // anonymous namespace 1489 this.setUniqueId(makeUniqueUSR); 1490 } 1491 1492 } 1493 1494 void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const { 1495 import std.algorithm : map, joiner; 1496 import std.ascii : newline; 1497 import std.format : formattedWrite; 1498 import std.meta : AliasSeq; 1499 import std.range : takeOne, retro, put; 1500 1501 auto ns_top_name = stack.payload.retro.takeOne.map!(a => cast(string) a).joiner(); 1502 auto ns_full_name = stack.payload.map!(a => cast(string) a).joiner("::"); 1503 1504 formattedWrite(w, "namespace %s { //%s", ns_top_name, ns_full_name); 1505 1506 if (fmt.spec == 'u') { 1507 formattedWrite(w, " %s", id); 1508 } 1509 1510 put(w, newline); 1511 1512 foreach (range; AliasSeq!("globals[]", "funcs[]", "classes", "namespaces")) { 1513 foreach (a; mixin(range)) { 1514 a.toString(w, fmt); 1515 put(w, newline); 1516 } 1517 } 1518 1519 formattedWrite(w, "} //NS:%s", ns_top_name); 1520 } 1521 1522 /** Merge the content of other_ns into this. 1523 * 1524 * The namespaces do NOT become nested. Use `put` instead. 1525 * 1526 * The order of the items are preserved. 1527 * The items are deduplicated via the `id` attribute. 1528 * 1529 * Implemented to be cheap but we aware that after this operation the two 1530 * namespaces will point to the same elements. A mutation in one of them 1531 * will affect both. 1532 */ 1533 void merge(ref CppNamespace other_ns, MergeMode mode) @safe pure nothrow { 1534 import std.meta : AliasSeq; 1535 1536 foreach (kind; AliasSeq!("funcRange", "globalRange")) { 1537 foreach (ref item; __traits(getMember, other_ns, kind)) { 1538 put(item); 1539 } 1540 } 1541 1542 // not a RedBlackTree so must ensure deduplication via a AA 1543 1544 bool[size_t] exists; 1545 foreach (ref item; classRange) { 1546 exists[item.id] = true; 1547 } 1548 1549 // only copy items from other NS that are NOT in this NS. 1550 // assumption: two items with the same ID are the same content wise. 1551 foreach (ref item; other_ns.classRange) { 1552 if (item.id !in exists) { 1553 put(item); 1554 } 1555 } 1556 1557 if (mode == MergeMode.full) { 1558 mergeRecursive(other_ns); 1559 } 1560 } 1561 1562 private void mergeRecursive(ref CppNamespace other_ns) @safe pure nothrow { 1563 void slowMerge(ref CppNamespace other_ns) @safe pure nothrow { 1564 foreach (ref item; namespaceRange) { 1565 if (item.id == other_ns.id) { 1566 item.merge(other_ns, MergeMode.full); 1567 return; 1568 } 1569 } 1570 1571 // should NEVER happen. If it happens then some mutation has 1572 // happened in parallel. 1573 // It has already been proven via exists that the namespace exist 1574 // among the namespaces this object have. 1575 assert(0); 1576 } 1577 1578 bool[size_t] exists; 1579 1580 foreach (ref item; namespaceRange) { 1581 exists[item.id] = true; 1582 } 1583 1584 foreach (ref item; other_ns.namespaceRange) { 1585 if (item.id in exists) { 1586 slowMerge(item); 1587 } else { 1588 this.put(item); 1589 } 1590 } 1591 } 1592 1593 /// Put item in storage. 1594 void put(CFunction f) pure nothrow { 1595 auto tmp = SortByString!CFunction(f, f.usr.get); 1596 1597 try { 1598 () @trusted pure{ funcs.insert(tmp); }(); 1599 } catch (Exception ex) { 1600 } 1601 } 1602 1603 /// ditto 1604 void put(CppClass s) pure nothrow { 1605 classes ~= s; 1606 } 1607 1608 /// ditto 1609 void put(CppNamespace ns) pure nothrow { 1610 // TODO this is slow. 1611 1612 foreach (ref item; namespaceRange) { 1613 if (item.id == ns.id) { 1614 item.merge(ns, MergeMode.full); 1615 return; 1616 } 1617 } 1618 1619 namespaces ~= ns; 1620 } 1621 1622 /// ditto 1623 void put(CxGlobalVariable g) pure nothrow { 1624 auto tmp = SortByString!CxGlobalVariable(g, g.name); 1625 1626 try { 1627 () @trusted pure{ globals.insert(tmp); }(); 1628 } catch (Exception ex) { 1629 } 1630 } 1631 1632 /** Range of the fully qualified name starting from the top. 1633 * 1634 * The top is THIS namespace. 1635 * So A::B::C would be a range of [C, B, A]. 1636 */ 1637 auto nsNestingRange() @nogc pure nothrow { 1638 import std.range : retro; 1639 1640 return stack.payload.retro; 1641 } 1642 1643 /// Range data of symbols residing in this namespace. 1644 auto classRange() @nogc pure nothrow { 1645 return classes; 1646 } 1647 1648 /// Range of free functions residing in this namespace. 1649 auto funcRange() @nogc pure nothrow { 1650 return funcs[]; 1651 } 1652 1653 /// Range of namespaces residing in this namespace. 1654 auto namespaceRange() @nogc pure nothrow { 1655 return namespaces; 1656 } 1657 1658 /// Global variables residing in this namespace. 1659 auto globalRange() @nogc pure nothrow { 1660 return globals[]; 1661 } 1662 1663 const: 1664 1665 mixin(standardToString); 1666 1667 /// If the namespace is anonymous, aka has no name. 1668 bool isAnonymous() pure nothrow { 1669 return name_.length == 0; 1670 } 1671 1672 /// Returns: True if completely empty. 1673 bool empty() pure nothrow { 1674 return !(classes.length || funcs.length || namespaces.length || globals.length); 1675 } 1676 1677 /// Name of the namespace 1678 auto name() pure nothrow { 1679 return name_; 1680 } 1681 1682 /** Range representation of the fully qualified name. 1683 * 1684 * TODO change function name, it is the full stack. So fully qualified 1685 * name. 1686 */ 1687 auto resideInNs() pure nothrow { 1688 return stack; 1689 } 1690 1691 /** The fully qualified name of where the namespace reside. 1692 * 1693 * Example of FQN for C could be A::B::C. 1694 */ 1695 auto fullyQualifiedName() pure { 1696 //TODO optimize by only calculating once. 1697 1698 import std.array : array; 1699 import std.algorithm : map, joiner; 1700 import std.utf : byChar; 1701 1702 // dfmt off 1703 auto fqn = stack.payload.map!(a => cast(string) a).joiner("::"); 1704 return FullyQualifiedNameType(fqn.byChar.array().idup); 1705 // dfmt on 1706 } 1707 } 1708 1709 /// Use to get sorting in e.g. a binary tree by a string. 1710 private struct SortByString(T) { 1711 mixin mixinUniqueId!string; 1712 T payload; 1713 alias payload this; 1714 1715 this(T a, string sort_by) { 1716 payload = a; 1717 setUniqueId(sort_by); 1718 } 1719 } 1720 1721 /** The root of the data structure of the semantic representation of the 1722 * analyzed C++ source. 1723 */ 1724 struct CppRoot { 1725 import std.container : RedBlackTree; 1726 1727 private { 1728 CppNamespace[] ns; 1729 CppClass[] classes; 1730 RedBlackTree!(SortByString!CxGlobalVariable, "a.id < b.id") globals; 1731 RedBlackTree!(SortByString!CFunction, "a.id < b.id") funcs; 1732 } 1733 1734 /// Returns: An initialized CppRootX 1735 static auto make() @safe { 1736 import std.container : make; 1737 1738 CppRoot r; 1739 r.globals = make!(typeof(this.globals)); 1740 r.funcs = make!(typeof(this.funcs)); 1741 1742 return r; 1743 } 1744 1745 /// Recrusive stringify the content for human readability. 1746 void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const { 1747 import std.ascii : newline; 1748 import std.meta : AliasSeq; 1749 import std.range : put; 1750 1751 foreach (range; AliasSeq!("globals[]", "funcs[]", "classes", "ns")) { 1752 foreach (a; mixin(range)) { 1753 a.toString(w, fmt); 1754 put(w, newline); 1755 } 1756 } 1757 } 1758 1759 @safe: 1760 1761 /** Merge the roots. 1762 * 1763 * Implemented to be cheap but we aware that after this operation the two 1764 * root's will point to the same elements. A mutation in one of them will 1765 * affect both. 1766 */ 1767 void merge(ref CppRoot root, MergeMode mode) pure nothrow { 1768 import std.meta : AliasSeq; 1769 1770 foreach (kind; AliasSeq!("funcRange", "globalRange")) { 1771 foreach (ref item; __traits(getMember, root, kind)) { 1772 put(item); 1773 } 1774 } 1775 1776 // not a RedBlackTree so must ensure deduplication via a AA 1777 1778 bool[size_t] exists; 1779 foreach (ref item; classRange) { 1780 exists[item.id] = true; 1781 } 1782 1783 foreach (ref item; root.classRange) { 1784 if (item.id !in exists) { 1785 put(item); 1786 } 1787 } 1788 1789 if (mode == MergeMode.full) { 1790 mergeRecursive(root); 1791 } 1792 } 1793 1794 private void mergeRecursive(ref CppRoot root) @safe pure nothrow { 1795 void slowMerge(ref CppNamespace other_ns) @safe pure nothrow { 1796 foreach (ref item; namespaceRange) { 1797 if (item.id == other_ns.id) { 1798 item.merge(other_ns, MergeMode.full); 1799 return; 1800 } 1801 } 1802 1803 // should NEVER happen. If it happens then some mutation has 1804 // happened in parallel. 1805 // It has already been proven via exists that the namespace exist 1806 // among the namespaces this object have. 1807 assert(0); 1808 } 1809 1810 bool[size_t] exists; 1811 1812 foreach (ref item; namespaceRange) { 1813 exists[item.id] = true; 1814 } 1815 1816 foreach (ref item; root.namespaceRange) { 1817 if (item.id in exists) { 1818 slowMerge(item); 1819 } else { 1820 this.put(item); 1821 } 1822 } 1823 } 1824 1825 /// Put item in storage. 1826 void put(CFunction f) pure nothrow { 1827 auto tmp = SortByString!CFunction(f, f.usr.get); 1828 1829 try { 1830 () @trusted pure{ funcs.insert(tmp); }(); 1831 } catch (Exception ex) { 1832 } 1833 } 1834 1835 /// ditto 1836 void put(CppClass s) pure nothrow { 1837 classes ~= s; 1838 } 1839 1840 /// ditto 1841 void put(CppNamespace ns) pure nothrow { 1842 // TODO this is slow. 1843 1844 foreach (ref item; namespaceRange) { 1845 if (item.id == ns.id) { 1846 item.merge(ns, MergeMode.full); 1847 return; 1848 } 1849 } 1850 1851 this.ns ~= ns; 1852 } 1853 1854 /// ditto 1855 void put(CxGlobalVariable g) pure nothrow { 1856 auto tmp = SortByString!CxGlobalVariable(g, g.name); 1857 1858 try { 1859 () @trusted pure{ globals.insert(tmp); }(); 1860 } catch (Exception ex) { 1861 } 1862 } 1863 1864 /// Range of contained data. 1865 auto namespaceRange() @nogc { 1866 return ns; 1867 } 1868 1869 /// ditto 1870 auto classRange() @nogc { 1871 return classes; 1872 } 1873 1874 /// ditto 1875 auto funcRange() @nogc { 1876 return funcs[]; 1877 } 1878 1879 /// ditto 1880 auto globalRange() @nogc { 1881 return globals[]; 1882 } 1883 1884 /// Cast to string representation 1885 T opCast(T : string)() const { 1886 return this.toString; 1887 } 1888 1889 mixin(standardToString); 1890 } 1891 1892 @("Test of c-function") 1893 unittest { 1894 { // simple version, no return or parameters. 1895 auto f = CFunction(dummyUSR, CFunctionName("nothing")); 1896 shouldEqual(f.returnType.toStringDecl("x"), "void x"); 1897 shouldEqual(format("%u", f), "void nothing(); // None dummyUSR"); 1898 } 1899 1900 { // extern storage. 1901 auto f = CFunction(dummyUSR, CFunctionName("nothing"), [], 1902 CxReturnType(makeSimple("void")), VariadicType.no, StorageClass.Extern); 1903 shouldEqual(f.returnType.toStringDecl("x"), "void x"); 1904 shouldEqual(format("%u", f), "void nothing(); // Extern dummyUSR"); 1905 } 1906 1907 { // a return type. 1908 auto f = CFunction(dummyUSR, CFunctionName("nothing"), CxReturnType(makeSimple("int"))); 1909 shouldEqual(format("%u", f), "int nothing(); // None dummyUSR"); 1910 } 1911 1912 { // return type and parameters. 1913 auto p0 = makeCxParam(TypeKindVariable(makeSimple("int"), CppVariable("x"))); 1914 auto p1 = makeCxParam(TypeKindVariable(makeSimple("char"), CppVariable("y"))); 1915 auto f = CFunction(dummyUSR, CFunctionName("nothing"), [p0, p1], 1916 CxReturnType(makeSimple("int")), VariadicType.no, StorageClass.None); 1917 shouldEqual(format("%u", f), "int nothing(int x, char y); // None dummyUSR"); 1918 } 1919 } 1920 1921 @("Test of creating simples CppMethod") 1922 unittest { 1923 auto m = CppMethod(dummyUSR, CppMethodName("voider"), CppAccess(AccessType.Public)); 1924 shouldEqual(m.isConst, false); 1925 shouldEqual(m.classification, MemberVirtualType.Normal); 1926 shouldEqual(m.name, "voider"); 1927 shouldEqual(m.params_.length, 0); 1928 shouldEqual(m.returnType.toStringDecl("x"), "void x"); 1929 shouldEqual(m.accessType, AccessType.Public); 1930 } 1931 1932 @("Test creating a CppMethod with multiple parameters") 1933 unittest { 1934 auto tk = makeSimple("char*"); 1935 tk.attr.isPtr = Yes.isPtr; 1936 auto p = CxParam(TypeKindVariable(tk, CppVariable("x"))); 1937 1938 auto m = CppMethod(dummyUSR, CppMethodName("none"), [p, p], CxReturnType(tk), 1939 CppAccess(AccessType.Public), CppConstMethod(true), 1940 CppVirtualMethod(MemberVirtualType.Virtual)); 1941 1942 shouldEqual(format("%u", m), "virtual char* none(char* x, char* x) const; // dummyUSR"); 1943 } 1944 1945 @("should represent the operator as a string") 1946 unittest { 1947 auto m = CppMethodOp(dummyUSR, CppMethodName("operator="), CppAccess(AccessType.Public)); 1948 1949 shouldEqual(format("%u", m), "void operator=(); // dummyUSR"); 1950 } 1951 1952 @("should separate the operator keyword from the actual operator") 1953 unittest { 1954 auto m = CppMethodOp(dummyUSR, CppMethodName("operator="), CppAccess(AccessType.Public)); 1955 1956 shouldEqual(m.op, "="); 1957 } 1958 1959 @("should represent a class with one public method") 1960 unittest { 1961 auto c = CppClass(CppClassName("Foo")); 1962 auto m = CppMethod(dummyUSR, CppMethodName("voider"), CppAccess(AccessType.Public)); 1963 c.put(m); 1964 shouldEqual(c.methods_pub.length, 1); 1965 shouldEqual(format("%u", c), "class Foo { // Normal 1966 public: 1967 void voider(); // dummyUSR 1968 }; //Class:Foo"); 1969 } 1970 1971 @("should represent a class with one public operator overload") 1972 unittest { 1973 auto c = CppClass(CppClassName("Foo")); 1974 auto op = CppMethodOp(dummyUSR, CppMethodName("operator="), CppAccess(AccessType.Public)); 1975 c.put(op); 1976 1977 shouldEqual(format("%u", c), "class Foo { // Normal 1978 public: 1979 void operator=(); // dummyUSR 1980 }; //Class:Foo"); 1981 } 1982 1983 @("Create an anonymous namespace struct") 1984 unittest { 1985 auto n = CppNamespace(CppNsStack.init); 1986 shouldEqual(n.name.length, 0); 1987 shouldEqual(n.isAnonymous, true); 1988 } 1989 1990 @("Create a namespace struct two deep") 1991 unittest { 1992 auto stack = CppNsStack([CppNs("foo"), CppNs("bar")]); 1993 auto n = CppNamespace(stack); 1994 shouldEqual(cast(string) n.name, "bar"); 1995 shouldEqual(n.isAnonymous, false); 1996 } 1997 1998 @("Test of iterating over parameters in a class") 1999 unittest { 2000 import std.array : appender; 2001 2002 auto c = CppClass(CppClassName("Foo")); 2003 auto m = CppMethod(dummyUSR, CppMethodName("voider"), CppAccess(AccessType.Public)); 2004 c.put(m); 2005 2006 auto app = appender!string(); 2007 2008 foreach (d; c.methodRange) { 2009 d.funcToString(app, "%u"); 2010 } 2011 2012 shouldEqual(app.data, "void voider(); // dummyUSR"); 2013 } 2014 2015 @("Test of toString for a free function") 2016 unittest { 2017 auto ptk = makeSimple("char*"); 2018 ptk.attr.isPtr = Yes.isPtr; 2019 auto rtk = makeSimple("int"); 2020 auto f = CFunction(dummyUSR, CFunctionName("nothing"), [ 2021 makeCxParam(TypeKindVariable(ptk, CppVariable("x"))), 2022 makeCxParam(TypeKindVariable(ptk, CppVariable("y"))) 2023 ], CxReturnType(rtk), VariadicType.no, StorageClass.None); 2024 2025 shouldEqual(format("%u", f), "int nothing(char* x, char* y); // None dummyUSR"); 2026 } 2027 2028 @("Test of Ctor's") 2029 unittest { 2030 auto tk = makeSimple("char*"); 2031 tk.attr.isPtr = Yes.isPtr; 2032 auto p = CxParam(TypeKindVariable(tk, CppVariable("x"))); 2033 2034 auto ctor = CppCtor(dummyUSR, CppMethodName("ctor"), [p, p], CppAccess(AccessType.Public)); 2035 2036 shouldEqual(format("%u", ctor), "ctor(char* x, char* x); // dummyUSR"); 2037 } 2038 2039 @("Test of Dtor's") 2040 unittest { 2041 auto dtor = CppDtor(dummyUSR, CppMethodName("~dtor"), 2042 CppAccess(AccessType.Public), CppVirtualMethod(MemberVirtualType.Virtual)); 2043 2044 shouldEqual(format("%u", dtor), "virtual ~dtor(); // dummyUSR"); 2045 } 2046 2047 @("Test of toString for CppClass") 2048 unittest { 2049 auto c = CppClass(CppClassName("Foo")); 2050 c.put(CppMethod(dummyUSR, CppMethodName("voider"), CppAccess(AccessType.Public))); 2051 2052 { 2053 auto m = CppCtor(dummyUSR, CppMethodName("Foo"), CxParam[].init, 2054 CppAccess(AccessType.Public)); 2055 c.put(m); 2056 } 2057 2058 { 2059 auto tk = makeSimple("int"); 2060 auto m = CppMethod(dummyUSR, CppMethodName("fun"), CxReturnType(tk), 2061 CppAccess(AccessType.Protected), CppConstMethod(false), 2062 CppVirtualMethod(MemberVirtualType.Pure)); 2063 c.put(m); 2064 } 2065 2066 { 2067 auto tk = makeSimple("char*"); 2068 tk.attr.isPtr = Yes.isPtr; 2069 auto m = CppMethod(dummyUSR, CppMethodName("gun"), CxReturnType(tk), CppAccess(AccessType.Private), 2070 CppConstMethod(false), CppVirtualMethod(MemberVirtualType.Normal)); 2071 m.put(CxParam(TypeKindVariable(makeSimple("int"), CppVariable("x")))); 2072 m.put(CxParam(TypeKindVariable(makeSimple("int"), CppVariable("y")))); 2073 c.put(m); 2074 } 2075 2076 { 2077 auto tk = makeSimple("int"); 2078 auto m = CppMethod(dummyUSR, CppMethodName("wun"), CxReturnType(tk), 2079 CppAccess(AccessType.Public), CppConstMethod(true), 2080 CppVirtualMethod(MemberVirtualType.Normal)); 2081 c.put(m); 2082 } 2083 2084 shouldEqual(format("%u", c), "class Foo { // Abstract 2085 public: 2086 void voider(); // dummyUSR 2087 Foo(); // dummyUSR 2088 int wun() const; // dummyUSR 2089 protected: 2090 virtual int fun() = 0; // dummyUSR 2091 private: 2092 char* gun(int x, int y); // dummyUSR 2093 }; //Class:Foo"); 2094 } 2095 2096 @("should be a class in a ns in the comment") 2097 unittest { 2098 auto ns = CppNsStack([CppNs("a_ns"), CppNs("another_ns")]); 2099 auto c = CppClass(CppClassName("A_Class"), CppInherit[].init, ns); 2100 2101 shouldEqual(c.toString, "class A_Class { // Unknown 2102 }; //Class:a_ns::another_ns::A_Class"); 2103 } 2104 2105 @("should contain the inherited classes") 2106 unittest { 2107 CppInherit[] inherit; 2108 inherit ~= CppInherit(CppClassName("pub"), CppAccess(AccessType.Public)); 2109 inherit ~= CppInherit(CppClassName("prot"), CppAccess(AccessType.Protected)); 2110 inherit ~= CppInherit(CppClassName("priv"), CppAccess(AccessType.Private)); 2111 2112 auto c = CppClass(CppClassName("Foo"), inherit); 2113 2114 shouldEqual(c.toString, "class Foo : public pub, protected prot, private priv { // Unknown 2115 }; //Class:Foo"); 2116 } 2117 2118 @("should contain nested classes") 2119 unittest { 2120 auto c = CppClass(CppClassName("Foo")); 2121 2122 c.put(CppClass(CppClassName("Pub")), AccessType.Public); 2123 c.put(CppClass(CppClassName("Prot")), AccessType.Protected); 2124 c.put(CppClass(CppClassName("Priv")), AccessType.Private); 2125 2126 shouldEqual(c.toString, "class Foo { // Unknown 2127 public: 2128 class Pub { // Unknown 2129 }; //Class:Pub 2130 protected: 2131 class Prot { // Unknown 2132 }; //Class:Prot 2133 private: 2134 class Priv { // Unknown 2135 }; //Class:Priv 2136 }; //Class:Foo"); 2137 } 2138 2139 @("should be a virtual class") 2140 unittest { 2141 auto c = CppClass(CppClassName("Foo")); 2142 2143 { 2144 auto m = CppCtor(dummyUSR, CppMethodName("Foo"), CxParam[].init, 2145 CppAccess(AccessType.Public)); 2146 c.put(m); 2147 } 2148 { 2149 auto m = CppDtor(dummyUSR, CppMethodName("~Foo"), 2150 CppAccess(AccessType.Public), CppVirtualMethod(MemberVirtualType.Virtual)); 2151 c.put(m); 2152 } 2153 { 2154 auto m = CppMethod(dummyUSR, CppMethodName("wun"), CxReturnType(makeSimple("int")), 2155 CppAccess(AccessType.Public), CppConstMethod(false), 2156 CppVirtualMethod(MemberVirtualType.Virtual)); 2157 c.put(m); 2158 } 2159 2160 shouldEqual(format("%u", c), "class Foo { // Virtual 2161 public: 2162 Foo(); // dummyUSR 2163 virtual ~Foo(); // dummyUSR 2164 virtual int wun(); // dummyUSR 2165 }; //Class:Foo"); 2166 } 2167 2168 @("should be a pure virtual class") 2169 unittest { 2170 auto c = CppClass(CppClassName("Foo")); 2171 2172 { 2173 auto m = CppCtor(dummyUSR, CppMethodName("Foo"), CxParam[].init, 2174 CppAccess(AccessType.Public)); 2175 c.put(m); 2176 } 2177 { 2178 auto m = CppDtor(dummyUSR, CppMethodName("~Foo"), 2179 CppAccess(AccessType.Public), CppVirtualMethod(MemberVirtualType.Virtual)); 2180 c.put(m); 2181 } 2182 { 2183 auto m = CppMethod(dummyUSR, CppMethodName("wun"), CxReturnType(makeSimple("int")), 2184 CppAccess(AccessType.Public), CppConstMethod(false), 2185 CppVirtualMethod(MemberVirtualType.Pure)); 2186 c.put(m); 2187 } 2188 2189 shouldEqual(format("%u", c), "class Foo { // Pure 2190 public: 2191 Foo(); // dummyUSR 2192 virtual ~Foo(); // dummyUSR 2193 virtual int wun() = 0; // dummyUSR 2194 }; //Class:Foo"); 2195 } 2196 2197 @("Test of toString for CppNamespace") 2198 unittest { 2199 auto ns = CppNamespace.make(CppNs("simple")); 2200 2201 auto c = CppClass(CppClassName("Foo")); 2202 c.put(CppMethod(dummyUSR, CppMethodName("voider"), CppAccess(AccessType.Public))); 2203 ns.put(c); 2204 2205 shouldEqual(format("%s", ns), "namespace simple { //simple 2206 class Foo { // Normal 2207 public: 2208 void voider(); 2209 }; //Class:Foo 2210 } //NS:simple"); 2211 } 2212 2213 @("Should show nesting of namespaces as valid C++ code") 2214 unittest { 2215 auto stack = CppNsStack([CppNs("foo"), CppNs("bar")]); 2216 auto n = CppNamespace(stack); 2217 shouldEqual(n.toString, "namespace bar { //foo::bar 2218 } //NS:bar"); 2219 } 2220 2221 @("Test of toString for CppRoot") 2222 unittest { 2223 auto root = CppRoot.make(); 2224 2225 { // free function 2226 auto f = CFunction(dummyUSR, CFunctionName("nothing")); 2227 root.put(f); 2228 } 2229 2230 auto c = CppClass(CppClassName("Foo")); 2231 auto m = CppMethod(dummyUSR, CppMethodName("voider"), CppAccess(AccessType.Public)); 2232 c.put(m); 2233 root.put(c); 2234 2235 root.put(CppNamespace.make(CppNs("simple"))); 2236 2237 shouldEqual(format("%s", root), "void nothing(); // None 2238 class Foo { // Normal 2239 public: 2240 void voider(); 2241 }; //Class:Foo 2242 namespace simple { //simple 2243 } //NS:simple 2244 "); 2245 } 2246 2247 @("CppNamespace.toString should return nested namespace") 2248 unittest { 2249 auto stack = [CppNs("Depth1"), CppNs("Depth2"), CppNs("Depth3")]; 2250 auto depth1 = CppNamespace(CppNsStack(stack[0 .. 1])); 2251 auto depth2 = CppNamespace(CppNsStack(stack[0 .. 2])); 2252 auto depth3 = CppNamespace(CppNsStack(stack[0 .. $])); 2253 2254 depth2.put(depth3); 2255 depth1.put(depth2); 2256 2257 shouldEqual(depth1.toString, "namespace Depth1 { //Depth1 2258 namespace Depth2 { //Depth1::Depth2 2259 namespace Depth3 { //Depth1::Depth2::Depth3 2260 } //NS:Depth3 2261 } //NS:Depth2 2262 } //NS:Depth1"); 2263 } 2264 2265 @("Create anonymous namespace") 2266 unittest { 2267 auto n = CppNamespace.makeAnonymous(); 2268 2269 shouldEqual(n.toString, "namespace { // 2270 } //NS:"); 2271 } 2272 2273 @("Add a C-func to a namespace") 2274 unittest { 2275 auto n = CppNamespace.makeAnonymous(); 2276 auto f = CFunction(dummyUSR, CFunctionName("nothing")); 2277 n.put(f); 2278 2279 shouldEqual(format("%s", n), "namespace { // 2280 void nothing(); // None 2281 } //NS:"); 2282 } 2283 2284 @("should be a hash value based on string representation") 2285 unittest { 2286 struct A { 2287 mixin mixinUniqueId!size_t; 2288 this(bool fun) { 2289 setUniqueId("foo"); 2290 } 2291 } 2292 2293 auto a = A(true); 2294 auto b = A(true); 2295 2296 shouldBeGreaterThan(a.id(), 0); 2297 shouldEqual(a.id(), b.id()); 2298 } 2299 2300 @("should be a global definition") 2301 unittest { 2302 auto v0 = CxGlobalVariable(dummyUSR, TypeKindVariable(makeSimple("int"), CppVariable("x"))); 2303 auto v1 = CxGlobalVariable(dummyUSR, makeSimple("int"), CppVariable("y")); 2304 2305 shouldEqual(format("%u", v0), "int x; // dummyUSR"); 2306 shouldEqual(format("%u", v1), "int y; // dummyUSR"); 2307 } 2308 2309 @("Should be globals stored in the root object") 2310 unittest { 2311 auto v = CxGlobalVariable(dummyUSR, TypeKindVariable(makeSimple("int"), CppVariable("x"))); 2312 auto n = CppNamespace.makeAnonymous(); 2313 auto r = CppRoot.make(); 2314 n.put(v); 2315 r.put(v); 2316 r.put(n); 2317 2318 shouldEqual(format("%s", r), "int x; 2319 namespace { // 2320 int x; 2321 } //NS: 2322 "); 2323 } 2324 2325 @("should be possible to sort the data structures") 2326 unittest { 2327 import std.array : array; 2328 2329 auto v0 = CxGlobalVariable(dummyUSR, TypeKindVariable(makeSimple("int"), CppVariable("x"))); 2330 auto v1 = CxGlobalVariable(dummyUSR, TypeKindVariable(makeSimple("int"), CppVariable("x"))); 2331 auto r = CppRoot.make(); 2332 r.put(v0); 2333 r.put(v1); 2334 r.put(v0); 2335 2336 auto s = r.globalRange; 2337 shouldEqual(s.array().length, 1); 2338 } 2339 2340 @("should be proper access specifiers for a inherit reference, no nesting") 2341 unittest { 2342 auto ih = CppInherit(CppClassName("Class"), CppAccess(AccessType.Public)); 2343 shouldEqual("public Class", ih.toString); 2344 2345 ih = CppInherit(CppClassName("Class"), CppAccess(AccessType.Protected)); 2346 shouldEqual("protected Class", ih.toString); 2347 2348 ih = CppInherit(CppClassName("Class"), CppAccess(AccessType.Private)); 2349 shouldEqual("private Class", ih.toString); 2350 } 2351 2352 @("should be a inheritances of a class in namespaces") 2353 unittest { 2354 auto ih = CppInherit(CppClassName("Class"), CppAccess(AccessType.Public)); 2355 ih.put(CppNs("ns1")); 2356 ih.toString.shouldEqual("public ns1::Class"); 2357 2358 ih.put(CppNs("ns2")); 2359 ih.toString.shouldEqual("public ns1::ns2::Class"); 2360 2361 ih.put(CppNs("ns3")); 2362 ih.toString.shouldEqual("public ns1::ns2::ns3::Class"); 2363 } 2364 2365 @("should be a class that inherits") 2366 unittest { 2367 auto ih = CppInherit(CppClassName("Class"), CppAccess(AccessType.Public)); 2368 ih.put(CppNs("ns1")); 2369 2370 auto c = CppClass(CppClassName("A")); 2371 c.put(ih); 2372 2373 shouldEqual(c.toString, "class A : public ns1::Class { // Unknown 2374 }; //Class:A"); 2375 } 2376 2377 @("Should be a class with a data member") 2378 unittest { 2379 auto c = CppClass(CppClassName("Foo")); 2380 auto tk = makeSimple("int"); 2381 c.put(TypeKindVariable(tk, CppVariable("x")), AccessType.Public); 2382 2383 shouldEqual(c.toString, "class Foo { // Unknown 2384 public: 2385 int x; 2386 }; //Class:Foo"); 2387 } 2388 2389 @("Should be an abstract class") 2390 unittest { 2391 auto c = CppClass(CppClassName("Foo")); 2392 2393 { 2394 auto m = CppDtor(dummyUSR, CppMethodName("~Foo"), 2395 CppAccess(AccessType.Public), CppVirtualMethod(MemberVirtualType.Normal)); 2396 c.put(m); 2397 } 2398 { 2399 auto m = CppMethod(dummyUSR, CppMethodName("wun"), CppAccess(AccessType.Public), 2400 CppConstMethod(false), CppVirtualMethod(MemberVirtualType.Pure)); 2401 c.put(m); 2402 } 2403 { 2404 auto m = CppMethod(dummyUSR, CppMethodName("gun"), CppAccess(AccessType.Public), 2405 CppConstMethod(false), CppVirtualMethod(MemberVirtualType.Virtual)); 2406 c.put(m); 2407 } 2408 2409 shouldEqual(format("%u", c), "class Foo { // Abstract 2410 public: 2411 ~Foo(); // dummyUSR 2412 virtual void wun() = 0; // dummyUSR 2413 virtual void gun(); // dummyUSR 2414 }; //Class:Foo"); 2415 2416 } 2417 2418 @("Should be a class with comments") 2419 unittest { 2420 auto c = CppClass(CppClassName("Foo")); 2421 c.comment("A comment"); 2422 2423 shouldEqual(c.toString, "// A comment 2424 class Foo { // Unknown 2425 }; //Class:Foo"); 2426 } 2427 2428 @("It is a c'tor with a multiline comment") 2429 unittest { 2430 auto ctor = CppCtor(dummyUSR, CppMethodName("Foo"), CxParam[].init, 2431 CppAccess(AccessType.Public)); 2432 ctor.comment("a multiline").comment("comment"); 2433 2434 shouldEqual(ctor.toString, "// a multiline 2435 // comment 2436 Foo();"); 2437 } 2438 2439 @("Shall merge two namespaces and preserve the order of the items") 2440 unittest { 2441 import std.array : array; 2442 import std.algorithm : map; 2443 2444 auto ns1 = CppNamespace(CppNsStack([CppNs("ns1")])); 2445 ns1.put(CppClass(CppClassName("ns1_class"))); 2446 ns1.put(CxGlobalVariable(USRType("ns1_var"), 2447 TypeKindVariable(makeSimple("int"), CppVariable("ns1_var")))); 2448 ns1.put(CFunction(USRType("ns1_func"), CFunctionName("ns1_func"))); 2449 2450 auto ns2 = CppNamespace(CppNsStack([CppNs("ns2")])); 2451 ns2.put(CppClass(CppClassName("ns2_class"))); 2452 ns2.put(CxGlobalVariable(USRType("ns2_var"), 2453 TypeKindVariable(makeSimple("int"), CppVariable("ns2_var")))); 2454 ns2.put(CFunction(USRType("ns2_func"), CFunctionName("ns2_func"))); 2455 2456 ns2.merge(ns1, MergeMode.shallow); 2457 2458 ns2.classRange.map!(a => cast(string) a.name).array().shouldEqual([ 2459 "ns2_class", "ns1_class" 2460 ]); 2461 ns2.globalRange.array().map!(a => cast(string) a.name).array() 2462 .shouldEqual(["ns1_var", "ns2_var"]); 2463 ns2.funcRange.array().map!(a => cast(string) a.name).array() 2464 .shouldEqual(["ns1_func", "ns2_func"]); 2465 } 2466 2467 @("Shall merge two namespaces recursively") 2468 unittest { 2469 import std.array : array; 2470 import std.algorithm : map; 2471 2472 // Arrange 2473 auto ns1 = CppNamespace(CppNsStack([CppNs("ns1")])); 2474 auto ns2 = CppNamespace(CppNsStack([CppNs("ns2")])); 2475 auto ns3 = CppNamespace(CppNsStack([CppNs("ns3")])); 2476 2477 // Act 2478 ns1.put(ns3); 2479 ns2.merge(ns1, MergeMode.shallow); 2480 2481 // Assert 2482 // shallow do NOT merge 2483 ns2.namespaceRange.length.shouldEqual(0); 2484 2485 // Act 2486 ns2.merge(ns1, MergeMode.full); 2487 ns2.namespaceRange.length.shouldEqual(1); 2488 ns2.namespaceRange.map!(a => cast(string) a.name).array().shouldEqual([ 2489 "ns3" 2490 ]); 2491 } 2492 2493 @("Shall merge two namespaces recursively with common namespaces merged to ensure no duplication") 2494 unittest { 2495 import std.array : array; 2496 import std.algorithm : map; 2497 2498 // Arrange 2499 auto ns1 = CppNamespace(CppNsStack([CppNs("ns1")])); 2500 auto ns4 = CppNamespace(CppNsStack([CppNs("ns4")])); 2501 2502 auto ns2 = CppNamespace(CppNsStack([CppNs("ns2")])); 2503 ns2.put(CppClass(CppClassName("ns2_class"))); 2504 ns2.put(CxGlobalVariable(USRType("ns2_var"), 2505 TypeKindVariable(makeSimple("int"), CppVariable("ns2_var")))); 2506 ns2.put(CFunction(USRType("ns2_func"), CFunctionName("ns2_func"))); 2507 2508 auto ns3_a = CppNamespace(CppNsStack([CppNs("ns3")])); 2509 ns3_a.put(CppClass(CppClassName("ns3_class"))); 2510 ns3_a.put(CxGlobalVariable(USRType("ns3_var"), 2511 TypeKindVariable(makeSimple("int"), CppVariable("ns3_var")))); 2512 ns3_a.put(CFunction(USRType("ns3_func"), CFunctionName("ns3_func"))); 2513 2514 auto ns3_b = CppNamespace(CppNsStack([CppNs("ns3")])); 2515 // expected do be deduplicated 2516 ns3_b.put(CppClass(CppClassName("ns3_class"))); 2517 ns3_b.put(CxGlobalVariable(USRType("ns3_var"), 2518 TypeKindVariable(makeSimple("int"), CppVariable("ns3_var")))); 2519 ns3_b.put(CFunction(USRType("ns3_func"), CFunctionName("ns3_func"))); 2520 2521 // expected to be merged in ns2 into the already existing ns3 2522 ns3_b.put(CppClass(CppClassName("ns3_b_class"))); 2523 ns3_b.put(CxGlobalVariable(USRType("ns3_b_var"), 2524 TypeKindVariable(makeSimple("int"), CppVariable("ns3_b_var")))); 2525 ns3_b.put(CFunction(USRType("ns3_b_func"), CFunctionName("ns3_b_func"))); 2526 2527 // Act 2528 ns1.put(ns3_a); 2529 ns2.merge(ns1, MergeMode.shallow); 2530 2531 // Assert 2532 // because of a shallow merge no namespaces are expected 2533 ns2.namespaceRange.length.shouldEqual(0); 2534 2535 // Act 2536 ns2.merge(ns1, MergeMode.full); 2537 2538 // Assert 2539 ns2.namespaceRange.length.shouldEqual(1); 2540 ns2.namespaceRange.map!(a => cast(string) a.name).array().shouldEqual([ 2541 "ns3" 2542 ]); 2543 ns2.namespaceRange[0].classRange.length.shouldEqual(1); 2544 ns2.namespaceRange[0].funcRange.array().length.shouldEqual(1); 2545 ns2.namespaceRange[0].globalRange.array().length.shouldEqual(1); 2546 2547 // Act 2548 ns4.put(ns3_b); 2549 ns2.merge(ns4, MergeMode.full); 2550 2551 // Assert 2552 ns2.namespaceRange.length.shouldEqual(1); 2553 ns2.namespaceRange.map!(a => cast(string) a.name).array().shouldEqual([ 2554 "ns3" 2555 ]); 2556 ns2.namespaceRange[0].classRange.length.shouldEqual(2); 2557 ns2.namespaceRange[0].funcRange.array().length.shouldEqual(2); 2558 ns2.namespaceRange[0].globalRange.array().length.shouldEqual(2); 2559 }