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