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