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