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