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