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