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