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