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