1 /**
2  * Generate $(LINK2 https://dlang.org/dmd-windows.html#interface-files, D interface files).
3  *
4  * Also used to convert AST nodes to D code in general, e.g. for error messages or `printf` debugging.
5  *
6  * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/hdrgen.d, _hdrgen.d)
10  * Documentation:  https://dlang.org/phobos/dmd_hdrgen.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/hdrgen.d
12  */
13 
14 module dmd.hdrgen;
15 
16 import core.stdc.ctype;
17 import core.stdc.stdio;
18 import core.stdc..string;
19 import dmd.aggregate;
20 import dmd.aliasthis;
21 import dmd.arraytypes;
22 import dmd.attrib;
23 import dmd.complex;
24 import dmd.cond;
25 import dmd.ctfeexpr;
26 import dmd.dclass;
27 import dmd.declaration;
28 import dmd.denum;
29 import dmd.dimport;
30 import dmd.dmodule;
31 import dmd.doc;
32 import dmd.dstruct;
33 import dmd.dsymbol;
34 import dmd.dtemplate;
35 import dmd.dversion;
36 import dmd.expression;
37 import dmd.func;
38 import dmd.globals;
39 import dmd.id;
40 import dmd.identifier;
41 import dmd.init;
42 import dmd.mtype;
43 import dmd.nspace;
44 import dmd.parse;
45 import dmd.root.ctfloat;
46 import dmd.root.outbuffer;
47 import dmd.root.rootobject;
48 import dmd.root..string;
49 import dmd.statement;
50 import dmd.staticassert;
51 import dmd.target;
52 import dmd.tokens;
53 import dmd.utils;
54 import dmd.visitor;
55 
56 struct HdrGenState
57 {
58     bool hdrgen;        /// true if generating header file
59     bool ddoc;          /// true if generating Ddoc file
60     bool fullDump;      /// true if generating a full AST dump file
61 
62     bool fullQual;      /// fully qualify types when printing
63     int tpltMember;
64     int autoMember;
65     int forStmtInit;
66 
67     bool declstring; // set while declaring alias for string,wstring or dstring
68     EnumDeclaration inEnumDecl;
69 }
70 
71 enum TEST_EMIT_ALL = 0;
72 
73 extern (C++) void genhdrfile(Module m)
74 {
75     OutBuffer buf;
76     buf.doindent = 1;
77     buf.printf("// D import file generated from '%s'", m.srcfile.toChars());
78     buf.writenl();
79     HdrGenState hgs;
80     hgs.hdrgen = true;
81     toCBuffer(m, &buf, &hgs);
82     writeFile(m.loc, m.hdrfile.toString(), buf[]);
83 }
84 
85 /**
86  * Dumps the full contents of module `m` to `buf`.
87  * Params:
88  *   buf = buffer to write to.
89  *   m = module to visit all members of.
90  */
91 extern (C++) void moduleToBuffer(OutBuffer* buf, Module m)
92 {
93     HdrGenState hgs;
94     hgs.fullDump = true;
95     toCBuffer(m, buf, &hgs);
96 }
97 
98 void moduleToBuffer2(Module m, OutBuffer* buf, HdrGenState* hgs)
99 {
100     if (m.md)
101     {
102         if (m.userAttribDecl)
103         {
104             buf.writestring("@(");
105             argsToBuffer(m.userAttribDecl.atts, buf, hgs);
106             buf.writeByte(')');
107             buf.writenl();
108         }
109         if (m.md.isdeprecated)
110         {
111             if (m.md.msg)
112             {
113                 buf.writestring("deprecated(");
114                 m.md.msg.expressionToBuffer(buf, hgs);
115                 buf.writestring(") ");
116             }
117             else
118                 buf.writestring("deprecated ");
119         }
120         buf.writestring("module ");
121         buf.writestring(m.md.toChars());
122         buf.writeByte(';');
123         buf.writenl();
124     }
125 
126     foreach (s; *m.members)
127     {
128         s.dsymbolToBuffer(buf, hgs);
129     }
130 }
131 
132 private void statementToBuffer(Statement s, OutBuffer* buf, HdrGenState* hgs)
133 {
134     scope v = new StatementPrettyPrintVisitor(buf, hgs);
135     s.accept(v);
136 }
137 
138 private extern (C++) final class StatementPrettyPrintVisitor : Visitor
139 {
140     alias visit = Visitor.visit;
141 public:
142     OutBuffer* buf;
143     HdrGenState* hgs;
144 
145     extern (D) this(OutBuffer* buf, HdrGenState* hgs)
146     {
147         this.buf = buf;
148         this.hgs = hgs;
149     }
150 
151     override void visit(Statement s)
152     {
153         buf.writestring("Statement::toCBuffer()");
154         buf.writenl();
155         assert(0);
156     }
157 
158     override void visit(ErrorStatement s)
159     {
160         buf.writestring("__error__");
161         buf.writenl();
162     }
163 
164     override void visit(ExpStatement s)
165     {
166         if (s.exp && s.exp.op == TOK.declaration &&
167             (cast(DeclarationExp)s.exp).declaration)
168         {
169             // bypass visit(DeclarationExp)
170             (cast(DeclarationExp)s.exp).declaration.dsymbolToBuffer(buf, hgs);
171             return;
172         }
173         if (s.exp)
174             s.exp.expressionToBuffer(buf, hgs);
175         buf.writeByte(';');
176         if (!hgs.forStmtInit)
177             buf.writenl();
178     }
179 
180     override void visit(CompileStatement s)
181     {
182         buf.writestring("mixin(");
183         argsToBuffer(s.exps, buf, hgs, null);
184         buf.writestring(");");
185         if (!hgs.forStmtInit)
186             buf.writenl();
187     }
188 
189     override void visit(CompoundStatement s)
190     {
191         foreach (sx; *s.statements)
192         {
193             if (sx)
194                 sx.accept(this);
195         }
196     }
197 
198     override void visit(CompoundDeclarationStatement s)
199     {
200         bool anywritten = false;
201         foreach (sx; *s.statements)
202         {
203             auto ds = sx ? sx.isExpStatement() : null;
204             if (ds && ds.exp.op == TOK.declaration)
205             {
206                 auto d = (cast(DeclarationExp)ds.exp).declaration;
207                 assert(d.isDeclaration());
208                 if (auto v = d.isVarDeclaration())
209                 {
210                     scope ppv = new DsymbolPrettyPrintVisitor(buf, hgs);
211                     ppv.visitVarDecl(v, anywritten);
212                 }
213                 else
214                     d.dsymbolToBuffer(buf, hgs);
215                 anywritten = true;
216             }
217         }
218         buf.writeByte(';');
219         if (!hgs.forStmtInit)
220             buf.writenl();
221     }
222 
223     override void visit(UnrolledLoopStatement s)
224     {
225         buf.writestring("/*unrolled*/ {");
226         buf.writenl();
227         buf.level++;
228         foreach (sx; *s.statements)
229         {
230             if (sx)
231                 sx.accept(this);
232         }
233         buf.level--;
234         buf.writeByte('}');
235         buf.writenl();
236     }
237 
238     override void visit(ScopeStatement s)
239     {
240         buf.writeByte('{');
241         buf.writenl();
242         buf.level++;
243         if (s.statement)
244             s.statement.accept(this);
245         buf.level--;
246         buf.writeByte('}');
247         buf.writenl();
248     }
249 
250     override void visit(WhileStatement s)
251     {
252         buf.writestring("while (");
253         s.condition.expressionToBuffer(buf, hgs);
254         buf.writeByte(')');
255         buf.writenl();
256         if (s._body)
257             s._body.accept(this);
258     }
259 
260     override void visit(DoStatement s)
261     {
262         buf.writestring("do");
263         buf.writenl();
264         if (s._body)
265             s._body.accept(this);
266         buf.writestring("while (");
267         s.condition.expressionToBuffer(buf, hgs);
268         buf.writestring(");");
269         buf.writenl();
270     }
271 
272     override void visit(ForStatement s)
273     {
274         buf.writestring("for (");
275         if (s._init)
276         {
277             hgs.forStmtInit++;
278             s._init.accept(this);
279             hgs.forStmtInit--;
280         }
281         else
282             buf.writeByte(';');
283         if (s.condition)
284         {
285             buf.writeByte(' ');
286             s.condition.expressionToBuffer(buf, hgs);
287         }
288         buf.writeByte(';');
289         if (s.increment)
290         {
291             buf.writeByte(' ');
292             s.increment.expressionToBuffer(buf, hgs);
293         }
294         buf.writeByte(')');
295         buf.writenl();
296         buf.writeByte('{');
297         buf.writenl();
298         buf.level++;
299         if (s._body)
300             s._body.accept(this);
301         buf.level--;
302         buf.writeByte('}');
303         buf.writenl();
304     }
305 
306     private void foreachWithoutBody(ForeachStatement s)
307     {
308         buf.writestring(Token.toString(s.op));
309         buf.writestring(" (");
310         foreach (i, p; *s.parameters)
311         {
312             if (i)
313                 buf.writestring(", ");
314             if (stcToBuffer(buf, p.storageClass))
315                 buf.writeByte(' ');
316             if (p.type)
317                 typeToBuffer(p.type, p.ident, buf, hgs);
318             else
319                 buf.writestring(p.ident.toString());
320         }
321         buf.writestring("; ");
322         s.aggr.expressionToBuffer(buf, hgs);
323         buf.writeByte(')');
324         buf.writenl();
325     }
326 
327     override void visit(ForeachStatement s)
328     {
329         foreachWithoutBody(s);
330         buf.writeByte('{');
331         buf.writenl();
332         buf.level++;
333         if (s._body)
334             s._body.accept(this);
335         buf.level--;
336         buf.writeByte('}');
337         buf.writenl();
338     }
339 
340     private void foreachRangeWithoutBody(ForeachRangeStatement s)
341     {
342         buf.writestring(Token.toString(s.op));
343         buf.writestring(" (");
344         if (s.prm.type)
345             typeToBuffer(s.prm.type, s.prm.ident, buf, hgs);
346         else
347             buf.writestring(s.prm.ident.toString());
348         buf.writestring("; ");
349         s.lwr.expressionToBuffer(buf, hgs);
350         buf.writestring(" .. ");
351         s.upr.expressionToBuffer(buf, hgs);
352         buf.writeByte(')');
353         buf.writenl();
354     }
355 
356     override void visit(ForeachRangeStatement s)
357     {
358         foreachRangeWithoutBody(s);
359         buf.writeByte('{');
360         buf.writenl();
361         buf.level++;
362         if (s._body)
363             s._body.accept(this);
364         buf.level--;
365         buf.writeByte('}');
366         buf.writenl();
367     }
368 
369     override void visit(StaticForeachStatement s)
370     {
371         buf.writestring("static ");
372         if (s.sfe.aggrfe)
373         {
374             visit(s.sfe.aggrfe);
375         }
376         else
377         {
378             assert(s.sfe.rangefe);
379             visit(s.sfe.rangefe);
380         }
381     }
382 
383     override void visit(ForwardingStatement s)
384     {
385         s.statement.accept(this);
386     }
387 
388     override void visit(IfStatement s)
389     {
390         buf.writestring("if (");
391         if (Parameter p = s.prm)
392         {
393             StorageClass stc = p.storageClass;
394             if (!p.type && !stc)
395                 stc = STC.auto_;
396             if (stcToBuffer(buf, stc))
397                 buf.writeByte(' ');
398             if (p.type)
399                 typeToBuffer(p.type, p.ident, buf, hgs);
400             else
401                 buf.writestring(p.ident.toString());
402             buf.writestring(" = ");
403         }
404         s.condition.expressionToBuffer(buf, hgs);
405         buf.writeByte(')');
406         buf.writenl();
407         if (s.ifbody.isScopeStatement())
408         {
409             s.ifbody.accept(this);
410         }
411         else
412         {
413             buf.level++;
414             s.ifbody.accept(this);
415             buf.level--;
416         }
417         if (s.elsebody)
418         {
419             buf.writestring("else");
420             if (!s.elsebody.isIfStatement())
421             {
422                 buf.writenl();
423             }
424             else
425             {
426                 buf.writeByte(' ');
427             }
428             if (s.elsebody.isScopeStatement() || s.elsebody.isIfStatement())
429             {
430                 s.elsebody.accept(this);
431             }
432             else
433             {
434                 buf.level++;
435                 s.elsebody.accept(this);
436                 buf.level--;
437             }
438         }
439     }
440 
441     override void visit(ConditionalStatement s)
442     {
443         s.condition.conditionToBuffer(buf, hgs);
444         buf.writenl();
445         buf.writeByte('{');
446         buf.writenl();
447         buf.level++;
448         if (s.ifbody)
449             s.ifbody.accept(this);
450         buf.level--;
451         buf.writeByte('}');
452         buf.writenl();
453         if (s.elsebody)
454         {
455             buf.writestring("else");
456             buf.writenl();
457             buf.writeByte('{');
458             buf.level++;
459             buf.writenl();
460             s.elsebody.accept(this);
461             buf.level--;
462             buf.writeByte('}');
463         }
464         buf.writenl();
465     }
466 
467     override void visit(PragmaStatement s)
468     {
469         buf.writestring("pragma (");
470         buf.writestring(s.ident.toString());
471         if (s.args && s.args.dim)
472         {
473             buf.writestring(", ");
474             argsToBuffer(s.args, buf, hgs);
475         }
476         buf.writeByte(')');
477         if (s._body)
478         {
479             buf.writenl();
480             buf.writeByte('{');
481             buf.writenl();
482             buf.level++;
483             s._body.accept(this);
484             buf.level--;
485             buf.writeByte('}');
486             buf.writenl();
487         }
488         else
489         {
490             buf.writeByte(';');
491             buf.writenl();
492         }
493     }
494 
495     override void visit(StaticAssertStatement s)
496     {
497         s.sa.dsymbolToBuffer(buf, hgs);
498     }
499 
500     override void visit(SwitchStatement s)
501     {
502         buf.writestring(s.isFinal ? "final switch (" : "switch (");
503         s.condition.expressionToBuffer(buf, hgs);
504         buf.writeByte(')');
505         buf.writenl();
506         if (s._body)
507         {
508             if (!s._body.isScopeStatement())
509             {
510                 buf.writeByte('{');
511                 buf.writenl();
512                 buf.level++;
513                 s._body.accept(this);
514                 buf.level--;
515                 buf.writeByte('}');
516                 buf.writenl();
517             }
518             else
519             {
520                 s._body.accept(this);
521             }
522         }
523     }
524 
525     override void visit(CaseStatement s)
526     {
527         buf.writestring("case ");
528         s.exp.expressionToBuffer(buf, hgs);
529         buf.writeByte(':');
530         buf.writenl();
531         s.statement.accept(this);
532     }
533 
534     override void visit(CaseRangeStatement s)
535     {
536         buf.writestring("case ");
537         s.first.expressionToBuffer(buf, hgs);
538         buf.writestring(": .. case ");
539         s.last.expressionToBuffer(buf, hgs);
540         buf.writeByte(':');
541         buf.writenl();
542         s.statement.accept(this);
543     }
544 
545     override void visit(DefaultStatement s)
546     {
547         buf.writestring("default:");
548         buf.writenl();
549         s.statement.accept(this);
550     }
551 
552     override void visit(GotoDefaultStatement s)
553     {
554         buf.writestring("goto default;");
555         buf.writenl();
556     }
557 
558     override void visit(GotoCaseStatement s)
559     {
560         buf.writestring("goto case");
561         if (s.exp)
562         {
563             buf.writeByte(' ');
564             s.exp.expressionToBuffer(buf, hgs);
565         }
566         buf.writeByte(';');
567         buf.writenl();
568     }
569 
570     override void visit(SwitchErrorStatement s)
571     {
572         buf.writestring("SwitchErrorStatement::toCBuffer()");
573         buf.writenl();
574     }
575 
576     override void visit(ReturnStatement s)
577     {
578         buf.writestring("return ");
579         if (s.exp)
580             s.exp.expressionToBuffer(buf, hgs);
581         buf.writeByte(';');
582         buf.writenl();
583     }
584 
585     override void visit(BreakStatement s)
586     {
587         buf.writestring("break");
588         if (s.ident)
589         {
590             buf.writeByte(' ');
591             buf.writestring(s.ident.toString());
592         }
593         buf.writeByte(';');
594         buf.writenl();
595     }
596 
597     override void visit(ContinueStatement s)
598     {
599         buf.writestring("continue");
600         if (s.ident)
601         {
602             buf.writeByte(' ');
603             buf.writestring(s.ident.toString());
604         }
605         buf.writeByte(';');
606         buf.writenl();
607     }
608 
609     override void visit(SynchronizedStatement s)
610     {
611         buf.writestring("synchronized");
612         if (s.exp)
613         {
614             buf.writeByte('(');
615             s.exp.expressionToBuffer(buf, hgs);
616             buf.writeByte(')');
617         }
618         if (s._body)
619         {
620             buf.writeByte(' ');
621             s._body.accept(this);
622         }
623     }
624 
625     override void visit(WithStatement s)
626     {
627         buf.writestring("with (");
628         s.exp.expressionToBuffer(buf, hgs);
629         buf.writestring(")");
630         buf.writenl();
631         if (s._body)
632             s._body.accept(this);
633     }
634 
635     override void visit(TryCatchStatement s)
636     {
637         buf.writestring("try");
638         buf.writenl();
639         if (s._body)
640         {
641             if (s._body.isScopeStatement())
642             {
643                 s._body.accept(this);
644             }
645             else
646             {
647                 buf.level++;
648                 s._body.accept(this);
649                 buf.level--;
650             }
651         }
652         foreach (c; *s.catches)
653         {
654             visit(c);
655         }
656     }
657 
658     override void visit(TryFinallyStatement s)
659     {
660         buf.writestring("try");
661         buf.writenl();
662         buf.writeByte('{');
663         buf.writenl();
664         buf.level++;
665         s._body.accept(this);
666         buf.level--;
667         buf.writeByte('}');
668         buf.writenl();
669         buf.writestring("finally");
670         buf.writenl();
671         if (s.finalbody.isScopeStatement())
672         {
673             s.finalbody.accept(this);
674         }
675         else
676         {
677             buf.level++;
678             s.finalbody.accept(this);
679             buf.level--;
680         }
681     }
682 
683     override void visit(ScopeGuardStatement s)
684     {
685         buf.writestring(Token.toString(s.tok));
686         buf.writeByte(' ');
687         if (s.statement)
688             s.statement.accept(this);
689     }
690 
691     override void visit(ThrowStatement s)
692     {
693         buf.writestring("throw ");
694         s.exp.expressionToBuffer(buf, hgs);
695         buf.writeByte(';');
696         buf.writenl();
697     }
698 
699     override void visit(DebugStatement s)
700     {
701         if (s.statement)
702         {
703             s.statement.accept(this);
704         }
705     }
706 
707     override void visit(GotoStatement s)
708     {
709         buf.writestring("goto ");
710         buf.writestring(s.ident.toString());
711         buf.writeByte(';');
712         buf.writenl();
713     }
714 
715     override void visit(LabelStatement s)
716     {
717         buf.writestring(s.ident.toString());
718         buf.writeByte(':');
719         buf.writenl();
720         if (s.statement)
721             s.statement.accept(this);
722     }
723 
724     override void visit(AsmStatement s)
725     {
726         buf.writestring("asm { ");
727         Token* t = s.tokens;
728         buf.level++;
729         while (t)
730         {
731             buf.writestring(t.toChars());
732             if (t.next &&
733                 t.value != TOK.min      &&
734                 t.value != TOK.comma    && t.next.value != TOK.comma    &&
735                 t.value != TOK.leftBracket && t.next.value != TOK.leftBracket &&
736                                           t.next.value != TOK.rightBracket &&
737                 t.value != TOK.leftParentheses   && t.next.value != TOK.leftParentheses   &&
738                                           t.next.value != TOK.rightParentheses   &&
739                 t.value != TOK.dot      && t.next.value != TOK.dot)
740             {
741                 buf.writeByte(' ');
742             }
743             t = t.next;
744         }
745         buf.level--;
746         buf.writestring("; }");
747         buf.writenl();
748     }
749 
750     override void visit(ImportStatement s)
751     {
752         foreach (imp; *s.imports)
753         {
754             imp.dsymbolToBuffer(buf, hgs);
755         }
756     }
757 
758     void visit(Catch c)
759     {
760         buf.writestring("catch");
761         if (c.type)
762         {
763             buf.writeByte('(');
764             typeToBuffer(c.type, c.ident, buf, hgs);
765             buf.writeByte(')');
766         }
767         buf.writenl();
768         buf.writeByte('{');
769         buf.writenl();
770         buf.level++;
771         if (c.handler)
772             c.handler.accept(this);
773         buf.level--;
774         buf.writeByte('}');
775         buf.writenl();
776     }
777 }
778 
779 private void dsymbolToBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs)
780 {
781     scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
782     s.accept(v);
783 }
784 
785 private extern (C++) final class DsymbolPrettyPrintVisitor : Visitor
786 {
787     alias visit = Visitor.visit;
788 public:
789     OutBuffer* buf;
790     HdrGenState* hgs;
791 
792     extern (D) this(OutBuffer* buf, HdrGenState* hgs)
793     {
794         this.buf = buf;
795         this.hgs = hgs;
796     }
797 
798     ////////////////////////////////////////////////////////////////////////////
799 
800     override void visit(Dsymbol s)
801     {
802         buf.writestring(s.toChars());
803     }
804 
805     override void visit(StaticAssert s)
806     {
807         buf.writestring(s.kind());
808         buf.writeByte('(');
809         s.exp.expressionToBuffer(buf, hgs);
810         if (s.msg)
811         {
812             buf.writestring(", ");
813             s.msg.expressionToBuffer(buf, hgs);
814         }
815         buf.writestring(");");
816         buf.writenl();
817     }
818 
819     override void visit(DebugSymbol s)
820     {
821         buf.writestring("debug = ");
822         if (s.ident)
823             buf.writestring(s.ident.toString());
824         else
825             buf.print(s.level);
826         buf.writeByte(';');
827         buf.writenl();
828     }
829 
830     override void visit(VersionSymbol s)
831     {
832         buf.writestring("version = ");
833         if (s.ident)
834             buf.writestring(s.ident.toString());
835         else
836             buf.print(s.level);
837         buf.writeByte(';');
838         buf.writenl();
839     }
840 
841     override void visit(EnumMember em)
842     {
843         if (em.type)
844             typeToBuffer(em.type, em.ident, buf, hgs);
845         else
846             buf.writestring(em.ident.toString());
847         if (em.value)
848         {
849             buf.writestring(" = ");
850             em.value.expressionToBuffer(buf, hgs);
851         }
852     }
853 
854     override void visit(Import imp)
855     {
856         if (hgs.hdrgen && imp.id == Id.object)
857             return; // object is imported by default
858         if (imp.isstatic)
859             buf.writestring("static ");
860         buf.writestring("import ");
861         if (imp.aliasId)
862         {
863             buf.printf("%s = ", imp.aliasId.toChars());
864         }
865         if (imp.packages && imp.packages.dim)
866         {
867             foreach (const pid; *imp.packages)
868             {
869                 buf.printf("%s.", pid.toChars());
870             }
871         }
872         buf.writestring(imp.id.toString());
873         if (imp.names.dim)
874         {
875             buf.writestring(" : ");
876             foreach (const i, const name; imp.names)
877             {
878                 if (i)
879                     buf.writestring(", ");
880                 const _alias = imp.aliases[i];
881                 if (_alias)
882                     buf.printf("%s = %s", _alias.toChars(), name.toChars());
883                 else
884                     buf.writestring(name.toChars());
885             }
886         }
887         buf.writeByte(';');
888         buf.writenl();
889     }
890 
891     override void visit(AliasThis d)
892     {
893         buf.writestring("alias ");
894         buf.writestring(d.ident.toString());
895         buf.writestring(" this;\n");
896     }
897 
898     override void visit(AttribDeclaration d)
899     {
900         if (!d.decl)
901         {
902             buf.writeByte(';');
903             buf.writenl();
904             return;
905         }
906         if (d.decl.dim == 0)
907             buf.writestring("{}");
908         else if (hgs.hdrgen && d.decl.dim == 1 && (*d.decl)[0].isUnitTestDeclaration())
909         {
910             // hack for bugzilla 8081
911             buf.writestring("{}");
912         }
913         else if (d.decl.dim == 1)
914         {
915             (*d.decl)[0].accept(this);
916             return;
917         }
918         else
919         {
920             buf.writenl();
921             buf.writeByte('{');
922             buf.writenl();
923             buf.level++;
924             foreach (de; *d.decl)
925                 de.accept(this);
926             buf.level--;
927             buf.writeByte('}');
928         }
929         buf.writenl();
930     }
931 
932     override void visit(StorageClassDeclaration d)
933     {
934         if (stcToBuffer(buf, d.stc))
935             buf.writeByte(' ');
936         visit(cast(AttribDeclaration)d);
937     }
938 
939     override void visit(DeprecatedDeclaration d)
940     {
941         buf.writestring("deprecated(");
942         d.msg.expressionToBuffer(buf, hgs);
943         buf.writestring(") ");
944         visit(cast(AttribDeclaration)d);
945     }
946 
947     override void visit(LinkDeclaration d)
948     {
949         buf.writestring("extern (");
950         buf.writestring(linkageToString(d.linkage));
951         buf.writestring(") ");
952         visit(cast(AttribDeclaration)d);
953     }
954 
955     override void visit(CPPMangleDeclaration d)
956     {
957         string s;
958         final switch (d.cppmangle)
959         {
960         case CPPMANGLE.asClass:
961             s = "class";
962             break;
963         case CPPMANGLE.asStruct:
964             s = "struct";
965             break;
966         case CPPMANGLE.def:
967             break;
968         }
969         buf.writestring("extern (C++, ");
970         buf.writestring(s);
971         buf.writestring(") ");
972         visit(cast(AttribDeclaration)d);
973     }
974 
975     override void visit(ProtDeclaration d)
976     {
977         protectionToBuffer(buf, d.protection);
978         buf.writeByte(' ');
979         AttribDeclaration ad = cast(AttribDeclaration)d;
980         if (ad.decl.dim == 1 && (*ad.decl)[0].isProtDeclaration)
981             visit(cast(AttribDeclaration)(*ad.decl)[0]);
982         else
983             visit(cast(AttribDeclaration)d);
984     }
985 
986     override void visit(AlignDeclaration d)
987     {
988         buf.writestring("align ");
989         if (d.ealign)
990             buf.printf("(%s) ", d.ealign.toChars());
991         visit(cast(AttribDeclaration)d);
992     }
993 
994     override void visit(AnonDeclaration d)
995     {
996         buf.writestring(d.isunion ? "union" : "struct");
997         buf.writenl();
998         buf.writestring("{");
999         buf.writenl();
1000         buf.level++;
1001         if (d.decl)
1002         {
1003             foreach (de; *d.decl)
1004                 de.accept(this);
1005         }
1006         buf.level--;
1007         buf.writestring("}");
1008         buf.writenl();
1009     }
1010 
1011     override void visit(PragmaDeclaration d)
1012     {
1013         buf.writestring("pragma (");
1014         buf.writestring(d.ident.toString());
1015         if (d.args && d.args.dim)
1016         {
1017             buf.writestring(", ");
1018             argsToBuffer(d.args, buf, hgs);
1019         }
1020         buf.writeByte(')');
1021         visit(cast(AttribDeclaration)d);
1022     }
1023 
1024     override void visit(ConditionalDeclaration d)
1025     {
1026         d.condition.conditionToBuffer(buf, hgs);
1027         if (d.decl || d.elsedecl)
1028         {
1029             buf.writenl();
1030             buf.writeByte('{');
1031             buf.writenl();
1032             buf.level++;
1033             if (d.decl)
1034             {
1035                 foreach (de; *d.decl)
1036                     de.accept(this);
1037             }
1038             buf.level--;
1039             buf.writeByte('}');
1040             if (d.elsedecl)
1041             {
1042                 buf.writenl();
1043                 buf.writestring("else");
1044                 buf.writenl();
1045                 buf.writeByte('{');
1046                 buf.writenl();
1047                 buf.level++;
1048                 foreach (de; *d.elsedecl)
1049                     de.accept(this);
1050                 buf.level--;
1051                 buf.writeByte('}');
1052             }
1053         }
1054         else
1055             buf.writeByte(':');
1056         buf.writenl();
1057     }
1058 
1059     override void visit(StaticForeachDeclaration s)
1060     {
1061         void foreachWithoutBody(ForeachStatement s)
1062         {
1063             buf.writestring(Token.toString(s.op));
1064             buf.writestring(" (");
1065             foreach (i, p; *s.parameters)
1066             {
1067                 if (i)
1068                     buf.writestring(", ");
1069                 if (stcToBuffer(buf, p.storageClass))
1070                     buf.writeByte(' ');
1071                 if (p.type)
1072                     typeToBuffer(p.type, p.ident, buf, hgs);
1073                 else
1074                     buf.writestring(p.ident.toString());
1075             }
1076             buf.writestring("; ");
1077             s.aggr.expressionToBuffer(buf, hgs);
1078             buf.writeByte(')');
1079             buf.writenl();
1080         }
1081 
1082         void foreachRangeWithoutBody(ForeachRangeStatement s)
1083         {
1084             /* s.op ( prm ; lwr .. upr )
1085              */
1086             buf.writestring(Token.toString(s.op));
1087             buf.writestring(" (");
1088             if (s.prm.type)
1089                 typeToBuffer(s.prm.type, s.prm.ident, buf, hgs);
1090             else
1091                 buf.writestring(s.prm.ident.toString());
1092             buf.writestring("; ");
1093             s.lwr.expressionToBuffer(buf, hgs);
1094             buf.writestring(" .. ");
1095             s.upr.expressionToBuffer(buf, hgs);
1096             buf.writeByte(')');
1097             buf.writenl();
1098         }
1099 
1100         buf.writestring("static ");
1101         if (s.sfe.aggrfe)
1102         {
1103             foreachWithoutBody(s.sfe.aggrfe);
1104         }
1105         else
1106         {
1107             assert(s.sfe.rangefe);
1108             foreachRangeWithoutBody(s.sfe.rangefe);
1109         }
1110         buf.writeByte('{');
1111         buf.writenl();
1112         buf.level++;
1113         visit(cast(AttribDeclaration)s);
1114         buf.level--;
1115         buf.writeByte('}');
1116         buf.writenl();
1117 
1118     }
1119 
1120     override void visit(CompileDeclaration d)
1121     {
1122         buf.writestring("mixin(");
1123         argsToBuffer(d.exps, buf, hgs, null);
1124         buf.writestring(");");
1125         buf.writenl();
1126     }
1127 
1128     override void visit(UserAttributeDeclaration d)
1129     {
1130         buf.writestring("@(");
1131         argsToBuffer(d.atts, buf, hgs);
1132         buf.writeByte(')');
1133         visit(cast(AttribDeclaration)d);
1134     }
1135 
1136     override void visit(TemplateDeclaration d)
1137     {
1138         version (none)
1139         {
1140             // Should handle template functions for doc generation
1141             if (onemember && onemember.isFuncDeclaration())
1142                 buf.writestring("foo ");
1143         }
1144         if ((hgs.hdrgen || hgs.fullDump) && visitEponymousMember(d))
1145             return;
1146         if (hgs.ddoc)
1147             buf.writestring(d.kind());
1148         else
1149             buf.writestring("template");
1150         buf.writeByte(' ');
1151         buf.writestring(d.ident.toString());
1152         buf.writeByte('(');
1153         visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
1154         buf.writeByte(')');
1155         visitTemplateConstraint(d.constraint);
1156         if (hgs.hdrgen || hgs.fullDump)
1157         {
1158             hgs.tpltMember++;
1159             buf.writenl();
1160             buf.writeByte('{');
1161             buf.writenl();
1162             buf.level++;
1163             foreach (s; *d.members)
1164                 s.accept(this);
1165             buf.level--;
1166             buf.writeByte('}');
1167             buf.writenl();
1168             hgs.tpltMember--;
1169         }
1170     }
1171 
1172     bool visitEponymousMember(TemplateDeclaration d)
1173     {
1174         if (!d.members || d.members.dim != 1)
1175             return false;
1176         Dsymbol onemember = (*d.members)[0];
1177         if (onemember.ident != d.ident)
1178             return false;
1179         if (FuncDeclaration fd = onemember.isFuncDeclaration())
1180         {
1181             assert(fd.type);
1182             if (stcToBuffer(buf, fd.storage_class))
1183                 buf.writeByte(' ');
1184             functionToBufferFull(cast(TypeFunction)fd.type, buf, d.ident, hgs, d);
1185             visitTemplateConstraint(d.constraint);
1186             hgs.tpltMember++;
1187             bodyToBuffer(fd);
1188             hgs.tpltMember--;
1189             return true;
1190         }
1191         if (AggregateDeclaration ad = onemember.isAggregateDeclaration())
1192         {
1193             buf.writestring(ad.kind());
1194             buf.writeByte(' ');
1195             buf.writestring(ad.ident.toString());
1196             buf.writeByte('(');
1197             visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
1198             buf.writeByte(')');
1199             visitTemplateConstraint(d.constraint);
1200             visitBaseClasses(ad.isClassDeclaration());
1201             hgs.tpltMember++;
1202             if (ad.members)
1203             {
1204                 buf.writenl();
1205                 buf.writeByte('{');
1206                 buf.writenl();
1207                 buf.level++;
1208                 foreach (s; *ad.members)
1209                     s.accept(this);
1210                 buf.level--;
1211                 buf.writeByte('}');
1212             }
1213             else
1214                 buf.writeByte(';');
1215             buf.writenl();
1216             hgs.tpltMember--;
1217             return true;
1218         }
1219         if (VarDeclaration vd = onemember.isVarDeclaration())
1220         {
1221             if (d.constraint)
1222                 return false;
1223             if (stcToBuffer(buf, vd.storage_class))
1224                 buf.writeByte(' ');
1225             if (vd.type)
1226                 typeToBuffer(vd.type, vd.ident, buf, hgs);
1227             else
1228                 buf.writestring(vd.ident.toString());
1229             buf.writeByte('(');
1230             visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
1231             buf.writeByte(')');
1232             if (vd._init)
1233             {
1234                 buf.writestring(" = ");
1235                 ExpInitializer ie = vd._init.isExpInitializer();
1236                 if (ie && (ie.exp.op == TOK.construct || ie.exp.op == TOK.blit))
1237                     (cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
1238                 else
1239                     vd._init.initializerToBuffer(buf, hgs);
1240             }
1241             buf.writeByte(';');
1242             buf.writenl();
1243             return true;
1244         }
1245         return false;
1246     }
1247 
1248     void visitTemplateParameters(TemplateParameters* parameters)
1249     {
1250         if (!parameters || !parameters.dim)
1251             return;
1252         foreach (i, p; *parameters)
1253         {
1254             if (i)
1255                 buf.writestring(", ");
1256             p.templateParameterToBuffer(buf, hgs);
1257         }
1258     }
1259 
1260     void visitTemplateConstraint(Expression constraint)
1261     {
1262         if (!constraint)
1263             return;
1264         buf.writestring(" if (");
1265         constraint.expressionToBuffer(buf, hgs);
1266         buf.writeByte(')');
1267     }
1268 
1269     override void visit(TemplateInstance ti)
1270     {
1271         buf.writestring(ti.name.toChars());
1272         tiargsToBuffer(ti, buf, hgs);
1273 
1274         if (hgs.fullDump)
1275         {
1276             buf.writenl();
1277             dumpTemplateInstance(ti, buf, hgs);
1278         }
1279     }
1280 
1281     override void visit(TemplateMixin tm)
1282     {
1283         buf.writestring("mixin ");
1284         typeToBuffer(tm.tqual, null, buf, hgs);
1285         tiargsToBuffer(tm, buf, hgs);
1286         if (tm.ident && memcmp(tm.ident.toChars(), cast(const(char)*)"__mixin", 7) != 0)
1287         {
1288             buf.writeByte(' ');
1289             buf.writestring(tm.ident.toString());
1290         }
1291         buf.writeByte(';');
1292         buf.writenl();
1293         if (hgs.fullDump)
1294             dumpTemplateInstance(tm, buf, hgs);
1295     }
1296 
1297     override void visit(EnumDeclaration d)
1298     {
1299         auto oldInEnumDecl = hgs.inEnumDecl;
1300         scope(exit) hgs.inEnumDecl = oldInEnumDecl;
1301         hgs.inEnumDecl = d;
1302         buf.writestring("enum ");
1303         if (d.ident)
1304         {
1305             buf.writestring(d.ident.toString());
1306             buf.writeByte(' ');
1307         }
1308         if (d.memtype)
1309         {
1310             buf.writestring(": ");
1311             typeToBuffer(d.memtype, null, buf, hgs);
1312         }
1313         if (!d.members)
1314         {
1315             buf.writeByte(';');
1316             buf.writenl();
1317             return;
1318         }
1319         buf.writenl();
1320         buf.writeByte('{');
1321         buf.writenl();
1322         buf.level++;
1323         foreach (em; *d.members)
1324         {
1325             if (!em)
1326                 continue;
1327             em.accept(this);
1328             buf.writeByte(',');
1329             buf.writenl();
1330         }
1331         buf.level--;
1332         buf.writeByte('}');
1333         buf.writenl();
1334     }
1335 
1336     override void visit(Nspace d)
1337     {
1338         buf.writestring("extern (C++, ");
1339         buf.writestring(d.ident.toString());
1340         buf.writeByte(')');
1341         buf.writenl();
1342         buf.writeByte('{');
1343         buf.writenl();
1344         buf.level++;
1345         foreach (s; *d.members)
1346             s.accept(this);
1347         buf.level--;
1348         buf.writeByte('}');
1349         buf.writenl();
1350     }
1351 
1352     override void visit(StructDeclaration d)
1353     {
1354         buf.writestring(d.kind());
1355         buf.writeByte(' ');
1356         if (!d.isAnonymous())
1357             buf.writestring(d.toChars());
1358         if (!d.members)
1359         {
1360             buf.writeByte(';');
1361             buf.writenl();
1362             return;
1363         }
1364         buf.writenl();
1365         buf.writeByte('{');
1366         buf.writenl();
1367         buf.level++;
1368         foreach (s; *d.members)
1369             s.accept(this);
1370         buf.level--;
1371         buf.writeByte('}');
1372         buf.writenl();
1373     }
1374 
1375     override void visit(ClassDeclaration d)
1376     {
1377         if (!d.isAnonymous())
1378         {
1379             buf.writestring(d.kind());
1380             buf.writeByte(' ');
1381             buf.writestring(d.ident.toString());
1382         }
1383         visitBaseClasses(d);
1384         if (d.members)
1385         {
1386             buf.writenl();
1387             buf.writeByte('{');
1388             buf.writenl();
1389             buf.level++;
1390             foreach (s; *d.members)
1391                 s.accept(this);
1392             buf.level--;
1393             buf.writeByte('}');
1394         }
1395         else
1396             buf.writeByte(';');
1397         buf.writenl();
1398     }
1399 
1400     void visitBaseClasses(ClassDeclaration d)
1401     {
1402         if (!d || !d.baseclasses.dim)
1403             return;
1404         if (!d.isAnonymous())
1405             buf.writestring(" : ");
1406         foreach (i, b; *d.baseclasses)
1407         {
1408             if (i)
1409                 buf.writestring(", ");
1410             typeToBuffer(b.type, null, buf, hgs);
1411         }
1412     }
1413 
1414     override void visit(AliasDeclaration d)
1415     {
1416         if (d.storage_class & STC.local)
1417             return;
1418         buf.writestring("alias ");
1419         if (d.aliassym)
1420         {
1421             buf.writestring(d.ident.toString());
1422             buf.writestring(" = ");
1423             if (stcToBuffer(buf, d.storage_class))
1424                 buf.writeByte(' ');
1425             d.aliassym.accept(this);
1426         }
1427         else if (d.type.ty == Tfunction)
1428         {
1429             if (stcToBuffer(buf, d.storage_class))
1430                 buf.writeByte(' ');
1431             typeToBuffer(d.type, d.ident, buf, hgs);
1432         }
1433         else if (d.ident)
1434         {
1435             hgs.declstring = (d.ident == Id..string || d.ident == Id.wstring || d.ident == Id.dstring);
1436             buf.writestring(d.ident.toString());
1437             buf.writestring(" = ");
1438             if (stcToBuffer(buf, d.storage_class))
1439                 buf.writeByte(' ');
1440             typeToBuffer(d.type, null, buf, hgs);
1441             hgs.declstring = false;
1442         }
1443         buf.writeByte(';');
1444         buf.writenl();
1445     }
1446 
1447     override void visit(VarDeclaration d)
1448     {
1449         if (d.storage_class & STC.local)
1450             return;
1451         visitVarDecl(d, false);
1452         buf.writeByte(';');
1453         buf.writenl();
1454     }
1455 
1456     void visitVarDecl(VarDeclaration v, bool anywritten)
1457     {
1458         if (anywritten)
1459         {
1460             buf.writestring(", ");
1461             buf.writestring(v.ident.toString());
1462         }
1463         else
1464         {
1465             if (stcToBuffer(buf, v.storage_class))
1466                 buf.writeByte(' ');
1467             if (v.type)
1468                 typeToBuffer(v.type, v.ident, buf, hgs);
1469             else
1470                 buf.writestring(v.ident.toString());
1471         }
1472         if (v._init)
1473         {
1474             buf.writestring(" = ");
1475             auto ie = v._init.isExpInitializer();
1476             if (ie && (ie.exp.op == TOK.construct || ie.exp.op == TOK.blit))
1477                 (cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
1478             else
1479                 v._init.initializerToBuffer(buf, hgs);
1480         }
1481     }
1482 
1483     override void visit(FuncDeclaration f)
1484     {
1485         //printf("FuncDeclaration::toCBuffer() '%s'\n", f.toChars());
1486         if (stcToBuffer(buf, f.storage_class))
1487             buf.writeByte(' ');
1488         auto tf = cast(TypeFunction)f.type;
1489         typeToBuffer(tf, f.ident, buf, hgs);
1490 
1491         if (hgs.hdrgen)
1492         {
1493             // if the return type is missing (e.g. ref functions or auto)
1494             if (!tf.next || f.storage_class & STC.auto_)
1495             {
1496                 hgs.autoMember++;
1497                 bodyToBuffer(f);
1498                 hgs.autoMember--;
1499             }
1500             else if (hgs.tpltMember == 0 && global.params.hdrStripPlainFunctions)
1501             {
1502                 buf.writeByte(';');
1503                 buf.writenl();
1504             }
1505             else
1506                 bodyToBuffer(f);
1507         }
1508         else
1509             bodyToBuffer(f);
1510     }
1511 
1512     void bodyToBuffer(FuncDeclaration f)
1513     {
1514         if (!f.fbody || (hgs.hdrgen && global.params.hdrStripPlainFunctions && !hgs.autoMember && !hgs.tpltMember))
1515         {
1516             buf.writeByte(';');
1517             buf.writenl();
1518             return;
1519         }
1520         const savetlpt = hgs.tpltMember;
1521         const saveauto = hgs.autoMember;
1522         hgs.tpltMember = 0;
1523         hgs.autoMember = 0;
1524         buf.writenl();
1525         bool requireDo = false;
1526         // in{}
1527         if (f.frequires)
1528         {
1529             foreach (frequire; *f.frequires)
1530             {
1531                 buf.writestring("in");
1532                 if (auto es = frequire.isExpStatement())
1533                 {
1534                     assert(es.exp && es.exp.op == TOK.assert_);
1535                     buf.writestring(" (");
1536                     (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
1537                     buf.writeByte(')');
1538                     buf.writenl();
1539                     requireDo = false;
1540                 }
1541                 else
1542                 {
1543                     buf.writenl();
1544                     frequire.statementToBuffer(buf, hgs);
1545                     requireDo = true;
1546                 }
1547             }
1548         }
1549         // out{}
1550         if (f.fensures)
1551         {
1552             foreach (fensure; *f.fensures)
1553             {
1554                 buf.writestring("out");
1555                 if (auto es = fensure.ensure.isExpStatement())
1556                 {
1557                     assert(es.exp && es.exp.op == TOK.assert_);
1558                     buf.writestring(" (");
1559                     if (fensure.id)
1560                     {
1561                         buf.writestring(fensure.id.toString());
1562                     }
1563                     buf.writestring("; ");
1564                     (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
1565                     buf.writeByte(')');
1566                     buf.writenl();
1567                     requireDo = false;
1568                 }
1569                 else
1570                 {
1571                     if (fensure.id)
1572                     {
1573                         buf.writeByte('(');
1574                         buf.writestring(fensure.id.toString());
1575                         buf.writeByte(')');
1576                     }
1577                     buf.writenl();
1578                     fensure.ensure.statementToBuffer(buf, hgs);
1579                     requireDo = true;
1580                 }
1581             }
1582         }
1583         if (requireDo)
1584         {
1585             buf.writestring("do");
1586             buf.writenl();
1587         }
1588         buf.writeByte('{');
1589         buf.writenl();
1590         buf.level++;
1591         f.fbody.statementToBuffer(buf, hgs);
1592         buf.level--;
1593         buf.writeByte('}');
1594         buf.writenl();
1595         hgs.tpltMember = savetlpt;
1596         hgs.autoMember = saveauto;
1597     }
1598 
1599     override void visit(FuncLiteralDeclaration f)
1600     {
1601         if (f.type.ty == Terror)
1602         {
1603             buf.writestring("__error");
1604             return;
1605         }
1606         if (f.tok != TOK.reserved)
1607         {
1608             buf.writestring(f.kind());
1609             buf.writeByte(' ');
1610         }
1611         TypeFunction tf = cast(TypeFunction)f.type;
1612 
1613         if (!f.inferRetType && tf.next)
1614             typeToBuffer(tf.next, null, buf, hgs);
1615         parametersToBuffer(tf.parameterList, buf, hgs);
1616 
1617         // https://issues.dlang.org/show_bug.cgi?id=20074
1618         void printAttribute(string str)
1619         {
1620             buf.writeByte(' ');
1621             buf.writestring(str);
1622         }
1623         tf.attributesApply(&printAttribute);
1624 
1625 
1626         CompoundStatement cs = f.fbody.isCompoundStatement();
1627         Statement s1;
1628         if (f.semanticRun >= PASS.semantic3done && cs)
1629         {
1630             s1 = (*cs.statements)[cs.statements.dim - 1];
1631         }
1632         else
1633             s1 = !cs ? f.fbody : null;
1634         ReturnStatement rs = s1 ? s1.endsWithReturnStatement() : null;
1635         if (rs && rs.exp)
1636         {
1637             buf.writestring(" => ");
1638             rs.exp.expressionToBuffer(buf, hgs);
1639         }
1640         else
1641         {
1642             hgs.tpltMember++;
1643             bodyToBuffer(f);
1644             hgs.tpltMember--;
1645         }
1646     }
1647 
1648     override void visit(PostBlitDeclaration d)
1649     {
1650         if (stcToBuffer(buf, d.storage_class))
1651             buf.writeByte(' ');
1652         buf.writestring("this(this)");
1653         bodyToBuffer(d);
1654     }
1655 
1656     override void visit(DtorDeclaration d)
1657     {
1658         if (d.storage_class & STC.trusted)
1659             buf.writestring("@trusted ");
1660         if (d.storage_class & STC.safe)
1661             buf.writestring("@safe ");
1662         if (d.storage_class & STC.nogc)
1663             buf.writestring("@nogc ");
1664         if (d.storage_class & STC.disable)
1665             buf.writestring("@disable ");
1666 
1667         buf.writestring("~this()");
1668         bodyToBuffer(d);
1669     }
1670 
1671     override void visit(StaticCtorDeclaration d)
1672     {
1673         if (stcToBuffer(buf, d.storage_class & ~STC.static_))
1674             buf.writeByte(' ');
1675         if (d.isSharedStaticCtorDeclaration())
1676             buf.writestring("shared ");
1677         buf.writestring("static this()");
1678         if (hgs.hdrgen && !hgs.tpltMember)
1679         {
1680             buf.writeByte(';');
1681             buf.writenl();
1682         }
1683         else
1684             bodyToBuffer(d);
1685     }
1686 
1687     override void visit(StaticDtorDeclaration d)
1688     {
1689         if (stcToBuffer(buf, d.storage_class & ~STC.static_))
1690             buf.writeByte(' ');
1691         if (d.isSharedStaticDtorDeclaration())
1692             buf.writestring("shared ");
1693         buf.writestring("static ~this()");
1694         if (hgs.hdrgen && !hgs.tpltMember)
1695         {
1696             buf.writeByte(';');
1697             buf.writenl();
1698         }
1699         else
1700             bodyToBuffer(d);
1701     }
1702 
1703     override void visit(InvariantDeclaration d)
1704     {
1705         if (hgs.hdrgen)
1706             return;
1707         if (stcToBuffer(buf, d.storage_class))
1708             buf.writeByte(' ');
1709         buf.writestring("invariant");
1710         if(auto es = d.fbody.isExpStatement())
1711         {
1712             assert(es.exp && es.exp.op == TOK.assert_);
1713             buf.writestring(" (");
1714             (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
1715             buf.writestring(");");
1716             buf.writenl();
1717         }
1718         else
1719         {
1720             bodyToBuffer(d);
1721         }
1722     }
1723 
1724     override void visit(UnitTestDeclaration d)
1725     {
1726         if (hgs.hdrgen)
1727             return;
1728         if (stcToBuffer(buf, d.storage_class))
1729             buf.writeByte(' ');
1730         buf.writestring("unittest");
1731         bodyToBuffer(d);
1732     }
1733 
1734     override void visit(NewDeclaration d)
1735     {
1736         if (stcToBuffer(buf, d.storage_class & ~STC.static_))
1737             buf.writeByte(' ');
1738         buf.writestring("new");
1739         parametersToBuffer(ParameterList(d.parameters, d.varargs), buf, hgs);
1740         bodyToBuffer(d);
1741     }
1742 
1743     override void visit(Module m)
1744     {
1745         moduleToBuffer2(m, buf, hgs);
1746     }
1747 }
1748 
1749 private extern (C++) final class ExpressionPrettyPrintVisitor : Visitor
1750 {
1751     alias visit = Visitor.visit;
1752 public:
1753     OutBuffer* buf;
1754     HdrGenState* hgs;
1755 
1756     extern (D) this(OutBuffer* buf, HdrGenState* hgs)
1757     {
1758         this.buf = buf;
1759         this.hgs = hgs;
1760     }
1761 
1762     ////////////////////////////////////////////////////////////////////////////
1763     override void visit(Expression e)
1764     {
1765         buf.writestring(Token.toString(e.op));
1766     }
1767 
1768     override void visit(IntegerExp e)
1769     {
1770         const dinteger_t v = e.toInteger();
1771         if (e.type)
1772         {
1773             Type t = e.type;
1774         L1:
1775             switch (t.ty)
1776             {
1777             case Tenum:
1778                 {
1779                     TypeEnum te = cast(TypeEnum)t;
1780                     if (hgs.fullDump)
1781                     {
1782                         auto sym = te.sym;
1783                         if (hgs.inEnumDecl && sym && hgs.inEnumDecl != sym)  foreach(i;0 .. sym.members.dim)
1784                         {
1785                             EnumMember em = cast(EnumMember) (*sym.members)[i];
1786                             if (em.value.toInteger == v)
1787                             {
1788                                 buf.printf("%s.%s", sym.toChars(), em.ident.toChars());
1789                                 return ;
1790                             }
1791                         }
1792                         //assert(0, "We could not find the EmumMember");// for some reason it won't append char* ~ e.toChars() ~ " in " ~ sym.toChars() );
1793                     }
1794 
1795                     buf.printf("cast(%s)", te.sym.toChars());
1796                     t = te.sym.memtype;
1797                     goto L1;
1798                 }
1799             case Twchar:
1800                 // BUG: need to cast(wchar)
1801             case Tdchar:
1802                 // BUG: need to cast(dchar)
1803                 if (cast(uinteger_t)v > 0xFF)
1804                 {
1805                     buf.printf("'\\U%08llx'", cast(long)v);
1806                     break;
1807                 }
1808                 goto case;
1809             case Tchar:
1810                 {
1811                     size_t o = buf.length;
1812                     if (v == '\'')
1813                         buf.writestring("'\\''");
1814                     else if (isprint(cast(int)v) && v != '\\')
1815                         buf.printf("'%c'", cast(int)v);
1816                     else
1817                         buf.printf("'\\x%02x'", cast(int)v);
1818                     if (hgs.ddoc)
1819                         escapeDdocString(buf, o);
1820                     break;
1821                 }
1822             case Tint8:
1823                 buf.writestring("cast(byte)");
1824                 goto L2;
1825             case Tint16:
1826                 buf.writestring("cast(short)");
1827                 goto L2;
1828             case Tint32:
1829             L2:
1830                 buf.printf("%d", cast(int)v);
1831                 break;
1832             case Tuns8:
1833                 buf.writestring("cast(ubyte)");
1834                 goto case Tuns32;
1835             case Tuns16:
1836                 buf.writestring("cast(ushort)");
1837                 goto case Tuns32;
1838             case Tuns32:
1839                 buf.printf("%uu", cast(uint)v);
1840                 break;
1841             case Tint64:
1842                 buf.printf("%lldL", v);
1843                 break;
1844             case Tuns64:
1845                 buf.printf("%lluLU", v);
1846                 break;
1847             case Tbool:
1848                 buf.writestring(v ? "true" : "false");
1849                 break;
1850             case Tpointer:
1851                 buf.writestring("cast(");
1852                 buf.writestring(t.toChars());
1853                 buf.writeByte(')');
1854                 if (target.ptrsize == 8)
1855                     goto case Tuns64;
1856                 else
1857                     goto case Tuns32;
1858             default:
1859                 /* This can happen if errors, such as
1860                  * the type is painted on like in fromConstInitializer().
1861                  */
1862                 if (!global.errors)
1863                 {
1864                     assert(0);
1865                 }
1866                 break;
1867             }
1868         }
1869         else if (v & 0x8000000000000000L)
1870             buf.printf("0x%llx", v);
1871         else
1872             buf.print(v);
1873     }
1874 
1875     override void visit(ErrorExp e)
1876     {
1877         buf.writestring("__error");
1878     }
1879 
1880     override void visit(VoidInitExp e)
1881     {
1882         buf.writestring("__void");
1883     }
1884 
1885     void floatToBuffer(Type type, real_t value)
1886     {
1887         /** sizeof(value)*3 is because each byte of mantissa is max
1888          of 256 (3 characters). The string will be "-M.MMMMe-4932".
1889          (ie, 8 chars more than mantissa). Plus one for trailing \0.
1890          Plus one for rounding. */
1891         const(size_t) BUFFER_LEN = value.sizeof * 3 + 8 + 1 + 1;
1892         char[BUFFER_LEN] buffer;
1893         CTFloat.sprint(buffer.ptr, 'g', value);
1894         assert(strlen(buffer.ptr) < BUFFER_LEN);
1895         if (hgs.hdrgen)
1896         {
1897             real_t r = CTFloat.parse(buffer.ptr);
1898             if (r != value) // if exact duplication
1899                 CTFloat.sprint(buffer.ptr, 'a', value);
1900         }
1901         buf.writestring(buffer.ptr);
1902         if (buffer.ptr[strlen(buffer.ptr) - 1] == '.')
1903             buf.remove(buf.length() - 1, 1);
1904 
1905         if (type)
1906         {
1907             Type t = type.toBasetype();
1908             switch (t.ty)
1909             {
1910             case Tfloat32:
1911             case Timaginary32:
1912             case Tcomplex32:
1913                 buf.writeByte('F');
1914                 break;
1915             case Tfloat80:
1916             case Timaginary80:
1917             case Tcomplex80:
1918                 buf.writeByte('L');
1919                 break;
1920             default:
1921                 break;
1922             }
1923             if (t.isimaginary())
1924                 buf.writeByte('i');
1925         }
1926     }
1927 
1928     override void visit(RealExp e)
1929     {
1930         floatToBuffer(e.type, e.value);
1931     }
1932 
1933     override void visit(ComplexExp e)
1934     {
1935         /* Print as:
1936          *  (re+imi)
1937          */
1938         buf.writeByte('(');
1939         floatToBuffer(e.type, creall(e.value));
1940         buf.writeByte('+');
1941         floatToBuffer(e.type, cimagl(e.value));
1942         buf.writestring("i)");
1943     }
1944 
1945     override void visit(IdentifierExp e)
1946     {
1947         if (hgs.hdrgen || hgs.ddoc)
1948             buf.writestring(e.ident.toHChars2());
1949         else
1950             buf.writestring(e.ident.toString());
1951     }
1952 
1953     override void visit(DsymbolExp e)
1954     {
1955         buf.writestring(e.s.toChars());
1956     }
1957 
1958     override void visit(ThisExp e)
1959     {
1960         buf.writestring("this");
1961     }
1962 
1963     override void visit(SuperExp e)
1964     {
1965         buf.writestring("super");
1966     }
1967 
1968     override void visit(NullExp e)
1969     {
1970         buf.writestring("null");
1971     }
1972 
1973     override void visit(StringExp e)
1974     {
1975         buf.writeByte('"');
1976         const o = buf.length;
1977         for (size_t i = 0; i < e.len; i++)
1978         {
1979             const c = e.charAt(i);
1980             switch (c)
1981             {
1982             case '"':
1983             case '\\':
1984                 buf.writeByte('\\');
1985                 goto default;
1986             default:
1987                 if (c <= 0xFF)
1988                 {
1989                     if (c <= 0x7F && isprint(c))
1990                         buf.writeByte(c);
1991                     else
1992                         buf.printf("\\x%02x", c);
1993                 }
1994                 else if (c <= 0xFFFF)
1995                     buf.printf("\\x%02x\\x%02x", c & 0xFF, c >> 8);
1996                 else
1997                     buf.printf("\\x%02x\\x%02x\\x%02x\\x%02x", c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF, c >> 24);
1998                 break;
1999             }
2000         }
2001         if (hgs.ddoc)
2002             escapeDdocString(buf, o);
2003         buf.writeByte('"');
2004         if (e.postfix)
2005             buf.writeByte(e.postfix);
2006     }
2007 
2008     override void visit(ArrayLiteralExp e)
2009     {
2010         buf.writeByte('[');
2011         argsToBuffer(e.elements, buf, hgs, e.basis);
2012         buf.writeByte(']');
2013     }
2014 
2015     override void visit(AssocArrayLiteralExp e)
2016     {
2017         buf.writeByte('[');
2018         foreach (i, key; *e.keys)
2019         {
2020             if (i)
2021                 buf.writestring(", ");
2022             expToBuffer(key, PREC.assign, buf, hgs);
2023             buf.writeByte(':');
2024             auto value = (*e.values)[i];
2025             expToBuffer(value, PREC.assign, buf, hgs);
2026         }
2027         buf.writeByte(']');
2028     }
2029 
2030     override void visit(StructLiteralExp e)
2031     {
2032         buf.writestring(e.sd.toChars());
2033         buf.writeByte('(');
2034         // CTFE can generate struct literals that contain an AddrExp pointing
2035         // to themselves, need to avoid infinite recursion:
2036         // struct S { this(int){ this.s = &this; } S* s; }
2037         // const foo = new S(0);
2038         if (e.stageflags & stageToCBuffer)
2039             buf.writestring("<recursion>");
2040         else
2041         {
2042             const old = e.stageflags;
2043             e.stageflags |= stageToCBuffer;
2044             argsToBuffer(e.elements, buf, hgs);
2045             e.stageflags = old;
2046         }
2047         buf.writeByte(')');
2048     }
2049 
2050     override void visit(TypeExp e)
2051     {
2052         typeToBuffer(e.type, null, buf, hgs);
2053     }
2054 
2055     override void visit(ScopeExp e)
2056     {
2057         if (e.sds.isTemplateInstance())
2058         {
2059             e.sds.dsymbolToBuffer(buf, hgs);
2060         }
2061         else if (hgs !is null && hgs.ddoc)
2062         {
2063             // fixes bug 6491
2064             if (auto m = e.sds.isModule())
2065                 buf.writestring(m.md.toChars());
2066             else
2067                 buf.writestring(e.sds.toChars());
2068         }
2069         else
2070         {
2071             buf.writestring(e.sds.kind());
2072             buf.writeByte(' ');
2073             buf.writestring(e.sds.toChars());
2074         }
2075     }
2076 
2077     override void visit(TemplateExp e)
2078     {
2079         buf.writestring(e.td.toChars());
2080     }
2081 
2082     override void visit(NewExp e)
2083     {
2084         if (e.thisexp)
2085         {
2086             expToBuffer(e.thisexp, PREC.primary, buf, hgs);
2087             buf.writeByte('.');
2088         }
2089         buf.writestring("new ");
2090         if (e.newargs && e.newargs.dim)
2091         {
2092             buf.writeByte('(');
2093             argsToBuffer(e.newargs, buf, hgs);
2094             buf.writeByte(')');
2095         }
2096         typeToBuffer(e.newtype, null, buf, hgs);
2097         if (e.arguments && e.arguments.dim)
2098         {
2099             buf.writeByte('(');
2100             argsToBuffer(e.arguments, buf, hgs);
2101             buf.writeByte(')');
2102         }
2103     }
2104 
2105     override void visit(NewAnonClassExp e)
2106     {
2107         if (e.thisexp)
2108         {
2109             expToBuffer(e.thisexp, PREC.primary, buf, hgs);
2110             buf.writeByte('.');
2111         }
2112         buf.writestring("new");
2113         if (e.newargs && e.newargs.dim)
2114         {
2115             buf.writeByte('(');
2116             argsToBuffer(e.newargs, buf, hgs);
2117             buf.writeByte(')');
2118         }
2119         buf.writestring(" class ");
2120         if (e.arguments && e.arguments.dim)
2121         {
2122             buf.writeByte('(');
2123             argsToBuffer(e.arguments, buf, hgs);
2124             buf.writeByte(')');
2125         }
2126         if (e.cd)
2127             e.cd.dsymbolToBuffer(buf, hgs);
2128     }
2129 
2130     override void visit(SymOffExp e)
2131     {
2132         if (e.offset)
2133             buf.printf("(& %s%+lld)", e.var.toChars(), e.offset);
2134         else if (e.var.isTypeInfoDeclaration())
2135             buf.writestring(e.var.toChars());
2136         else
2137             buf.printf("& %s", e.var.toChars());
2138     }
2139 
2140     override void visit(VarExp e)
2141     {
2142         buf.writestring(e.var.toChars());
2143     }
2144 
2145     override void visit(OverExp e)
2146     {
2147         buf.writestring(e.vars.ident.toString());
2148     }
2149 
2150     override void visit(TupleExp e)
2151     {
2152         if (e.e0)
2153         {
2154             buf.writeByte('(');
2155             e.e0.accept(this);
2156             buf.writestring(", tuple(");
2157             argsToBuffer(e.exps, buf, hgs);
2158             buf.writestring("))");
2159         }
2160         else
2161         {
2162             buf.writestring("tuple(");
2163             argsToBuffer(e.exps, buf, hgs);
2164             buf.writeByte(')');
2165         }
2166     }
2167 
2168     override void visit(FuncExp e)
2169     {
2170         e.fd.dsymbolToBuffer(buf, hgs);
2171         //buf.writestring(e.fd.toChars());
2172     }
2173 
2174     override void visit(DeclarationExp e)
2175     {
2176         /* Normal dmd execution won't reach here - regular variable declarations
2177          * are handled in visit(ExpStatement), so here would be used only when
2178          * we'll directly call Expression.toChars() for debugging.
2179          */
2180         if (e.declaration)
2181         {
2182             if (auto var = e.declaration.isVarDeclaration())
2183             {
2184             // For debugging use:
2185             // - Avoid printing newline.
2186             // - Intentionally use the format (Type var;)
2187             //   which isn't correct as regular D code.
2188                 buf.writeByte('(');
2189 
2190                 scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
2191                 v.visitVarDecl(var, false);
2192 
2193                 buf.writeByte(';');
2194                 buf.writeByte(')');
2195             }
2196             else e.declaration.dsymbolToBuffer(buf, hgs);
2197         }
2198     }
2199 
2200     override void visit(TypeidExp e)
2201     {
2202         buf.writestring("typeid(");
2203         objectToBuffer(e.obj, buf, hgs);
2204         buf.writeByte(')');
2205     }
2206 
2207     override void visit(TraitsExp e)
2208     {
2209         buf.writestring("__traits(");
2210         if (e.ident)
2211             buf.writestring(e.ident.toString());
2212         if (e.args)
2213         {
2214             foreach (arg; *e.args)
2215             {
2216                 buf.writestring(", ");
2217                 objectToBuffer(arg, buf, hgs);
2218             }
2219         }
2220         buf.writeByte(')');
2221     }
2222 
2223     override void visit(HaltExp e)
2224     {
2225         buf.writestring("halt");
2226     }
2227 
2228     override void visit(IsExp e)
2229     {
2230         buf.writestring("is(");
2231         typeToBuffer(e.targ, e.id, buf, hgs);
2232         if (e.tok2 != TOK.reserved)
2233         {
2234             buf.printf(" %s %s", Token.toChars(e.tok), Token.toChars(e.tok2));
2235         }
2236         else if (e.tspec)
2237         {
2238             if (e.tok == TOK.colon)
2239                 buf.writestring(" : ");
2240             else
2241                 buf.writestring(" == ");
2242             typeToBuffer(e.tspec, null, buf, hgs);
2243         }
2244         if (e.parameters && e.parameters.dim)
2245         {
2246             buf.writestring(", ");
2247             scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
2248             v.visitTemplateParameters(e.parameters);
2249         }
2250         buf.writeByte(')');
2251     }
2252 
2253     override void visit(UnaExp e)
2254     {
2255         buf.writestring(Token.toString(e.op));
2256         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2257     }
2258 
2259     override void visit(BinExp e)
2260     {
2261         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2262         buf.writeByte(' ');
2263         buf.writestring(Token.toString(e.op));
2264         buf.writeByte(' ');
2265         expToBuffer(e.e2, cast(PREC)(precedence[e.op] + 1), buf, hgs);
2266     }
2267 
2268     override void visit(CompileExp e)
2269     {
2270         buf.writestring("mixin(");
2271         argsToBuffer(e.exps, buf, hgs, null);
2272         buf.writeByte(')');
2273     }
2274 
2275     override void visit(ImportExp e)
2276     {
2277         buf.writestring("import(");
2278         expToBuffer(e.e1, PREC.assign, buf, hgs);
2279         buf.writeByte(')');
2280     }
2281 
2282     override void visit(AssertExp e)
2283     {
2284         buf.writestring("assert(");
2285         expToBuffer(e.e1, PREC.assign, buf, hgs);
2286         if (e.msg)
2287         {
2288             buf.writestring(", ");
2289             expToBuffer(e.msg, PREC.assign, buf, hgs);
2290         }
2291         buf.writeByte(')');
2292     }
2293 
2294     override void visit(DotIdExp e)
2295     {
2296         expToBuffer(e.e1, PREC.primary, buf, hgs);
2297         buf.writeByte('.');
2298         buf.writestring(e.ident.toString());
2299     }
2300 
2301     override void visit(DotTemplateExp e)
2302     {
2303         expToBuffer(e.e1, PREC.primary, buf, hgs);
2304         buf.writeByte('.');
2305         buf.writestring(e.td.toChars());
2306     }
2307 
2308     override void visit(DotVarExp e)
2309     {
2310         expToBuffer(e.e1, PREC.primary, buf, hgs);
2311         buf.writeByte('.');
2312         buf.writestring(e.var.toChars());
2313     }
2314 
2315     override void visit(DotTemplateInstanceExp e)
2316     {
2317         expToBuffer(e.e1, PREC.primary, buf, hgs);
2318         buf.writeByte('.');
2319         e.ti.dsymbolToBuffer(buf, hgs);
2320     }
2321 
2322     override void visit(DelegateExp e)
2323     {
2324         buf.writeByte('&');
2325         if (!e.func.isNested() || e.func.needThis())
2326         {
2327             expToBuffer(e.e1, PREC.primary, buf, hgs);
2328             buf.writeByte('.');
2329         }
2330         buf.writestring(e.func.toChars());
2331     }
2332 
2333     override void visit(DotTypeExp e)
2334     {
2335         expToBuffer(e.e1, PREC.primary, buf, hgs);
2336         buf.writeByte('.');
2337         buf.writestring(e.sym.toChars());
2338     }
2339 
2340     override void visit(CallExp e)
2341     {
2342         if (e.e1.op == TOK.type)
2343         {
2344             /* Avoid parens around type to prevent forbidden cast syntax:
2345              *   (sometype)(arg1)
2346              * This is ok since types in constructor calls
2347              * can never depend on parens anyway
2348              */
2349             e.e1.accept(this);
2350         }
2351         else
2352             expToBuffer(e.e1, precedence[e.op], buf, hgs);
2353         buf.writeByte('(');
2354         argsToBuffer(e.arguments, buf, hgs);
2355         buf.writeByte(')');
2356     }
2357 
2358     override void visit(PtrExp e)
2359     {
2360         buf.writeByte('*');
2361         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2362     }
2363 
2364     override void visit(DeleteExp e)
2365     {
2366         buf.writestring("delete ");
2367         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2368     }
2369 
2370     override void visit(CastExp e)
2371     {
2372         buf.writestring("cast(");
2373         if (e.to)
2374             typeToBuffer(e.to, null, buf, hgs);
2375         else
2376         {
2377             MODtoBuffer(buf, e.mod);
2378         }
2379         buf.writeByte(')');
2380         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2381     }
2382 
2383     override void visit(VectorExp e)
2384     {
2385         buf.writestring("cast(");
2386         typeToBuffer(e.to, null, buf, hgs);
2387         buf.writeByte(')');
2388         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2389     }
2390 
2391     override void visit(VectorArrayExp e)
2392     {
2393         expToBuffer(e.e1, PREC.primary, buf, hgs);
2394         buf.writestring(".array");
2395     }
2396 
2397     override void visit(SliceExp e)
2398     {
2399         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2400         buf.writeByte('[');
2401         if (e.upr || e.lwr)
2402         {
2403             if (e.lwr)
2404                 sizeToBuffer(e.lwr, buf, hgs);
2405             else
2406                 buf.writeByte('0');
2407             buf.writestring("..");
2408             if (e.upr)
2409                 sizeToBuffer(e.upr, buf, hgs);
2410             else
2411                 buf.writeByte('$');
2412         }
2413         buf.writeByte(']');
2414     }
2415 
2416     override void visit(ArrayLengthExp e)
2417     {
2418         expToBuffer(e.e1, PREC.primary, buf, hgs);
2419         buf.writestring(".length");
2420     }
2421 
2422     override void visit(IntervalExp e)
2423     {
2424         expToBuffer(e.lwr, PREC.assign, buf, hgs);
2425         buf.writestring("..");
2426         expToBuffer(e.upr, PREC.assign, buf, hgs);
2427     }
2428 
2429     override void visit(DelegatePtrExp e)
2430     {
2431         expToBuffer(e.e1, PREC.primary, buf, hgs);
2432         buf.writestring(".ptr");
2433     }
2434 
2435     override void visit(DelegateFuncptrExp e)
2436     {
2437         expToBuffer(e.e1, PREC.primary, buf, hgs);
2438         buf.writestring(".funcptr");
2439     }
2440 
2441     override void visit(ArrayExp e)
2442     {
2443         expToBuffer(e.e1, PREC.primary, buf, hgs);
2444         buf.writeByte('[');
2445         argsToBuffer(e.arguments, buf, hgs);
2446         buf.writeByte(']');
2447     }
2448 
2449     override void visit(DotExp e)
2450     {
2451         expToBuffer(e.e1, PREC.primary, buf, hgs);
2452         buf.writeByte('.');
2453         expToBuffer(e.e2, PREC.primary, buf, hgs);
2454     }
2455 
2456     override void visit(IndexExp e)
2457     {
2458         expToBuffer(e.e1, PREC.primary, buf, hgs);
2459         buf.writeByte('[');
2460         sizeToBuffer(e.e2, buf, hgs);
2461         buf.writeByte(']');
2462     }
2463 
2464     override void visit(PostExp e)
2465     {
2466         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2467         buf.writestring(Token.toString(e.op));
2468     }
2469 
2470     override void visit(PreExp e)
2471     {
2472         buf.writestring(Token.toString(e.op));
2473         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2474     }
2475 
2476     override void visit(RemoveExp e)
2477     {
2478         expToBuffer(e.e1, PREC.primary, buf, hgs);
2479         buf.writestring(".remove(");
2480         expToBuffer(e.e2, PREC.assign, buf, hgs);
2481         buf.writeByte(')');
2482     }
2483 
2484     override void visit(CondExp e)
2485     {
2486         expToBuffer(e.econd, PREC.oror, buf, hgs);
2487         buf.writestring(" ? ");
2488         expToBuffer(e.e1, PREC.expr, buf, hgs);
2489         buf.writestring(" : ");
2490         expToBuffer(e.e2, PREC.cond, buf, hgs);
2491     }
2492 
2493     override void visit(DefaultInitExp e)
2494     {
2495         buf.writestring(Token.toString(e.subop));
2496     }
2497 
2498     override void visit(ClassReferenceExp e)
2499     {
2500         buf.writestring(e.value.toChars());
2501     }
2502 }
2503 
2504 
2505 private void templateParameterToBuffer(TemplateParameter tp, OutBuffer* buf, HdrGenState* hgs)
2506 {
2507     scope v = new TemplateParameterPrettyPrintVisitor(buf, hgs);
2508     tp.accept(v);
2509 }
2510 
2511 private extern (C++) final class TemplateParameterPrettyPrintVisitor : Visitor
2512 {
2513     alias visit = Visitor.visit;
2514 public:
2515     OutBuffer* buf;
2516     HdrGenState* hgs;
2517 
2518     extern (D) this(OutBuffer* buf, HdrGenState* hgs)
2519     {
2520         this.buf = buf;
2521         this.hgs = hgs;
2522     }
2523 
2524     override void visit(TemplateTypeParameter tp)
2525     {
2526         buf.writestring(tp.ident.toString());
2527         if (tp.specType)
2528         {
2529             buf.writestring(" : ");
2530             typeToBuffer(tp.specType, null, buf, hgs);
2531         }
2532         if (tp.defaultType)
2533         {
2534             buf.writestring(" = ");
2535             typeToBuffer(tp.defaultType, null, buf, hgs);
2536         }
2537     }
2538 
2539     override void visit(TemplateThisParameter tp)
2540     {
2541         buf.writestring("this ");
2542         visit(cast(TemplateTypeParameter)tp);
2543     }
2544 
2545     override void visit(TemplateAliasParameter tp)
2546     {
2547         buf.writestring("alias ");
2548         if (tp.specType)
2549             typeToBuffer(tp.specType, tp.ident, buf, hgs);
2550         else
2551             buf.writestring(tp.ident.toString());
2552         if (tp.specAlias)
2553         {
2554             buf.writestring(" : ");
2555             objectToBuffer(tp.specAlias, buf, hgs);
2556         }
2557         if (tp.defaultAlias)
2558         {
2559             buf.writestring(" = ");
2560             objectToBuffer(tp.defaultAlias, buf, hgs);
2561         }
2562     }
2563 
2564     override void visit(TemplateValueParameter tp)
2565     {
2566         typeToBuffer(tp.valType, tp.ident, buf, hgs);
2567         if (tp.specValue)
2568         {
2569             buf.writestring(" : ");
2570             tp.specValue.expressionToBuffer(buf, hgs);
2571         }
2572         if (tp.defaultValue)
2573         {
2574             buf.writestring(" = ");
2575             tp.defaultValue.expressionToBuffer(buf, hgs);
2576         }
2577     }
2578 
2579     override void visit(TemplateTupleParameter tp)
2580     {
2581         buf.writestring(tp.ident.toString());
2582         buf.writestring("...");
2583     }
2584 }
2585 
2586 private void conditionToBuffer(Condition c, OutBuffer* buf, HdrGenState* hgs)
2587 {
2588     scope v = new ConditionPrettyPrintVisitor(buf, hgs);
2589     c.accept(v);
2590 }
2591 
2592 private extern (C++) final class ConditionPrettyPrintVisitor : Visitor
2593 {
2594     alias visit = Visitor.visit;
2595 public:
2596     OutBuffer* buf;
2597     HdrGenState* hgs;
2598 
2599     extern (D) this(OutBuffer* buf, HdrGenState* hgs)
2600     {
2601         this.buf = buf;
2602         this.hgs = hgs;
2603     }
2604 
2605     override void visit(DebugCondition c)
2606     {
2607         buf.writestring("debug (");
2608         if (c.ident)
2609             buf.writestring(c.ident.toString());
2610         else
2611             buf.print(c.level);
2612         buf.writeByte(')');
2613     }
2614 
2615     override void visit(VersionCondition c)
2616     {
2617         buf.writestring("version (");
2618         if (c.ident)
2619             buf.writestring(c.ident.toString());
2620         else
2621             buf.print(c.level);
2622         buf.writeByte(')');
2623     }
2624 
2625     override void visit(StaticIfCondition c)
2626     {
2627         buf.writestring("static if (");
2628         c.exp.expressionToBuffer(buf, hgs);
2629         buf.writeByte(')');
2630     }
2631 }
2632 
2633 void toCBuffer(const Statement s, OutBuffer* buf, HdrGenState* hgs)
2634 {
2635     scope v = new StatementPrettyPrintVisitor(buf, hgs);
2636     (cast() s).accept(v);
2637 }
2638 
2639 void toCBuffer(const Type t, OutBuffer* buf, const Identifier ident, HdrGenState* hgs)
2640 {
2641     typeToBuffer(cast() t, ident, buf, hgs);
2642 }
2643 
2644 void toCBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs)
2645 {
2646     scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
2647     s.accept(v);
2648 }
2649 
2650 // used from TemplateInstance::toChars() and TemplateMixin::toChars()
2651 void toCBufferInstance(const TemplateInstance ti, OutBuffer* buf, bool qualifyTypes = false)
2652 {
2653     HdrGenState hgs;
2654     hgs.fullQual = qualifyTypes;
2655     scope v = new DsymbolPrettyPrintVisitor(buf, &hgs);
2656     v.visit(cast() ti);
2657 }
2658 
2659 void toCBuffer(const Initializer iz, OutBuffer* buf, HdrGenState* hgs)
2660 {
2661     initializerToBuffer(cast() iz, buf, hgs);
2662 }
2663 
2664 bool stcToBuffer(OutBuffer* buf, StorageClass stc)
2665 {
2666     bool result = false;
2667     if ((stc & (STC.return_ | STC.scope_)) == (STC.return_ | STC.scope_))
2668         stc &= ~STC.scope_;
2669     if (stc & STC.scopeinferred)
2670         stc &= ~(STC.scope_ | STC.scopeinferred);
2671     while (stc)
2672     {
2673         const s = stcToString(stc);
2674         if (!s.length)
2675             break;
2676         if (result)
2677             buf.writeByte(' ');
2678         result = true;
2679         buf.writestring(s);
2680     }
2681     return result;
2682 }
2683 
2684 /*************************************************
2685  * Pick off one of the storage classes from stc,
2686  * and return a string representation of it.
2687  * stc is reduced by the one picked.
2688  */
2689 string stcToString(ref StorageClass stc)
2690 {
2691     struct SCstring
2692     {
2693         StorageClass stc;
2694         TOK tok;
2695         string id;
2696     }
2697 
2698     __gshared SCstring* table =
2699     [
2700         SCstring(STC.auto_, TOK.auto_),
2701         SCstring(STC.scope_, TOK.scope_),
2702         SCstring(STC.static_, TOK.static_),
2703         SCstring(STC.extern_, TOK.extern_),
2704         SCstring(STC.const_, TOK.const_),
2705         SCstring(STC.final_, TOK.final_),
2706         SCstring(STC.abstract_, TOK.abstract_),
2707         SCstring(STC.synchronized_, TOK.synchronized_),
2708         SCstring(STC.deprecated_, TOK.deprecated_),
2709         SCstring(STC.override_, TOK.override_),
2710         SCstring(STC.lazy_, TOK.lazy_),
2711         SCstring(STC.alias_, TOK.alias_),
2712         SCstring(STC.out_, TOK.out_),
2713         SCstring(STC.in_, TOK.in_),
2714         SCstring(STC.manifest, TOK.enum_),
2715         SCstring(STC.immutable_, TOK.immutable_),
2716         SCstring(STC.shared_, TOK.shared_),
2717         SCstring(STC.nothrow_, TOK.nothrow_),
2718         SCstring(STC.wild, TOK.inout_),
2719         SCstring(STC.pure_, TOK.pure_),
2720         SCstring(STC.ref_, TOK.ref_),
2721         SCstring(STC.return_, TOK.return_),
2722         SCstring(STC.tls),
2723         SCstring(STC.gshared, TOK.gshared),
2724         SCstring(STC.nogc, TOK.at, "@nogc"),
2725         SCstring(STC.property, TOK.at, "@property"),
2726         SCstring(STC.safe, TOK.at, "@safe"),
2727         SCstring(STC.trusted, TOK.at, "@trusted"),
2728         SCstring(STC.system, TOK.at, "@system"),
2729         SCstring(STC.disable, TOK.at, "@disable"),
2730         SCstring(STC.future, TOK.at, "@__future"),
2731         SCstring(STC.local, TOK.at, "__local"),
2732         SCstring(0, TOK.reserved)
2733     ];
2734     for (int i = 0; table[i].stc; i++)
2735     {
2736         StorageClass tbl = table[i].stc;
2737         assert(tbl & STCStorageClass);
2738         if (stc & tbl)
2739         {
2740             stc &= ~tbl;
2741             if (tbl == STC.tls) // TOKtls was removed
2742                 return "__thread";
2743             TOK tok = table[i].tok;
2744             if (tok != TOK.at && !table[i].id.length)
2745                 table[i].id = Token.toString(tok); // lazilly initialize table
2746             return table[i].id;
2747         }
2748     }
2749     //printf("stc = %llx\n", stc);
2750     return null;
2751 }
2752 
2753 const(char)* stcToChars(ref StorageClass stc)
2754 {
2755     const s = stcToString(stc);
2756     return &s[0];  // assume 0 terminated
2757 }
2758 
2759 
2760 /// Ditto
2761 extern (D) string trustToString(TRUST trust) pure nothrow
2762 {
2763     final switch (trust)
2764     {
2765     case TRUST.default_:
2766         return null;
2767     case TRUST.system:
2768         return "@system";
2769     case TRUST.trusted:
2770         return "@trusted";
2771     case TRUST.safe:
2772         return "@safe";
2773     }
2774 }
2775 
2776 private void linkageToBuffer(OutBuffer* buf, LINK linkage)
2777 {
2778     const s = linkageToString(linkage);
2779     if (s.length)
2780     {
2781         buf.writestring("extern (");
2782         buf.writestring(s);
2783         buf.writeByte(')');
2784     }
2785 }
2786 
2787 const(char)* linkageToChars(LINK linkage)
2788 {
2789     /// Works because we return a literal
2790     return linkageToString(linkage).ptr;
2791 }
2792 
2793 string linkageToString(LINK linkage) pure nothrow
2794 {
2795     final switch (linkage)
2796     {
2797     case LINK.default_:
2798         return null;
2799     case LINK.d:
2800         return "D";
2801     case LINK.c:
2802         return "C";
2803     case LINK.cpp:
2804         return "C++";
2805     case LINK.windows:
2806         return "Windows";
2807     case LINK.pascal:
2808         return "Pascal";
2809     case LINK.objc:
2810         return "Objective-C";
2811     case LINK.system:
2812         return "System";
2813     }
2814 }
2815 
2816 void protectionToBuffer(OutBuffer* buf, Prot prot)
2817 {
2818     buf.writestring(protectionToString(prot.kind));
2819     if (prot.kind == Prot.Kind.package_ && prot.pkg)
2820     {
2821         buf.writeByte('(');
2822         buf.writestring(prot.pkg.toPrettyChars(true));
2823         buf.writeByte(')');
2824     }
2825 }
2826 
2827 /**
2828  * Returns:
2829  *   a human readable representation of `kind`
2830  */
2831 const(char)* protectionToChars(Prot.Kind kind)
2832 {
2833     // Null terminated because we return a literal
2834     return protectionToString(kind).ptr;
2835 }
2836 
2837 /// Ditto
2838 extern (D) string protectionToString(Prot.Kind kind) nothrow pure
2839 {
2840     final switch (kind)
2841     {
2842     case Prot.Kind.undefined:
2843         return null;
2844     case Prot.Kind.none:
2845         return "none";
2846     case Prot.Kind.private_:
2847         return "private";
2848     case Prot.Kind.package_:
2849         return "package";
2850     case Prot.Kind.protected_:
2851         return "protected";
2852     case Prot.Kind.public_:
2853         return "public";
2854     case Prot.Kind.export_:
2855         return "export";
2856     }
2857 }
2858 
2859 // Print the full function signature with correct ident, attributes and template args
2860 void functionToBufferFull(TypeFunction tf, OutBuffer* buf, const Identifier ident, HdrGenState* hgs, TemplateDeclaration td)
2861 {
2862     //printf("TypeFunction::toCBuffer() this = %p\n", this);
2863     visitFuncIdentWithPrefix(tf, ident, td, buf, hgs);
2864 }
2865 
2866 // ident is inserted before the argument list and will be "function" or "delegate" for a type
2867 void functionToBufferWithIdent(TypeFunction tf, OutBuffer* buf, const(char)* ident)
2868 {
2869     HdrGenState hgs;
2870     visitFuncIdentWithPostfix(tf, ident.toDString(), buf, &hgs);
2871 }
2872 
2873 void toCBuffer(const Expression e, OutBuffer* buf, HdrGenState* hgs)
2874 {
2875     scope v = new ExpressionPrettyPrintVisitor(buf, hgs);
2876     (cast() e).accept(v);
2877 }
2878 
2879 /**************************************************
2880  * Write out argument types to buf.
2881  */
2882 void argExpTypesToCBuffer(OutBuffer* buf, Expressions* arguments)
2883 {
2884     if (!arguments || !arguments.dim)
2885         return;
2886     HdrGenState hgs;
2887     foreach (i, arg; *arguments)
2888     {
2889         if (i)
2890             buf.writestring(", ");
2891         typeToBuffer(arg.type, null, buf, &hgs);
2892     }
2893 }
2894 
2895 void toCBuffer(const TemplateParameter tp, OutBuffer* buf, HdrGenState* hgs)
2896 {
2897     scope v = new TemplateParameterPrettyPrintVisitor(buf, hgs);
2898     (cast() tp).accept(v);
2899 }
2900 
2901 void arrayObjectsToBuffer(OutBuffer* buf, Objects* objects)
2902 {
2903     if (!objects || !objects.dim)
2904         return;
2905     HdrGenState hgs;
2906     foreach (i, o; *objects)
2907     {
2908         if (i)
2909             buf.writestring(", ");
2910         objectToBuffer(o, buf, &hgs);
2911     }
2912 }
2913 
2914 /*************************************************************
2915  * Pretty print function parameters.
2916  * Params:
2917  *  pl = parameter list to print
2918  * Returns: Null-terminated string representing parameters.
2919  */
2920 extern (C++) const(char)* parametersTypeToChars(ParameterList pl)
2921 {
2922     OutBuffer buf;
2923     HdrGenState hgs;
2924     parametersToBuffer(pl, &buf, &hgs);
2925     return buf.extractChars();
2926 }
2927 
2928 /*************************************************************
2929  * Pretty print function parameter.
2930  * Params:
2931  *  parameter = parameter to print.
2932  *  tf = TypeFunction which holds parameter.
2933  *  fullQual = whether to fully qualify types.
2934  * Returns: Null-terminated string representing parameters.
2935  */
2936 const(char)* parameterToChars(Parameter parameter, TypeFunction tf, bool fullQual)
2937 {
2938     OutBuffer buf;
2939     HdrGenState hgs;
2940     hgs.fullQual = fullQual;
2941 
2942     parameterToBuffer(parameter, &buf, &hgs);
2943 
2944     if (tf.parameterList.varargs == VarArg.typesafe && parameter == tf.parameterList[tf.parameterList.parameters.dim - 1])
2945     {
2946         buf.writestring("...");
2947     }
2948     return buf.extractChars();
2949 }
2950 
2951 
2952 /*************************************************
2953  * Write ParameterList to buffer.
2954  * Params:
2955  *      pl = parameter list to serialize
2956  *      buf = buffer to write it to
2957  *      hgs = context
2958  */
2959 
2960 private void parametersToBuffer(ParameterList pl, OutBuffer* buf, HdrGenState* hgs)
2961 {
2962     buf.writeByte('(');
2963     foreach (i; 0 .. pl.length)
2964     {
2965         if (i)
2966             buf.writestring(", ");
2967         pl[i].parameterToBuffer(buf, hgs);
2968     }
2969     final switch (pl.varargs)
2970     {
2971         case VarArg.none:
2972             break;
2973 
2974         case VarArg.variadic:
2975             if (pl.length == 0)
2976                 goto case VarArg.typesafe;
2977             buf.writestring(", ...");
2978             break;
2979 
2980         case VarArg.typesafe:
2981             buf.writestring("...");
2982             break;
2983     }
2984     buf.writeByte(')');
2985 }
2986 
2987 
2988 /***********************************************************
2989  * Write parameter `p` to buffer `buf`.
2990  * Params:
2991  *      p = parameter to serialize
2992  *      buf = buffer to write it to
2993  *      hgs = context
2994  */
2995 private void parameterToBuffer(Parameter p, OutBuffer* buf, HdrGenState* hgs)
2996 {
2997     if (p.userAttribDecl)
2998     {
2999         buf.writeByte('@');
3000 
3001         bool isAnonymous = p.userAttribDecl.atts.dim > 0 && (*p.userAttribDecl.atts)[0].op != TOK.call;
3002         if (isAnonymous)
3003             buf.writeByte('(');
3004 
3005         argsToBuffer(p.userAttribDecl.atts, buf, hgs);
3006 
3007         if (isAnonymous)
3008             buf.writeByte(')');
3009         buf.writeByte(' ');
3010     }
3011     if (p.storageClass & STC.auto_)
3012         buf.writestring("auto ");
3013     if (p.storageClass & STC.return_)
3014         buf.writestring("return ");
3015 
3016     if (p.storageClass & STC.out_)
3017         buf.writestring("out ");
3018     else if (p.storageClass & STC.ref_)
3019         buf.writestring("ref ");
3020     else if (p.storageClass & STC.in_)
3021         buf.writestring("in ");
3022     else if (p.storageClass & STC.lazy_)
3023         buf.writestring("lazy ");
3024     else if (p.storageClass & STC.alias_)
3025         buf.writestring("alias ");
3026 
3027     StorageClass stc = p.storageClass;
3028     if (p.type && p.type.mod & MODFlags.shared_)
3029         stc &= ~STC.shared_;
3030 
3031     if (stcToBuffer(buf, stc & (STC.const_ | STC.immutable_ | STC.wild | STC.shared_ | STC.scope_ | STC.scopeinferred)))
3032         buf.writeByte(' ');
3033 
3034     if (p.storageClass & STC.alias_)
3035     {
3036         if (p.ident)
3037             buf.writestring(p.ident.toString());
3038     }
3039     else if (p.type.ty == Tident &&
3040              (cast(TypeIdentifier)p.type).ident.toString().length > 3 &&
3041              strncmp((cast(TypeIdentifier)p.type).ident.toChars(), "__T", 3) == 0)
3042     {
3043         // print parameter name, instead of undetermined type parameter
3044         buf.writestring(p.ident.toString());
3045     }
3046     else
3047     {
3048         typeToBuffer(p.type, p.ident, buf, hgs);
3049     }
3050 
3051     if (p.defaultArg)
3052     {
3053         buf.writestring(" = ");
3054         p.defaultArg.expToBuffer(PREC.assign, buf, hgs);
3055     }
3056 }
3057 
3058 
3059 /**************************************************
3060  * Write out argument list to buf.
3061  */
3062 private void argsToBuffer(Expressions* expressions, OutBuffer* buf, HdrGenState* hgs, Expression basis = null)
3063 {
3064     if (!expressions || !expressions.dim)
3065         return;
3066     version (all)
3067     {
3068         foreach (i, el; *expressions)
3069         {
3070             if (i)
3071                 buf.writestring(", ");
3072             if (!el)
3073                 el = basis;
3074             if (el)
3075                 expToBuffer(el, PREC.assign, buf, hgs);
3076         }
3077     }
3078     else
3079     {
3080         // Sparse style formatting, for debug use only
3081         //      [0..dim: basis, 1: e1, 5: e5]
3082         if (basis)
3083         {
3084             buf.writestring("0..");
3085             buf.print(expressions.dim);
3086             buf.writestring(": ");
3087             expToBuffer(basis, PREC.assign, buf, hgs);
3088         }
3089         foreach (i, el; *expressions)
3090         {
3091             if (el)
3092             {
3093                 if (basis)
3094                 {
3095                     buf.writestring(", ");
3096                     buf.print(i);
3097                     buf.writestring(": ");
3098                 }
3099                 else if (i)
3100                     buf.writestring(", ");
3101                 expToBuffer(el, PREC.assign, buf, hgs);
3102             }
3103         }
3104     }
3105 }
3106 
3107 private void sizeToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs)
3108 {
3109     if (e.type == Type.tsize_t)
3110     {
3111         Expression ex = (e.op == TOK.cast_ ? (cast(CastExp)e).e1 : e);
3112         ex = ex.optimize(WANTvalue);
3113         const dinteger_t uval = ex.op == TOK.int64 ? ex.toInteger() : cast(dinteger_t)-1;
3114         if (cast(sinteger_t)uval >= 0)
3115         {
3116             dinteger_t sizemax = void;
3117             if (target.ptrsize == 8)
3118                 sizemax = 0xFFFFFFFFFFFFFFFFUL;
3119             else if (target.ptrsize == 4)
3120                 sizemax = 0xFFFFFFFFU;
3121             else if (target.ptrsize == 2)
3122                 sizemax = 0xFFFFU;
3123             else
3124                 assert(0);
3125             if (uval <= sizemax && uval <= 0x7FFFFFFFFFFFFFFFUL)
3126             {
3127                 buf.print(uval);
3128                 return;
3129             }
3130         }
3131     }
3132     expToBuffer(e, PREC.assign, buf, hgs);
3133 }
3134 
3135 private void expressionToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs)
3136 {
3137     scope v = new ExpressionPrettyPrintVisitor(buf, hgs);
3138     e.accept(v);
3139 }
3140 
3141 /**************************************************
3142  * Write expression out to buf, but wrap it
3143  * in ( ) if its precedence is less than pr.
3144  */
3145 private void expToBuffer(Expression e, PREC pr, OutBuffer* buf, HdrGenState* hgs)
3146 {
3147     debug
3148     {
3149         if (precedence[e.op] == PREC.zero)
3150             printf("precedence not defined for token '%s'\n", Token.toChars(e.op));
3151     }
3152     if (e.op == 0xFF)
3153     {
3154         buf.writestring("<FF>");
3155         return;
3156     }
3157     assert(precedence[e.op] != PREC.zero);
3158     assert(pr != PREC.zero);
3159     /* Despite precedence, we don't allow a<b<c expressions.
3160      * They must be parenthesized.
3161      */
3162     if (precedence[e.op] < pr || (pr == PREC.rel && precedence[e.op] == pr)
3163         || (pr >= PREC.or && pr <= PREC.and && precedence[e.op] == PREC.rel))
3164     {
3165         buf.writeByte('(');
3166         e.expressionToBuffer(buf, hgs);
3167         buf.writeByte(')');
3168     }
3169     else
3170     {
3171         e.expressionToBuffer(buf, hgs);
3172     }
3173 }
3174 
3175 
3176 /**************************************************
3177  * An entry point to pretty-print type.
3178  */
3179 private void typeToBuffer(Type t, const Identifier ident, OutBuffer* buf, HdrGenState* hgs)
3180 {
3181     if (auto tf = t.isTypeFunction())
3182     {
3183         visitFuncIdentWithPrefix(tf, ident, null, buf, hgs);
3184         return;
3185     }
3186     visitWithMask(t, 0, buf, hgs);
3187     if (ident)
3188     {
3189         buf.writeByte(' ');
3190         buf.writestring(ident.toString());
3191     }
3192 }
3193 
3194 private void visitWithMask(Type t, ubyte modMask, OutBuffer* buf, HdrGenState* hgs)
3195 {
3196     // Tuples and functions don't use the type constructor syntax
3197     if (modMask == t.mod || t.ty == Tfunction || t.ty == Ttuple)
3198     {
3199         typeToBufferx(t, buf, hgs);
3200     }
3201     else
3202     {
3203         ubyte m = t.mod & ~(t.mod & modMask);
3204         if (m & MODFlags.shared_)
3205         {
3206             MODtoBuffer(buf, MODFlags.shared_);
3207             buf.writeByte('(');
3208         }
3209         if (m & MODFlags.wild)
3210         {
3211             MODtoBuffer(buf, MODFlags.wild);
3212             buf.writeByte('(');
3213         }
3214         if (m & (MODFlags.const_ | MODFlags.immutable_))
3215         {
3216             MODtoBuffer(buf, m & (MODFlags.const_ | MODFlags.immutable_));
3217             buf.writeByte('(');
3218         }
3219         typeToBufferx(t, buf, hgs);
3220         if (m & (MODFlags.const_ | MODFlags.immutable_))
3221             buf.writeByte(')');
3222         if (m & MODFlags.wild)
3223             buf.writeByte(')');
3224         if (m & MODFlags.shared_)
3225             buf.writeByte(')');
3226     }
3227 }
3228 
3229 
3230 private void dumpTemplateInstance(TemplateInstance ti, OutBuffer* buf, HdrGenState* hgs)
3231 {
3232     buf.writeByte('{');
3233     buf.writenl();
3234     buf.level++;
3235 
3236     if (ti.aliasdecl)
3237     {
3238         ti.aliasdecl.dsymbolToBuffer(buf, hgs);
3239         buf.writenl();
3240     }
3241     else if (ti.members)
3242     {
3243         foreach(m;*ti.members)
3244             m.dsymbolToBuffer(buf, hgs);
3245     }
3246 
3247     buf.level--;
3248     buf.writeByte('}');
3249     buf.writenl();
3250 
3251 }
3252 
3253 private void tiargsToBuffer(TemplateInstance ti, OutBuffer* buf, HdrGenState* hgs)
3254 {
3255     buf.writeByte('!');
3256     if (ti.nest)
3257     {
3258         buf.writestring("(...)");
3259         return;
3260     }
3261     if (!ti.tiargs)
3262     {
3263         buf.writestring("()");
3264         return;
3265     }
3266     if (ti.tiargs.dim == 1)
3267     {
3268         RootObject oarg = (*ti.tiargs)[0];
3269         if (Type t = isType(oarg))
3270         {
3271             if (t.equals(Type.tstring) || t.equals(Type.twstring) || t.equals(Type.tdstring) || t.mod == 0 && (t.isTypeBasic() || t.ty == Tident && (cast(TypeIdentifier)t).idents.dim == 0))
3272             {
3273                 buf.writestring(t.toChars());
3274                 return;
3275             }
3276         }
3277         else if (Expression e = isExpression(oarg))
3278         {
3279             if (e.op == TOK.int64 || e.op == TOK.float64 || e.op == TOK.null_ || e.op == TOK.string_ || e.op == TOK.this_)
3280             {
3281                 buf.writestring(e.toChars());
3282                 return;
3283             }
3284         }
3285     }
3286     buf.writeByte('(');
3287     ti.nest++;
3288     foreach (i, arg; *ti.tiargs)
3289     {
3290         if (i)
3291             buf.writestring(", ");
3292         objectToBuffer(arg, buf, hgs);
3293     }
3294     ti.nest--;
3295     buf.writeByte(')');
3296 }
3297 
3298 /****************************************
3299  * This makes a 'pretty' version of the template arguments.
3300  * It's analogous to genIdent() which makes a mangled version.
3301  */
3302 private void objectToBuffer(RootObject oarg, OutBuffer* buf, HdrGenState* hgs)
3303 {
3304     //printf("objectToBuffer()\n");
3305     /* The logic of this should match what genIdent() does. The _dynamic_cast()
3306      * function relies on all the pretty strings to be unique for different classes
3307      * See https://issues.dlang.org/show_bug.cgi?id=7375
3308      * Perhaps it would be better to demangle what genIdent() does.
3309      */
3310     if (auto t = isType(oarg))
3311     {
3312         //printf("\tt: %s ty = %d\n", t.toChars(), t.ty);
3313         typeToBuffer(t, null, buf, hgs);
3314     }
3315     else if (auto e = isExpression(oarg))
3316     {
3317         if (e.op == TOK.variable)
3318             e = e.optimize(WANTvalue); // added to fix https://issues.dlang.org/show_bug.cgi?id=7375
3319         expToBuffer(e, PREC.assign, buf, hgs);
3320     }
3321     else if (Dsymbol s = isDsymbol(oarg))
3322     {
3323         const p = s.ident ? s.ident.toChars() : s.toChars();
3324         buf.writestring(p);
3325     }
3326     else if (auto v = isTuple(oarg))
3327     {
3328         auto args = &v.objects;
3329         foreach (i, arg; *args)
3330         {
3331             if (i)
3332                 buf.writestring(", ");
3333             objectToBuffer(arg, buf, hgs);
3334         }
3335     }
3336     else if (!oarg)
3337     {
3338         buf.writestring("NULL");
3339     }
3340     else
3341     {
3342         debug
3343         {
3344             printf("bad Object = %p\n", oarg);
3345         }
3346         assert(0);
3347     }
3348 }
3349 
3350 
3351 private void visitFuncIdentWithPostfix(TypeFunction t, const char[] ident, OutBuffer* buf, HdrGenState* hgs)
3352 {
3353     if (t.inuse)
3354     {
3355         t.inuse = 2; // flag error to caller
3356         return;
3357     }
3358     t.inuse++;
3359     if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen)
3360     {
3361         linkageToBuffer(buf, t.linkage);
3362         buf.writeByte(' ');
3363     }
3364     if (t.next)
3365     {
3366         typeToBuffer(t.next, null, buf, hgs);
3367         if (ident)
3368             buf.writeByte(' ');
3369     }
3370     else if (hgs.ddoc)
3371         buf.writestring("auto ");
3372     if (ident)
3373         buf.writestring(ident);
3374     parametersToBuffer(t.parameterList, buf, hgs);
3375     /* Use postfix style for attributes
3376      */
3377     if (t.mod)
3378     {
3379         buf.writeByte(' ');
3380         MODtoBuffer(buf, t.mod);
3381     }
3382 
3383     void dg(string str)
3384     {
3385         buf.writeByte(' ');
3386         buf.writestring(str);
3387     }
3388     t.attributesApply(&dg);
3389 
3390     t.inuse--;
3391 }
3392 
3393 private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, TemplateDeclaration td,
3394     OutBuffer* buf, HdrGenState* hgs)
3395 {
3396     if (t.inuse)
3397     {
3398         t.inuse = 2; // flag error to caller
3399         return;
3400     }
3401     t.inuse++;
3402 
3403     /* Use 'storage class' (prefix) style for attributes
3404      */
3405     if (t.mod)
3406     {
3407         MODtoBuffer(buf, t.mod);
3408         buf.writeByte(' ');
3409     }
3410 
3411     void ignoreReturn(string str)
3412     {
3413         if (str != "return")
3414         {
3415             // don't write 'ref' for ctors
3416             if ((ident == Id.ctor) && str == "ref")
3417                 return;
3418             buf.writestring(str);
3419             buf.writeByte(' ');
3420         }
3421     }
3422     t.attributesApply(&ignoreReturn);
3423 
3424     if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen)
3425     {
3426         linkageToBuffer(buf, t.linkage);
3427         buf.writeByte(' ');
3428     }
3429     if (ident && ident.toHChars2() != ident.toChars())
3430     {
3431         // Don't print return type for ctor, dtor, unittest, etc
3432     }
3433     else if (t.next)
3434     {
3435         typeToBuffer(t.next, null, buf, hgs);
3436         if (ident)
3437             buf.writeByte(' ');
3438     }
3439     else if (hgs.ddoc)
3440         buf.writestring("auto ");
3441     if (ident)
3442         buf.writestring(ident.toHChars2());
3443     if (td)
3444     {
3445         buf.writeByte('(');
3446         foreach (i, p; *td.origParameters)
3447         {
3448             if (i)
3449                 buf.writestring(", ");
3450             p.templateParameterToBuffer(buf, hgs);
3451         }
3452         buf.writeByte(')');
3453     }
3454     parametersToBuffer(t.parameterList, buf, hgs);
3455     if (t.isreturn)
3456     {
3457         buf.writestring(" return");
3458     }
3459     t.inuse--;
3460 }
3461 
3462 
3463 private void initializerToBuffer(Initializer inx, OutBuffer* buf, HdrGenState* hgs)
3464 {
3465     void visitError(ErrorInitializer iz)
3466     {
3467         buf.writestring("__error__");
3468     }
3469 
3470     void visitVoid(VoidInitializer iz)
3471     {
3472         buf.writestring("void");
3473     }
3474 
3475     void visitStruct(StructInitializer si)
3476     {
3477         //printf("StructInitializer::toCBuffer()\n");
3478         buf.writeByte('{');
3479         foreach (i, const id; si.field)
3480         {
3481             if (i)
3482                 buf.writestring(", ");
3483             if (id)
3484             {
3485                 buf.writestring(id.toString());
3486                 buf.writeByte(':');
3487             }
3488             if (auto iz = si.value[i])
3489                 initializerToBuffer(iz, buf, hgs);
3490         }
3491         buf.writeByte('}');
3492     }
3493 
3494     void visitArray(ArrayInitializer ai)
3495     {
3496         buf.writeByte('[');
3497         foreach (i, ex; ai.index)
3498         {
3499             if (i)
3500                 buf.writestring(", ");
3501             if (ex)
3502             {
3503                 ex.expressionToBuffer(buf, hgs);
3504                 buf.writeByte(':');
3505             }
3506             if (auto iz = ai.value[i])
3507                 initializerToBuffer(iz, buf, hgs);
3508         }
3509         buf.writeByte(']');
3510     }
3511 
3512     void visitExp(ExpInitializer ei)
3513     {
3514         ei.exp.expressionToBuffer(buf, hgs);
3515     }
3516 
3517     final switch (inx.kind)
3518     {
3519         case InitKind.error:   return visitError (inx.isErrorInitializer ());
3520         case InitKind.void_:   return visitVoid  (inx.isVoidInitializer  ());
3521         case InitKind.struct_: return visitStruct(inx.isStructInitializer());
3522         case InitKind.array:   return visitArray (inx.isArrayInitializer ());
3523         case InitKind.exp:     return visitExp   (inx.isExpInitializer   ());
3524     }
3525 }
3526 
3527 
3528 private void typeToBufferx(Type t, OutBuffer* buf, HdrGenState* hgs)
3529 {
3530     void visitType(Type t)
3531     {
3532         printf("t = %p, ty = %d\n", t, t.ty);
3533         assert(0);
3534     }
3535 
3536     void visitError(TypeError t)
3537     {
3538         buf.writestring("_error_");
3539     }
3540 
3541     void visitBasic(TypeBasic t)
3542     {
3543         //printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod);
3544         buf.writestring(t.dstring);
3545     }
3546 
3547     void visitTraits(TypeTraits t)
3548     {
3549         //printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod);
3550         t.exp.expressionToBuffer(buf, hgs);
3551     }
3552 
3553     void visitVector(TypeVector t)
3554     {
3555         //printf("TypeVector::toCBuffer2(t.mod = %d)\n", t.mod);
3556         buf.writestring("__vector(");
3557         visitWithMask(t.basetype, t.mod, buf, hgs);
3558         buf.writestring(")");
3559     }
3560 
3561     void visitSArray(TypeSArray t)
3562     {
3563         visitWithMask(t.next, t.mod, buf, hgs);
3564         buf.writeByte('[');
3565         sizeToBuffer(t.dim, buf, hgs);
3566         buf.writeByte(']');
3567     }
3568 
3569     void visitDArray(TypeDArray t)
3570     {
3571         Type ut = t.castMod(0);
3572         if (hgs.declstring)
3573             goto L1;
3574         if (ut.equals(Type.tstring))
3575             buf.writestring("string");
3576         else if (ut.equals(Type.twstring))
3577             buf.writestring("wstring");
3578         else if (ut.equals(Type.tdstring))
3579             buf.writestring("dstring");
3580         else
3581         {
3582         L1:
3583             visitWithMask(t.next, t.mod, buf, hgs);
3584             buf.writestring("[]");
3585         }
3586     }
3587 
3588     void visitAArray(TypeAArray t)
3589     {
3590         visitWithMask(t.next, t.mod, buf, hgs);
3591         buf.writeByte('[');
3592         visitWithMask(t.index, 0, buf, hgs);
3593         buf.writeByte(']');
3594     }
3595 
3596     void visitPointer(TypePointer t)
3597     {
3598         //printf("TypePointer::toCBuffer2() next = %d\n", t.next.ty);
3599         if (t.next.ty == Tfunction)
3600             visitFuncIdentWithPostfix(cast(TypeFunction)t.next, "function", buf, hgs);
3601         else
3602         {
3603             visitWithMask(t.next, t.mod, buf, hgs);
3604             buf.writeByte('*');
3605         }
3606     }
3607 
3608     void visitReference(TypeReference t)
3609     {
3610         visitWithMask(t.next, t.mod, buf, hgs);
3611         buf.writeByte('&');
3612     }
3613 
3614     void visitFunction(TypeFunction t)
3615     {
3616         //printf("TypeFunction::toCBuffer2() t = %p, ref = %d\n", t, t.isref);
3617         visitFuncIdentWithPostfix(t, null, buf, hgs);
3618     }
3619 
3620     void visitDelegate(TypeDelegate t)
3621     {
3622         visitFuncIdentWithPostfix(cast(TypeFunction)t.next, "delegate", buf, hgs);
3623     }
3624 
3625     void visitTypeQualifiedHelper(TypeQualified t)
3626     {
3627         foreach (id; t.idents)
3628         {
3629             if (id.dyncast() == DYNCAST.dsymbol)
3630             {
3631                 buf.writeByte('.');
3632                 TemplateInstance ti = cast(TemplateInstance)id;
3633                 ti.dsymbolToBuffer(buf, hgs);
3634             }
3635             else if (id.dyncast() == DYNCAST.expression)
3636             {
3637                 buf.writeByte('[');
3638                 (cast(Expression)id).expressionToBuffer(buf, hgs);
3639                 buf.writeByte(']');
3640             }
3641             else if (id.dyncast() == DYNCAST.type)
3642             {
3643                 buf.writeByte('[');
3644                 typeToBufferx(cast(Type)id, buf, hgs);
3645                 buf.writeByte(']');
3646             }
3647             else
3648             {
3649                 buf.writeByte('.');
3650                 buf.writestring(id.toString());
3651             }
3652         }
3653     }
3654 
3655     void visitIdentifier(TypeIdentifier t)
3656     {
3657         buf.writestring(t.ident.toString());
3658         visitTypeQualifiedHelper(t);
3659     }
3660 
3661     void visitInstance(TypeInstance t)
3662     {
3663         t.tempinst.dsymbolToBuffer(buf, hgs);
3664         visitTypeQualifiedHelper(t);
3665     }
3666 
3667     void visitTypeof(TypeTypeof t)
3668     {
3669         buf.writestring("typeof(");
3670         t.exp.expressionToBuffer(buf, hgs);
3671         buf.writeByte(')');
3672         visitTypeQualifiedHelper(t);
3673     }
3674 
3675     void visitReturn(TypeReturn t)
3676     {
3677         buf.writestring("typeof(return)");
3678         visitTypeQualifiedHelper(t);
3679     }
3680 
3681     void visitEnum(TypeEnum t)
3682     {
3683         buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
3684     }
3685 
3686     void visitStruct(TypeStruct t)
3687     {
3688         // https://issues.dlang.org/show_bug.cgi?id=13776
3689         // Don't use ti.toAlias() to avoid forward reference error
3690         // while printing messages.
3691         TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null;
3692         if (ti && ti.aliasdecl == t.sym)
3693             buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
3694         else
3695             buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
3696     }
3697 
3698     void visitClass(TypeClass t)
3699     {
3700         // https://issues.dlang.org/show_bug.cgi?id=13776
3701         // Don't use ti.toAlias() to avoid forward reference error
3702         // while printing messages.
3703         TemplateInstance ti = t.sym.parent.isTemplateInstance();
3704         if (ti && ti.aliasdecl == t.sym)
3705             buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
3706         else
3707             buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
3708     }
3709 
3710     void visitTuple(TypeTuple t)
3711     {
3712         parametersToBuffer(ParameterList(t.arguments, VarArg.none), buf, hgs);
3713     }
3714 
3715     void visitSlice(TypeSlice t)
3716     {
3717         visitWithMask(t.next, t.mod, buf, hgs);
3718         buf.writeByte('[');
3719         sizeToBuffer(t.lwr, buf, hgs);
3720         buf.writestring(" .. ");
3721         sizeToBuffer(t.upr, buf, hgs);
3722         buf.writeByte(']');
3723     }
3724 
3725     void visitNull(TypeNull t)
3726     {
3727         buf.writestring("typeof(null)");
3728     }
3729 
3730     void visitMixin(TypeMixin t)
3731     {
3732         buf.writestring("mixin(");
3733         argsToBuffer(t.exps, buf, hgs, null);
3734         buf.writeByte(')');
3735     }
3736 
3737     switch (t.ty)
3738     {
3739         default:        return t.isTypeBasic() ?
3740                                 visitBasic(cast(TypeBasic)t) :
3741                                 visitType(t);
3742 
3743         case Terror:     return visitError(cast(TypeError)t);
3744         case Ttraits:    return visitTraits(cast(TypeTraits)t);
3745         case Tvector:    return visitVector(cast(TypeVector)t);
3746         case Tsarray:    return visitSArray(cast(TypeSArray)t);
3747         case Tarray:     return visitDArray(cast(TypeDArray)t);
3748         case Taarray:    return visitAArray(cast(TypeAArray)t);
3749         case Tpointer:   return visitPointer(cast(TypePointer)t);
3750         case Treference: return visitReference(cast(TypeReference)t);
3751         case Tfunction:  return visitFunction(cast(TypeFunction)t);
3752         case Tdelegate:  return visitDelegate(cast(TypeDelegate)t);
3753         case Tident:     return visitIdentifier(cast(TypeIdentifier)t);
3754         case Tinstance:  return visitInstance(cast(TypeInstance)t);
3755         case Ttypeof:    return visitTypeof(cast(TypeTypeof)t);
3756         case Treturn:    return visitReturn(cast(TypeReturn)t);
3757         case Tenum:      return visitEnum(cast(TypeEnum)t);
3758         case Tstruct:    return visitStruct(cast(TypeStruct)t);
3759         case Tclass:     return visitClass(cast(TypeClass)t);
3760         case Ttuple:     return visitTuple (cast(TypeTuple)t);
3761         case Tslice:     return visitSlice(cast(TypeSlice)t);
3762         case Tnull:      return visitNull(cast(TypeNull)t);
3763         case Tmixin:     return visitMixin(cast(TypeMixin)t);
3764     }
3765 }