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-2021 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         foreach (const pid; imp.packages)
866         {
867             buf.printf("%s.", pid.toChars());
868         }
869         buf.writestring(imp.id.toString());
870         if (imp.names.dim)
871         {
872             buf.writestring(" : ");
873             foreach (const i, const name; imp.names)
874             {
875                 if (i)
876                     buf.writestring(", ");
877                 const _alias = imp.aliases[i];
878                 if (_alias)
879                     buf.printf("%s = %s", _alias.toChars(), name.toChars());
880                 else
881                     buf.writestring(name.toChars());
882             }
883         }
884         buf.writeByte(';');
885         buf.writenl();
886     }
887 
888     override void visit(AliasThis d)
889     {
890         buf.writestring("alias ");
891         buf.writestring(d.ident.toString());
892         buf.writestring(" this;\n");
893     }
894 
895     override void visit(AttribDeclaration d)
896     {
897         if (!d.decl)
898         {
899             buf.writeByte(';');
900             buf.writenl();
901             return;
902         }
903         if (d.decl.dim == 0 || (hgs.hdrgen && d.decl.dim == 1 && (*d.decl)[0].isUnitTestDeclaration()))
904         {
905             // hack for bugzilla 8081
906             buf.writestring("{}");
907         }
908         else if (d.decl.dim == 1)
909         {
910             (*d.decl)[0].accept(this);
911             return;
912         }
913         else
914         {
915             buf.writenl();
916             buf.writeByte('{');
917             buf.writenl();
918             buf.level++;
919             foreach (de; *d.decl)
920                 de.accept(this);
921             buf.level--;
922             buf.writeByte('}');
923         }
924         buf.writenl();
925     }
926 
927     override void visit(StorageClassDeclaration d)
928     {
929         if (stcToBuffer(buf, d.stc))
930             buf.writeByte(' ');
931         visit(cast(AttribDeclaration)d);
932     }
933 
934     override void visit(DeprecatedDeclaration d)
935     {
936         buf.writestring("deprecated(");
937         d.msg.expressionToBuffer(buf, hgs);
938         buf.writestring(") ");
939         visit(cast(AttribDeclaration)d);
940     }
941 
942     override void visit(LinkDeclaration d)
943     {
944         buf.writestring("extern (");
945         buf.writestring(linkageToString(d.linkage));
946         buf.writestring(") ");
947         visit(cast(AttribDeclaration)d);
948     }
949 
950     override void visit(CPPMangleDeclaration d)
951     {
952         string s;
953         final switch (d.cppmangle)
954         {
955         case CPPMANGLE.asClass:
956             s = "class";
957             break;
958         case CPPMANGLE.asStruct:
959             s = "struct";
960             break;
961         case CPPMANGLE.def:
962             break;
963         }
964         buf.writestring("extern (C++, ");
965         buf.writestring(s);
966         buf.writestring(") ");
967         visit(cast(AttribDeclaration)d);
968     }
969 
970     override void visit(VisibilityDeclaration d)
971     {
972         visibilityToBuffer(buf, d.visibility);
973         buf.writeByte(' ');
974         AttribDeclaration ad = cast(AttribDeclaration)d;
975         if (ad.decl.dim == 1 && (*ad.decl)[0].isVisibilityDeclaration)
976             visit(cast(AttribDeclaration)(*ad.decl)[0]);
977         else
978             visit(cast(AttribDeclaration)d);
979     }
980 
981     override void visit(AlignDeclaration d)
982     {
983         buf.writestring("align ");
984         if (d.ealign)
985         {
986             buf.printf("(%s)", d.ealign.toChars());
987             AttribDeclaration ad = cast(AttribDeclaration)d;
988             if (ad.decl && ad.decl.dim < 2)
989                 buf.writeByte(' ');
990         }
991 
992         visit(cast(AttribDeclaration)d);
993     }
994 
995     override void visit(AnonDeclaration d)
996     {
997         buf.writestring(d.isunion ? "union" : "struct");
998         buf.writenl();
999         buf.writestring("{");
1000         buf.writenl();
1001         buf.level++;
1002         if (d.decl)
1003         {
1004             foreach (de; *d.decl)
1005                 de.accept(this);
1006         }
1007         buf.level--;
1008         buf.writestring("}");
1009         buf.writenl();
1010     }
1011 
1012     override void visit(PragmaDeclaration d)
1013     {
1014         buf.writestring("pragma (");
1015         buf.writestring(d.ident.toString());
1016         if (d.args && d.args.dim)
1017         {
1018             buf.writestring(", ");
1019             argsToBuffer(d.args, buf, hgs);
1020         }
1021         buf.writeByte(')');
1022         visit(cast(AttribDeclaration)d);
1023     }
1024 
1025     override void visit(ConditionalDeclaration d)
1026     {
1027         d.condition.conditionToBuffer(buf, hgs);
1028         if (d.decl || d.elsedecl)
1029         {
1030             buf.writenl();
1031             buf.writeByte('{');
1032             buf.writenl();
1033             buf.level++;
1034             if (d.decl)
1035             {
1036                 foreach (de; *d.decl)
1037                     de.accept(this);
1038             }
1039             buf.level--;
1040             buf.writeByte('}');
1041             if (d.elsedecl)
1042             {
1043                 buf.writenl();
1044                 buf.writestring("else");
1045                 buf.writenl();
1046                 buf.writeByte('{');
1047                 buf.writenl();
1048                 buf.level++;
1049                 foreach (de; *d.elsedecl)
1050                     de.accept(this);
1051                 buf.level--;
1052                 buf.writeByte('}');
1053             }
1054         }
1055         else
1056             buf.writeByte(':');
1057         buf.writenl();
1058     }
1059 
1060     override void visit(StaticForeachDeclaration s)
1061     {
1062         void foreachWithoutBody(ForeachStatement s)
1063         {
1064             buf.writestring(Token.toString(s.op));
1065             buf.writestring(" (");
1066             foreach (i, p; *s.parameters)
1067             {
1068                 if (i)
1069                     buf.writestring(", ");
1070                 if (stcToBuffer(buf, p.storageClass))
1071                     buf.writeByte(' ');
1072                 if (p.type)
1073                     typeToBuffer(p.type, p.ident, buf, hgs);
1074                 else
1075                     buf.writestring(p.ident.toString());
1076             }
1077             buf.writestring("; ");
1078             s.aggr.expressionToBuffer(buf, hgs);
1079             buf.writeByte(')');
1080             buf.writenl();
1081         }
1082 
1083         void foreachRangeWithoutBody(ForeachRangeStatement s)
1084         {
1085             /* s.op ( prm ; lwr .. upr )
1086              */
1087             buf.writestring(Token.toString(s.op));
1088             buf.writestring(" (");
1089             if (s.prm.type)
1090                 typeToBuffer(s.prm.type, s.prm.ident, buf, hgs);
1091             else
1092                 buf.writestring(s.prm.ident.toString());
1093             buf.writestring("; ");
1094             s.lwr.expressionToBuffer(buf, hgs);
1095             buf.writestring(" .. ");
1096             s.upr.expressionToBuffer(buf, hgs);
1097             buf.writeByte(')');
1098             buf.writenl();
1099         }
1100 
1101         buf.writestring("static ");
1102         if (s.sfe.aggrfe)
1103         {
1104             foreachWithoutBody(s.sfe.aggrfe);
1105         }
1106         else
1107         {
1108             assert(s.sfe.rangefe);
1109             foreachRangeWithoutBody(s.sfe.rangefe);
1110         }
1111         buf.writeByte('{');
1112         buf.writenl();
1113         buf.level++;
1114         visit(cast(AttribDeclaration)s);
1115         buf.level--;
1116         buf.writeByte('}');
1117         buf.writenl();
1118 
1119     }
1120 
1121     override void visit(CompileDeclaration d)
1122     {
1123         buf.writestring("mixin(");
1124         argsToBuffer(d.exps, buf, hgs, null);
1125         buf.writestring(");");
1126         buf.writenl();
1127     }
1128 
1129     override void visit(UserAttributeDeclaration d)
1130     {
1131         buf.writestring("@(");
1132         argsToBuffer(d.atts, buf, hgs);
1133         buf.writeByte(')');
1134         visit(cast(AttribDeclaration)d);
1135     }
1136 
1137     override void visit(TemplateDeclaration d)
1138     {
1139         version (none)
1140         {
1141             // Should handle template functions for doc generation
1142             if (onemember && onemember.isFuncDeclaration())
1143                 buf.writestring("foo ");
1144         }
1145         if ((hgs.hdrgen || hgs.fullDump) && visitEponymousMember(d))
1146             return;
1147         if (hgs.ddoc)
1148             buf.writestring(d.kind());
1149         else
1150             buf.writestring("template");
1151         buf.writeByte(' ');
1152         buf.writestring(d.ident.toString());
1153         buf.writeByte('(');
1154         visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
1155         buf.writeByte(')');
1156         visitTemplateConstraint(d.constraint);
1157         if (hgs.hdrgen || hgs.fullDump)
1158         {
1159             hgs.tpltMember++;
1160             buf.writenl();
1161             buf.writeByte('{');
1162             buf.writenl();
1163             buf.level++;
1164             foreach (s; *d.members)
1165                 s.accept(this);
1166             buf.level--;
1167             buf.writeByte('}');
1168             buf.writenl();
1169             hgs.tpltMember--;
1170         }
1171     }
1172 
1173     bool visitEponymousMember(TemplateDeclaration d)
1174     {
1175         if (!d.members || d.members.dim != 1)
1176             return false;
1177         Dsymbol onemember = (*d.members)[0];
1178         if (onemember.ident != d.ident)
1179             return false;
1180         if (FuncDeclaration fd = onemember.isFuncDeclaration())
1181         {
1182             assert(fd.type);
1183             if (stcToBuffer(buf, fd.storage_class))
1184                 buf.writeByte(' ');
1185             functionToBufferFull(cast(TypeFunction)fd.type, buf, d.ident, hgs, d);
1186             visitTemplateConstraint(d.constraint);
1187             hgs.tpltMember++;
1188             bodyToBuffer(fd);
1189             hgs.tpltMember--;
1190             return true;
1191         }
1192         if (AggregateDeclaration ad = onemember.isAggregateDeclaration())
1193         {
1194             buf.writestring(ad.kind());
1195             buf.writeByte(' ');
1196             buf.writestring(ad.ident.toString());
1197             buf.writeByte('(');
1198             visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
1199             buf.writeByte(')');
1200             visitTemplateConstraint(d.constraint);
1201             visitBaseClasses(ad.isClassDeclaration());
1202             hgs.tpltMember++;
1203             if (ad.members)
1204             {
1205                 buf.writenl();
1206                 buf.writeByte('{');
1207                 buf.writenl();
1208                 buf.level++;
1209                 foreach (s; *ad.members)
1210                     s.accept(this);
1211                 buf.level--;
1212                 buf.writeByte('}');
1213             }
1214             else
1215                 buf.writeByte(';');
1216             buf.writenl();
1217             hgs.tpltMember--;
1218             return true;
1219         }
1220         if (VarDeclaration vd = onemember.isVarDeclaration())
1221         {
1222             if (d.constraint)
1223                 return false;
1224             if (stcToBuffer(buf, vd.storage_class))
1225                 buf.writeByte(' ');
1226             if (vd.type)
1227                 typeToBuffer(vd.type, vd.ident, buf, hgs);
1228             else
1229                 buf.writestring(vd.ident.toString());
1230             buf.writeByte('(');
1231             visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
1232             buf.writeByte(')');
1233             if (vd._init)
1234             {
1235                 buf.writestring(" = ");
1236                 ExpInitializer ie = vd._init.isExpInitializer();
1237                 if (ie && (ie.exp.op == TOK.construct || ie.exp.op == TOK.blit))
1238                     (cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
1239                 else
1240                     vd._init.initializerToBuffer(buf, hgs);
1241             }
1242             buf.writeByte(';');
1243             buf.writenl();
1244             return true;
1245         }
1246         return false;
1247     }
1248 
1249     void visitTemplateParameters(TemplateParameters* parameters)
1250     {
1251         if (!parameters || !parameters.dim)
1252             return;
1253         foreach (i, p; *parameters)
1254         {
1255             if (i)
1256                 buf.writestring(", ");
1257             p.templateParameterToBuffer(buf, hgs);
1258         }
1259     }
1260 
1261     void visitTemplateConstraint(Expression constraint)
1262     {
1263         if (!constraint)
1264             return;
1265         buf.writestring(" if (");
1266         constraint.expressionToBuffer(buf, hgs);
1267         buf.writeByte(')');
1268     }
1269 
1270     override void visit(TemplateInstance ti)
1271     {
1272         buf.writestring(ti.name.toChars());
1273         tiargsToBuffer(ti, buf, hgs);
1274 
1275         if (hgs.fullDump)
1276         {
1277             buf.writenl();
1278             dumpTemplateInstance(ti, buf, hgs);
1279         }
1280     }
1281 
1282     override void visit(TemplateMixin tm)
1283     {
1284         buf.writestring("mixin ");
1285         typeToBuffer(tm.tqual, null, buf, hgs);
1286         tiargsToBuffer(tm, buf, hgs);
1287         if (tm.ident && memcmp(tm.ident.toChars(), cast(const(char)*)"__mixin", 7) != 0)
1288         {
1289             buf.writeByte(' ');
1290             buf.writestring(tm.ident.toString());
1291         }
1292         buf.writeByte(';');
1293         buf.writenl();
1294         if (hgs.fullDump)
1295             dumpTemplateInstance(tm, buf, hgs);
1296     }
1297 
1298     override void visit(EnumDeclaration d)
1299     {
1300         auto oldInEnumDecl = hgs.inEnumDecl;
1301         scope(exit) hgs.inEnumDecl = oldInEnumDecl;
1302         hgs.inEnumDecl = d;
1303         buf.writestring("enum ");
1304         if (d.ident)
1305         {
1306             buf.writestring(d.ident.toString());
1307             buf.writeByte(' ');
1308         }
1309         if (d.memtype)
1310         {
1311             buf.writestring(": ");
1312             typeToBuffer(d.memtype, null, buf, hgs);
1313         }
1314         if (!d.members)
1315         {
1316             buf.writeByte(';');
1317             buf.writenl();
1318             return;
1319         }
1320         buf.writenl();
1321         buf.writeByte('{');
1322         buf.writenl();
1323         buf.level++;
1324         foreach (em; *d.members)
1325         {
1326             if (!em)
1327                 continue;
1328             em.accept(this);
1329             buf.writeByte(',');
1330             buf.writenl();
1331         }
1332         buf.level--;
1333         buf.writeByte('}');
1334         buf.writenl();
1335     }
1336 
1337     override void visit(Nspace d)
1338     {
1339         buf.writestring("extern (C++, ");
1340         buf.writestring(d.ident.toString());
1341         buf.writeByte(')');
1342         buf.writenl();
1343         buf.writeByte('{');
1344         buf.writenl();
1345         buf.level++;
1346         foreach (s; *d.members)
1347             s.accept(this);
1348         buf.level--;
1349         buf.writeByte('}');
1350         buf.writenl();
1351     }
1352 
1353     override void visit(StructDeclaration d)
1354     {
1355         buf.writestring(d.kind());
1356         buf.writeByte(' ');
1357         if (!d.isAnonymous())
1358             buf.writestring(d.toChars());
1359         if (!d.members)
1360         {
1361             buf.writeByte(';');
1362             buf.writenl();
1363             return;
1364         }
1365         buf.writenl();
1366         buf.writeByte('{');
1367         buf.writenl();
1368         buf.level++;
1369         foreach (s; *d.members)
1370             s.accept(this);
1371         buf.level--;
1372         buf.writeByte('}');
1373         buf.writenl();
1374     }
1375 
1376     override void visit(ClassDeclaration d)
1377     {
1378         if (!d.isAnonymous())
1379         {
1380             buf.writestring(d.kind());
1381             buf.writeByte(' ');
1382             buf.writestring(d.ident.toString());
1383         }
1384         visitBaseClasses(d);
1385         if (d.members)
1386         {
1387             buf.writenl();
1388             buf.writeByte('{');
1389             buf.writenl();
1390             buf.level++;
1391             foreach (s; *d.members)
1392                 s.accept(this);
1393             buf.level--;
1394             buf.writeByte('}');
1395         }
1396         else
1397             buf.writeByte(';');
1398         buf.writenl();
1399     }
1400 
1401     void visitBaseClasses(ClassDeclaration d)
1402     {
1403         if (!d || !d.baseclasses.dim)
1404             return;
1405         if (!d.isAnonymous())
1406             buf.writestring(" : ");
1407         foreach (i, b; *d.baseclasses)
1408         {
1409             if (i)
1410                 buf.writestring(", ");
1411             typeToBuffer(b.type, null, buf, hgs);
1412         }
1413     }
1414 
1415     override void visit(AliasDeclaration d)
1416     {
1417         if (d.storage_class & STC.local)
1418             return;
1419         buf.writestring("alias ");
1420         if (d.aliassym)
1421         {
1422             buf.writestring(d.ident.toString());
1423             buf.writestring(" = ");
1424             if (stcToBuffer(buf, d.storage_class))
1425                 buf.writeByte(' ');
1426             d.aliassym.accept(this);
1427         }
1428         else if (d.type.ty == Tfunction)
1429         {
1430             if (stcToBuffer(buf, d.storage_class))
1431                 buf.writeByte(' ');
1432             typeToBuffer(d.type, d.ident, buf, hgs);
1433         }
1434         else if (d.ident)
1435         {
1436             hgs.declstring = (d.ident == Id..string || d.ident == Id.wstring || d.ident == Id.dstring);
1437             buf.writestring(d.ident.toString());
1438             buf.writestring(" = ");
1439             if (stcToBuffer(buf, d.storage_class))
1440                 buf.writeByte(' ');
1441             typeToBuffer(d.type, null, buf, hgs);
1442             hgs.declstring = false;
1443         }
1444         buf.writeByte(';');
1445         buf.writenl();
1446     }
1447 
1448     override void visit(AliasAssign d)
1449     {
1450         buf.writestring(d.ident.toString());
1451         buf.writestring(" = ");
1452         if (d.aliassym)
1453             d.aliassym.accept(this);
1454         else // d.type
1455             typeToBuffer(d.type, null, buf, hgs);
1456         buf.writeByte(';');
1457         buf.writenl();
1458     }
1459 
1460     override void visit(VarDeclaration d)
1461     {
1462         if (d.storage_class & STC.local)
1463             return;
1464         visitVarDecl(d, false);
1465         buf.writeByte(';');
1466         buf.writenl();
1467     }
1468 
1469     void visitVarDecl(VarDeclaration v, bool anywritten)
1470     {
1471         if (anywritten)
1472         {
1473             buf.writestring(", ");
1474             buf.writestring(v.ident.toString());
1475         }
1476         else
1477         {
1478             if (stcToBuffer(buf, v.storage_class))
1479                 buf.writeByte(' ');
1480             if (v.type)
1481                 typeToBuffer(v.type, v.ident, buf, hgs);
1482             else
1483                 buf.writestring(v.ident.toString());
1484         }
1485         if (v._init)
1486         {
1487             buf.writestring(" = ");
1488             auto ie = v._init.isExpInitializer();
1489             if (ie && (ie.exp.op == TOK.construct || ie.exp.op == TOK.blit))
1490                 (cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
1491             else
1492                 v._init.initializerToBuffer(buf, hgs);
1493         }
1494     }
1495 
1496     override void visit(FuncDeclaration f)
1497     {
1498         //printf("FuncDeclaration::toCBuffer() '%s'\n", f.toChars());
1499         if (stcToBuffer(buf, f.storage_class))
1500             buf.writeByte(' ');
1501         auto tf = cast(TypeFunction)f.type;
1502         typeToBuffer(tf, f.ident, buf, hgs);
1503 
1504         if (hgs.hdrgen)
1505         {
1506             // if the return type is missing (e.g. ref functions or auto)
1507             if (!tf.next || f.storage_class & STC.auto_)
1508             {
1509                 hgs.autoMember++;
1510                 bodyToBuffer(f);
1511                 hgs.autoMember--;
1512             }
1513             else if (hgs.tpltMember == 0 && global.params.hdrStripPlainFunctions)
1514             {
1515                 buf.writeByte(';');
1516                 buf.writenl();
1517             }
1518             else
1519                 bodyToBuffer(f);
1520         }
1521         else
1522             bodyToBuffer(f);
1523     }
1524 
1525     void bodyToBuffer(FuncDeclaration f)
1526     {
1527         if (!f.fbody || (hgs.hdrgen && global.params.hdrStripPlainFunctions && !hgs.autoMember && !hgs.tpltMember))
1528         {
1529             buf.writeByte(';');
1530             buf.writenl();
1531             return;
1532         }
1533         const savetlpt = hgs.tpltMember;
1534         const saveauto = hgs.autoMember;
1535         hgs.tpltMember = 0;
1536         hgs.autoMember = 0;
1537         buf.writenl();
1538         bool requireDo = false;
1539         // in{}
1540         if (f.frequires)
1541         {
1542             foreach (frequire; *f.frequires)
1543             {
1544                 buf.writestring("in");
1545                 if (auto es = frequire.isExpStatement())
1546                 {
1547                     assert(es.exp && es.exp.op == TOK.assert_);
1548                     buf.writestring(" (");
1549                     (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
1550                     buf.writeByte(')');
1551                     buf.writenl();
1552                     requireDo = false;
1553                 }
1554                 else
1555                 {
1556                     buf.writenl();
1557                     frequire.statementToBuffer(buf, hgs);
1558                     requireDo = true;
1559                 }
1560             }
1561         }
1562         // out{}
1563         if (f.fensures)
1564         {
1565             foreach (fensure; *f.fensures)
1566             {
1567                 buf.writestring("out");
1568                 if (auto es = fensure.ensure.isExpStatement())
1569                 {
1570                     assert(es.exp && es.exp.op == TOK.assert_);
1571                     buf.writestring(" (");
1572                     if (fensure.id)
1573                     {
1574                         buf.writestring(fensure.id.toString());
1575                     }
1576                     buf.writestring("; ");
1577                     (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
1578                     buf.writeByte(')');
1579                     buf.writenl();
1580                     requireDo = false;
1581                 }
1582                 else
1583                 {
1584                     if (fensure.id)
1585                     {
1586                         buf.writeByte('(');
1587                         buf.writestring(fensure.id.toString());
1588                         buf.writeByte(')');
1589                     }
1590                     buf.writenl();
1591                     fensure.ensure.statementToBuffer(buf, hgs);
1592                     requireDo = true;
1593                 }
1594             }
1595         }
1596         if (requireDo)
1597         {
1598             buf.writestring("do");
1599             buf.writenl();
1600         }
1601         buf.writeByte('{');
1602         buf.writenl();
1603         buf.level++;
1604         f.fbody.statementToBuffer(buf, hgs);
1605         buf.level--;
1606         buf.writeByte('}');
1607         buf.writenl();
1608         hgs.tpltMember = savetlpt;
1609         hgs.autoMember = saveauto;
1610     }
1611 
1612     override void visit(FuncLiteralDeclaration f)
1613     {
1614         if (f.type.ty == Terror)
1615         {
1616             buf.writestring("__error");
1617             return;
1618         }
1619         if (f.tok != TOK.reserved)
1620         {
1621             buf.writestring(f.kind());
1622             buf.writeByte(' ');
1623         }
1624         TypeFunction tf = cast(TypeFunction)f.type;
1625 
1626         if (!f.inferRetType && tf.next)
1627             typeToBuffer(tf.next, null, buf, hgs);
1628         parametersToBuffer(tf.parameterList, buf, hgs);
1629 
1630         // https://issues.dlang.org/show_bug.cgi?id=20074
1631         void printAttribute(string str)
1632         {
1633             buf.writeByte(' ');
1634             buf.writestring(str);
1635         }
1636         tf.attributesApply(&printAttribute);
1637 
1638 
1639         CompoundStatement cs = f.fbody.isCompoundStatement();
1640         Statement s1;
1641         if (f.semanticRun >= PASS.semantic3done && cs)
1642         {
1643             s1 = (*cs.statements)[cs.statements.dim - 1];
1644         }
1645         else
1646             s1 = !cs ? f.fbody : null;
1647         ReturnStatement rs = s1 ? s1.endsWithReturnStatement() : null;
1648         if (rs && rs.exp)
1649         {
1650             buf.writestring(" => ");
1651             rs.exp.expressionToBuffer(buf, hgs);
1652         }
1653         else
1654         {
1655             hgs.tpltMember++;
1656             bodyToBuffer(f);
1657             hgs.tpltMember--;
1658         }
1659     }
1660 
1661     override void visit(PostBlitDeclaration d)
1662     {
1663         if (stcToBuffer(buf, d.storage_class))
1664             buf.writeByte(' ');
1665         buf.writestring("this(this)");
1666         bodyToBuffer(d);
1667     }
1668 
1669     override void visit(DtorDeclaration d)
1670     {
1671         if (d.storage_class & STC.trusted)
1672             buf.writestring("@trusted ");
1673         if (d.storage_class & STC.safe)
1674             buf.writestring("@safe ");
1675         if (d.storage_class & STC.nogc)
1676             buf.writestring("@nogc ");
1677         if (d.storage_class & STC.live)
1678             buf.writestring("@live ");
1679         if (d.storage_class & STC.disable)
1680             buf.writestring("@disable ");
1681 
1682         buf.writestring("~this()");
1683         bodyToBuffer(d);
1684     }
1685 
1686     override void visit(StaticCtorDeclaration d)
1687     {
1688         if (stcToBuffer(buf, d.storage_class & ~STC.static_))
1689             buf.writeByte(' ');
1690         if (d.isSharedStaticCtorDeclaration())
1691             buf.writestring("shared ");
1692         buf.writestring("static this()");
1693         if (hgs.hdrgen && !hgs.tpltMember)
1694         {
1695             buf.writeByte(';');
1696             buf.writenl();
1697         }
1698         else
1699             bodyToBuffer(d);
1700     }
1701 
1702     override void visit(StaticDtorDeclaration d)
1703     {
1704         if (stcToBuffer(buf, d.storage_class & ~STC.static_))
1705             buf.writeByte(' ');
1706         if (d.isSharedStaticDtorDeclaration())
1707             buf.writestring("shared ");
1708         buf.writestring("static ~this()");
1709         if (hgs.hdrgen && !hgs.tpltMember)
1710         {
1711             buf.writeByte(';');
1712             buf.writenl();
1713         }
1714         else
1715             bodyToBuffer(d);
1716     }
1717 
1718     override void visit(InvariantDeclaration d)
1719     {
1720         if (hgs.hdrgen)
1721             return;
1722         if (stcToBuffer(buf, d.storage_class))
1723             buf.writeByte(' ');
1724         buf.writestring("invariant");
1725         if(auto es = d.fbody.isExpStatement())
1726         {
1727             assert(es.exp && es.exp.op == TOK.assert_);
1728             buf.writestring(" (");
1729             (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
1730             buf.writestring(");");
1731             buf.writenl();
1732         }
1733         else
1734         {
1735             bodyToBuffer(d);
1736         }
1737     }
1738 
1739     override void visit(UnitTestDeclaration d)
1740     {
1741         if (hgs.hdrgen)
1742             return;
1743         if (stcToBuffer(buf, d.storage_class))
1744             buf.writeByte(' ');
1745         buf.writestring("unittest");
1746         bodyToBuffer(d);
1747     }
1748 
1749     override void visit(NewDeclaration d)
1750     {
1751         if (stcToBuffer(buf, d.storage_class & ~STC.static_))
1752             buf.writeByte(' ');
1753         buf.writestring("new");
1754         parametersToBuffer(d.parameterList, buf, hgs);
1755         bodyToBuffer(d);
1756     }
1757 
1758     override void visit(Module m)
1759     {
1760         moduleToBuffer2(m, buf, hgs);
1761     }
1762 }
1763 
1764 private extern (C++) final class ExpressionPrettyPrintVisitor : Visitor
1765 {
1766     alias visit = Visitor.visit;
1767 public:
1768     OutBuffer* buf;
1769     HdrGenState* hgs;
1770 
1771     extern (D) this(OutBuffer* buf, HdrGenState* hgs)
1772     {
1773         this.buf = buf;
1774         this.hgs = hgs;
1775     }
1776 
1777     ////////////////////////////////////////////////////////////////////////////
1778     override void visit(Expression e)
1779     {
1780         buf.writestring(Token.toString(e.op));
1781     }
1782 
1783     override void visit(IntegerExp e)
1784     {
1785         const dinteger_t v = e.toInteger();
1786         if (e.type)
1787         {
1788             Type t = e.type;
1789         L1:
1790             switch (t.ty)
1791             {
1792             case Tenum:
1793                 {
1794                     TypeEnum te = cast(TypeEnum)t;
1795                     auto sym = te.sym;
1796                     if (sym && sym.members && (!hgs.inEnumDecl || hgs.inEnumDecl != sym))
1797                     {
1798                         foreach (em; *sym.members)
1799                         {
1800                             if ((cast(EnumMember)em).value.toInteger == v)
1801                             {
1802                                 buf.printf("%s.%s", sym.toChars(), em.ident.toChars());
1803                                 return ;
1804                             }
1805                         }
1806                     }
1807 
1808                     buf.printf("cast(%s)", te.sym.toChars());
1809                     t = te.sym.memtype;
1810                     goto L1;
1811                 }
1812             case Twchar:
1813                 // BUG: need to cast(wchar)
1814             case Tdchar:
1815                 // BUG: need to cast(dchar)
1816                 if (cast(uinteger_t)v > 0xFF)
1817                 {
1818                     buf.printf("'\\U%08llx'", cast(long)v);
1819                     break;
1820                 }
1821                 goto case;
1822             case Tchar:
1823                 {
1824                     size_t o = buf.length;
1825                     if (v == '\'')
1826                         buf.writestring("'\\''");
1827                     else if (isprint(cast(int)v) && v != '\\')
1828                         buf.printf("'%c'", cast(int)v);
1829                     else
1830                         buf.printf("'\\x%02x'", cast(int)v);
1831                     if (hgs.ddoc)
1832                         escapeDdocString(buf, o);
1833                     break;
1834                 }
1835             case Tint8:
1836                 buf.writestring("cast(byte)");
1837                 goto L2;
1838             case Tint16:
1839                 buf.writestring("cast(short)");
1840                 goto L2;
1841             case Tint32:
1842             L2:
1843                 buf.printf("%d", cast(int)v);
1844                 break;
1845             case Tuns8:
1846                 buf.writestring("cast(ubyte)");
1847                 goto case Tuns32;
1848             case Tuns16:
1849                 buf.writestring("cast(ushort)");
1850                 goto case Tuns32;
1851             case Tuns32:
1852                 buf.printf("%uu", cast(uint)v);
1853                 break;
1854             case Tint64:
1855                 buf.printf("%lldL", v);
1856                 break;
1857             case Tuns64:
1858                 buf.printf("%lluLU", v);
1859                 break;
1860             case Tbool:
1861                 buf.writestring(v ? "true" : "false");
1862                 break;
1863             case Tpointer:
1864                 buf.writestring("cast(");
1865                 buf.writestring(t.toChars());
1866                 buf.writeByte(')');
1867                 if (target.ptrsize == 8)
1868                     goto case Tuns64;
1869                 else if (target.ptrsize == 4 ||
1870                          target.ptrsize == 2)
1871                     goto case Tuns32;
1872                 else
1873                     assert(0);
1874 
1875             case Tvoid:
1876                 buf.writestring("cast(void)0");
1877                 break;
1878 
1879             default:
1880                 /* This can happen if errors, such as
1881                  * the type is painted on like in fromConstInitializer().
1882                  */
1883                 if (!global.errors)
1884                 {
1885                     assert(0);
1886                 }
1887                 break;
1888             }
1889         }
1890         else if (v & 0x8000000000000000L)
1891             buf.printf("0x%llx", v);
1892         else
1893             buf.print(v);
1894     }
1895 
1896     override void visit(ErrorExp e)
1897     {
1898         buf.writestring("__error");
1899     }
1900 
1901     override void visit(VoidInitExp e)
1902     {
1903         buf.writestring("__void");
1904     }
1905 
1906     void floatToBuffer(Type type, real_t value)
1907     {
1908         .floatToBuffer(type, value, buf, hgs.hdrgen);
1909     }
1910 
1911     override void visit(RealExp e)
1912     {
1913         floatToBuffer(e.type, e.value);
1914     }
1915 
1916     override void visit(ComplexExp e)
1917     {
1918         /* Print as:
1919          *  (re+imi)
1920          */
1921         buf.writeByte('(');
1922         floatToBuffer(e.type, creall(e.value));
1923         buf.writeByte('+');
1924         floatToBuffer(e.type, cimagl(e.value));
1925         buf.writestring("i)");
1926     }
1927 
1928     override void visit(IdentifierExp e)
1929     {
1930         if (hgs.hdrgen || hgs.ddoc)
1931             buf.writestring(e.ident.toHChars2());
1932         else
1933             buf.writestring(e.ident.toString());
1934     }
1935 
1936     override void visit(DsymbolExp e)
1937     {
1938         buf.writestring(e.s.toChars());
1939     }
1940 
1941     override void visit(ThisExp e)
1942     {
1943         buf.writestring("this");
1944     }
1945 
1946     override void visit(SuperExp e)
1947     {
1948         buf.writestring("super");
1949     }
1950 
1951     override void visit(NullExp e)
1952     {
1953         buf.writestring("null");
1954     }
1955 
1956     override void visit(StringExp e)
1957     {
1958         buf.writeByte('"');
1959         const o = buf.length;
1960         for (size_t i = 0; i < e.len; i++)
1961         {
1962             const c = e.charAt(i);
1963             switch (c)
1964             {
1965             case '"':
1966             case '\\':
1967                 buf.writeByte('\\');
1968                 goto default;
1969             default:
1970                 if (c <= 0xFF)
1971                 {
1972                     if (c <= 0x7F && isprint(c))
1973                         buf.writeByte(c);
1974                     else
1975                         buf.printf("\\x%02x", c);
1976                 }
1977                 else if (c <= 0xFFFF)
1978                     buf.printf("\\x%02x\\x%02x", c & 0xFF, c >> 8);
1979                 else
1980                     buf.printf("\\x%02x\\x%02x\\x%02x\\x%02x", c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF, c >> 24);
1981                 break;
1982             }
1983         }
1984         if (hgs.ddoc)
1985             escapeDdocString(buf, o);
1986         buf.writeByte('"');
1987         if (e.postfix)
1988             buf.writeByte(e.postfix);
1989     }
1990 
1991     override void visit(ArrayLiteralExp e)
1992     {
1993         buf.writeByte('[');
1994         argsToBuffer(e.elements, buf, hgs, e.basis);
1995         buf.writeByte(']');
1996     }
1997 
1998     override void visit(AssocArrayLiteralExp e)
1999     {
2000         buf.writeByte('[');
2001         foreach (i, key; *e.keys)
2002         {
2003             if (i)
2004                 buf.writestring(", ");
2005             expToBuffer(key, PREC.assign, buf, hgs);
2006             buf.writeByte(':');
2007             auto value = (*e.values)[i];
2008             expToBuffer(value, PREC.assign, buf, hgs);
2009         }
2010         buf.writeByte(']');
2011     }
2012 
2013     override void visit(StructLiteralExp e)
2014     {
2015         buf.writestring(e.sd.toChars());
2016         buf.writeByte('(');
2017         // CTFE can generate struct literals that contain an AddrExp pointing
2018         // to themselves, need to avoid infinite recursion:
2019         // struct S { this(int){ this.s = &this; } S* s; }
2020         // const foo = new S(0);
2021         if (e.stageflags & stageToCBuffer)
2022             buf.writestring("<recursion>");
2023         else
2024         {
2025             const old = e.stageflags;
2026             e.stageflags |= stageToCBuffer;
2027             argsToBuffer(e.elements, buf, hgs);
2028             e.stageflags = old;
2029         }
2030         buf.writeByte(')');
2031     }
2032 
2033     override void visit(TypeExp e)
2034     {
2035         typeToBuffer(e.type, null, buf, hgs);
2036     }
2037 
2038     override void visit(ScopeExp e)
2039     {
2040         if (e.sds.isTemplateInstance())
2041         {
2042             e.sds.dsymbolToBuffer(buf, hgs);
2043         }
2044         else if (hgs !is null && hgs.ddoc)
2045         {
2046             // fixes bug 6491
2047             if (auto m = e.sds.isModule())
2048                 buf.writestring(m.md.toChars());
2049             else
2050                 buf.writestring(e.sds.toChars());
2051         }
2052         else
2053         {
2054             buf.writestring(e.sds.kind());
2055             buf.writeByte(' ');
2056             buf.writestring(e.sds.toChars());
2057         }
2058     }
2059 
2060     override void visit(TemplateExp e)
2061     {
2062         buf.writestring(e.td.toChars());
2063     }
2064 
2065     override void visit(NewExp e)
2066     {
2067         if (e.thisexp)
2068         {
2069             expToBuffer(e.thisexp, PREC.primary, buf, hgs);
2070             buf.writeByte('.');
2071         }
2072         buf.writestring("new ");
2073         if (e.newargs && e.newargs.dim)
2074         {
2075             buf.writeByte('(');
2076             argsToBuffer(e.newargs, buf, hgs);
2077             buf.writeByte(')');
2078         }
2079         typeToBuffer(e.newtype, null, buf, hgs);
2080         if (e.arguments && e.arguments.dim)
2081         {
2082             buf.writeByte('(');
2083             argsToBuffer(e.arguments, buf, hgs);
2084             buf.writeByte(')');
2085         }
2086     }
2087 
2088     override void visit(NewAnonClassExp e)
2089     {
2090         if (e.thisexp)
2091         {
2092             expToBuffer(e.thisexp, PREC.primary, buf, hgs);
2093             buf.writeByte('.');
2094         }
2095         buf.writestring("new");
2096         if (e.newargs && e.newargs.dim)
2097         {
2098             buf.writeByte('(');
2099             argsToBuffer(e.newargs, buf, hgs);
2100             buf.writeByte(')');
2101         }
2102         buf.writestring(" class ");
2103         if (e.arguments && e.arguments.dim)
2104         {
2105             buf.writeByte('(');
2106             argsToBuffer(e.arguments, buf, hgs);
2107             buf.writeByte(')');
2108         }
2109         if (e.cd)
2110             e.cd.dsymbolToBuffer(buf, hgs);
2111     }
2112 
2113     override void visit(SymOffExp e)
2114     {
2115         if (e.offset)
2116             buf.printf("(& %s%+lld)", e.var.toChars(), e.offset);
2117         else if (e.var.isTypeInfoDeclaration())
2118             buf.writestring(e.var.toChars());
2119         else
2120             buf.printf("& %s", e.var.toChars());
2121     }
2122 
2123     override void visit(VarExp e)
2124     {
2125         buf.writestring(e.var.toChars());
2126     }
2127 
2128     override void visit(OverExp e)
2129     {
2130         buf.writestring(e.vars.ident.toString());
2131     }
2132 
2133     override void visit(TupleExp e)
2134     {
2135         if (e.e0)
2136         {
2137             buf.writeByte('(');
2138             e.e0.accept(this);
2139             buf.writestring(", tuple(");
2140             argsToBuffer(e.exps, buf, hgs);
2141             buf.writestring("))");
2142         }
2143         else
2144         {
2145             buf.writestring("tuple(");
2146             argsToBuffer(e.exps, buf, hgs);
2147             buf.writeByte(')');
2148         }
2149     }
2150 
2151     override void visit(FuncExp e)
2152     {
2153         e.fd.dsymbolToBuffer(buf, hgs);
2154         //buf.writestring(e.fd.toChars());
2155     }
2156 
2157     override void visit(DeclarationExp e)
2158     {
2159         /* Normal dmd execution won't reach here - regular variable declarations
2160          * are handled in visit(ExpStatement), so here would be used only when
2161          * we'll directly call Expression.toChars() for debugging.
2162          */
2163         if (e.declaration)
2164         {
2165             if (auto var = e.declaration.isVarDeclaration())
2166             {
2167             // For debugging use:
2168             // - Avoid printing newline.
2169             // - Intentionally use the format (Type var;)
2170             //   which isn't correct as regular D code.
2171                 buf.writeByte('(');
2172 
2173                 scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
2174                 v.visitVarDecl(var, false);
2175 
2176                 buf.writeByte(';');
2177                 buf.writeByte(')');
2178             }
2179             else e.declaration.dsymbolToBuffer(buf, hgs);
2180         }
2181     }
2182 
2183     override void visit(TypeidExp e)
2184     {
2185         buf.writestring("typeid(");
2186         objectToBuffer(e.obj, buf, hgs);
2187         buf.writeByte(')');
2188     }
2189 
2190     override void visit(TraitsExp e)
2191     {
2192         buf.writestring("__traits(");
2193         if (e.ident)
2194             buf.writestring(e.ident.toString());
2195         if (e.args)
2196         {
2197             foreach (arg; *e.args)
2198             {
2199                 buf.writestring(", ");
2200                 objectToBuffer(arg, buf, hgs);
2201             }
2202         }
2203         buf.writeByte(')');
2204     }
2205 
2206     override void visit(HaltExp e)
2207     {
2208         buf.writestring("halt");
2209     }
2210 
2211     override void visit(IsExp e)
2212     {
2213         buf.writestring("is(");
2214         typeToBuffer(e.targ, e.id, buf, hgs);
2215         if (e.tok2 != TOK.reserved)
2216         {
2217             buf.printf(" %s %s", Token.toChars(e.tok), Token.toChars(e.tok2));
2218         }
2219         else if (e.tspec)
2220         {
2221             if (e.tok == TOK.colon)
2222                 buf.writestring(" : ");
2223             else
2224                 buf.writestring(" == ");
2225             typeToBuffer(e.tspec, null, buf, hgs);
2226         }
2227         if (e.parameters && e.parameters.dim)
2228         {
2229             buf.writestring(", ");
2230             scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
2231             v.visitTemplateParameters(e.parameters);
2232         }
2233         buf.writeByte(')');
2234     }
2235 
2236     override void visit(UnaExp e)
2237     {
2238         buf.writestring(Token.toString(e.op));
2239         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2240     }
2241 
2242     override void visit(BinExp e)
2243     {
2244         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2245         buf.writeByte(' ');
2246         buf.writestring(Token.toString(e.op));
2247         buf.writeByte(' ');
2248         expToBuffer(e.e2, cast(PREC)(precedence[e.op] + 1), buf, hgs);
2249     }
2250 
2251     override void visit(CommaExp e)
2252     {
2253         // CommaExp is generated by the compiler so it shouldn't
2254         // appear in error messages or header files.
2255         // For now, this treats the case where the compiler
2256         // generates CommaExp for temporaries by calling
2257         // the `sideeffect.copyToTemp` function.
2258         auto ve = e.e2.isVarExp();
2259 
2260         // not a CommaExp introduced for temporaries, go on
2261         // the old path
2262         if (!ve || !(ve.var.storage_class & STC.temp))
2263         {
2264             visit(cast(BinExp)e);
2265             return;
2266         }
2267 
2268         // CommaExp that contain temporaries inserted via
2269         // `copyToTemp` are usually of the form
2270         // ((T __temp = exp), __tmp).
2271         // Asserts are here to easily spot
2272         // missing cases where CommaExp
2273         // are used for other constructs
2274         auto vd = ve.var.isVarDeclaration();
2275         assert(vd && vd._init);
2276 
2277         if (auto ei = vd._init.isExpInitializer())
2278         {
2279             Expression commaExtract;
2280             auto exp = ei.exp;
2281             if (auto ce = exp.isConstructExp())
2282                 commaExtract = ce.e2;
2283             else if (auto se = exp.isStructLiteralExp())
2284                 commaExtract = se;
2285 
2286             if (commaExtract)
2287             {
2288                 expToBuffer(commaExtract, precedence[exp.op], buf, hgs);
2289                 return;
2290             }
2291         }
2292 
2293         // not one of the known cases, go on the old path
2294         visit(cast(BinExp)e);
2295         return;
2296     }
2297 
2298     override void visit(MixinExp e)
2299     {
2300         buf.writestring("mixin(");
2301         argsToBuffer(e.exps, buf, hgs, null);
2302         buf.writeByte(')');
2303     }
2304 
2305     override void visit(ImportExp e)
2306     {
2307         buf.writestring("import(");
2308         expToBuffer(e.e1, PREC.assign, buf, hgs);
2309         buf.writeByte(')');
2310     }
2311 
2312     override void visit(AssertExp e)
2313     {
2314         buf.writestring("assert(");
2315         expToBuffer(e.e1, PREC.assign, buf, hgs);
2316         if (e.msg)
2317         {
2318             buf.writestring(", ");
2319             expToBuffer(e.msg, PREC.assign, buf, hgs);
2320         }
2321         buf.writeByte(')');
2322     }
2323 
2324     override void visit(DotIdExp e)
2325     {
2326         expToBuffer(e.e1, PREC.primary, buf, hgs);
2327         buf.writeByte('.');
2328         buf.writestring(e.ident.toString());
2329     }
2330 
2331     override void visit(DotTemplateExp e)
2332     {
2333         expToBuffer(e.e1, PREC.primary, buf, hgs);
2334         buf.writeByte('.');
2335         buf.writestring(e.td.toChars());
2336     }
2337 
2338     override void visit(DotVarExp e)
2339     {
2340         expToBuffer(e.e1, PREC.primary, buf, hgs);
2341         buf.writeByte('.');
2342         buf.writestring(e.var.toChars());
2343     }
2344 
2345     override void visit(DotTemplateInstanceExp e)
2346     {
2347         expToBuffer(e.e1, PREC.primary, buf, hgs);
2348         buf.writeByte('.');
2349         e.ti.dsymbolToBuffer(buf, hgs);
2350     }
2351 
2352     override void visit(DelegateExp e)
2353     {
2354         buf.writeByte('&');
2355         if (!e.func.isNested() || e.func.needThis())
2356         {
2357             expToBuffer(e.e1, PREC.primary, buf, hgs);
2358             buf.writeByte('.');
2359         }
2360         buf.writestring(e.func.toChars());
2361     }
2362 
2363     override void visit(DotTypeExp e)
2364     {
2365         expToBuffer(e.e1, PREC.primary, buf, hgs);
2366         buf.writeByte('.');
2367         buf.writestring(e.sym.toChars());
2368     }
2369 
2370     override void visit(CallExp e)
2371     {
2372         if (e.e1.op == TOK.type)
2373         {
2374             /* Avoid parens around type to prevent forbidden cast syntax:
2375              *   (sometype)(arg1)
2376              * This is ok since types in constructor calls
2377              * can never depend on parens anyway
2378              */
2379             e.e1.accept(this);
2380         }
2381         else
2382             expToBuffer(e.e1, precedence[e.op], buf, hgs);
2383         buf.writeByte('(');
2384         argsToBuffer(e.arguments, buf, hgs);
2385         buf.writeByte(')');
2386     }
2387 
2388     override void visit(PtrExp e)
2389     {
2390         buf.writeByte('*');
2391         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2392     }
2393 
2394     override void visit(DeleteExp e)
2395     {
2396         buf.writestring("delete ");
2397         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2398     }
2399 
2400     override void visit(CastExp e)
2401     {
2402         buf.writestring("cast(");
2403         if (e.to)
2404             typeToBuffer(e.to, null, buf, hgs);
2405         else
2406         {
2407             MODtoBuffer(buf, e.mod);
2408         }
2409         buf.writeByte(')');
2410         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2411     }
2412 
2413     override void visit(VectorExp e)
2414     {
2415         buf.writestring("cast(");
2416         typeToBuffer(e.to, null, buf, hgs);
2417         buf.writeByte(')');
2418         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2419     }
2420 
2421     override void visit(VectorArrayExp e)
2422     {
2423         expToBuffer(e.e1, PREC.primary, buf, hgs);
2424         buf.writestring(".array");
2425     }
2426 
2427     override void visit(SliceExp e)
2428     {
2429         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2430         buf.writeByte('[');
2431         if (e.upr || e.lwr)
2432         {
2433             if (e.lwr)
2434                 sizeToBuffer(e.lwr, buf, hgs);
2435             else
2436                 buf.writeByte('0');
2437             buf.writestring("..");
2438             if (e.upr)
2439                 sizeToBuffer(e.upr, buf, hgs);
2440             else
2441                 buf.writeByte('$');
2442         }
2443         buf.writeByte(']');
2444     }
2445 
2446     override void visit(ArrayLengthExp e)
2447     {
2448         expToBuffer(e.e1, PREC.primary, buf, hgs);
2449         buf.writestring(".length");
2450     }
2451 
2452     override void visit(IntervalExp e)
2453     {
2454         expToBuffer(e.lwr, PREC.assign, buf, hgs);
2455         buf.writestring("..");
2456         expToBuffer(e.upr, PREC.assign, buf, hgs);
2457     }
2458 
2459     override void visit(DelegatePtrExp e)
2460     {
2461         expToBuffer(e.e1, PREC.primary, buf, hgs);
2462         buf.writestring(".ptr");
2463     }
2464 
2465     override void visit(DelegateFuncptrExp e)
2466     {
2467         expToBuffer(e.e1, PREC.primary, buf, hgs);
2468         buf.writestring(".funcptr");
2469     }
2470 
2471     override void visit(ArrayExp e)
2472     {
2473         expToBuffer(e.e1, PREC.primary, buf, hgs);
2474         buf.writeByte('[');
2475         argsToBuffer(e.arguments, buf, hgs);
2476         buf.writeByte(']');
2477     }
2478 
2479     override void visit(DotExp e)
2480     {
2481         expToBuffer(e.e1, PREC.primary, buf, hgs);
2482         buf.writeByte('.');
2483         expToBuffer(e.e2, PREC.primary, buf, hgs);
2484     }
2485 
2486     override void visit(IndexExp e)
2487     {
2488         expToBuffer(e.e1, PREC.primary, buf, hgs);
2489         buf.writeByte('[');
2490         sizeToBuffer(e.e2, buf, hgs);
2491         buf.writeByte(']');
2492     }
2493 
2494     override void visit(PostExp e)
2495     {
2496         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2497         buf.writestring(Token.toString(e.op));
2498     }
2499 
2500     override void visit(PreExp e)
2501     {
2502         buf.writestring(Token.toString(e.op));
2503         expToBuffer(e.e1, precedence[e.op], buf, hgs);
2504     }
2505 
2506     override void visit(RemoveExp e)
2507     {
2508         expToBuffer(e.e1, PREC.primary, buf, hgs);
2509         buf.writestring(".remove(");
2510         expToBuffer(e.e2, PREC.assign, buf, hgs);
2511         buf.writeByte(')');
2512     }
2513 
2514     override void visit(CondExp e)
2515     {
2516         expToBuffer(e.econd, PREC.oror, buf, hgs);
2517         buf.writestring(" ? ");
2518         expToBuffer(e.e1, PREC.expr, buf, hgs);
2519         buf.writestring(" : ");
2520         expToBuffer(e.e2, PREC.cond, buf, hgs);
2521     }
2522 
2523     override void visit(DefaultInitExp e)
2524     {
2525         buf.writestring(Token.toString(e.op));
2526     }
2527 
2528     override void visit(ClassReferenceExp e)
2529     {
2530         buf.writestring(e.value.toChars());
2531     }
2532 }
2533 
2534 /**
2535  * Formats `value` as a literal of type `type` into `buf`.
2536  *
2537  * Params:
2538  *   type     = literal type (e.g. Tfloat)
2539  *   value    = value to print
2540  *   buf      = target buffer
2541  *   allowHex = whether hex floating point literals may be used
2542  *              for greater accuracy
2543  */
2544 void floatToBuffer(Type type, const real_t value, OutBuffer* buf, const bool allowHex)
2545 {
2546     /** sizeof(value)*3 is because each byte of mantissa is max
2547         of 256 (3 characters). The string will be "-M.MMMMe-4932".
2548         (ie, 8 chars more than mantissa). Plus one for trailing \0.
2549         Plus one for rounding. */
2550     const(size_t) BUFFER_LEN = value.sizeof * 3 + 8 + 1 + 1;
2551     char[BUFFER_LEN] buffer;
2552     CTFloat.sprint(buffer.ptr, 'g', value);
2553     assert(strlen(buffer.ptr) < BUFFER_LEN);
2554     if (allowHex)
2555     {
2556         real_t r = CTFloat.parse(buffer.ptr);
2557         if (r != value) // if exact duplication
2558             CTFloat.sprint(buffer.ptr, 'a', value);
2559     }
2560     buf.writestring(buffer.ptr);
2561     if (buffer.ptr[strlen(buffer.ptr) - 1] == '.')
2562         buf.remove(buf.length() - 1, 1);
2563 
2564     if (type)
2565     {
2566         Type t = type.toBasetype();
2567         switch (t.ty)
2568         {
2569         case Tfloat32:
2570         case Timaginary32:
2571         case Tcomplex32:
2572             buf.writeByte('F');
2573             break;
2574         case Tfloat80:
2575         case Timaginary80:
2576         case Tcomplex80:
2577             buf.writeByte('L');
2578             break;
2579         default:
2580             break;
2581         }
2582         if (t.isimaginary())
2583             buf.writeByte('i');
2584     }
2585 }
2586 
2587 private void templateParameterToBuffer(TemplateParameter tp, OutBuffer* buf, HdrGenState* hgs)
2588 {
2589     scope v = new TemplateParameterPrettyPrintVisitor(buf, hgs);
2590     tp.accept(v);
2591 }
2592 
2593 private extern (C++) final class TemplateParameterPrettyPrintVisitor : Visitor
2594 {
2595     alias visit = Visitor.visit;
2596 public:
2597     OutBuffer* buf;
2598     HdrGenState* hgs;
2599 
2600     extern (D) this(OutBuffer* buf, HdrGenState* hgs)
2601     {
2602         this.buf = buf;
2603         this.hgs = hgs;
2604     }
2605 
2606     override void visit(TemplateTypeParameter tp)
2607     {
2608         buf.writestring(tp.ident.toString());
2609         if (tp.specType)
2610         {
2611             buf.writestring(" : ");
2612             typeToBuffer(tp.specType, null, buf, hgs);
2613         }
2614         if (tp.defaultType)
2615         {
2616             buf.writestring(" = ");
2617             typeToBuffer(tp.defaultType, null, buf, hgs);
2618         }
2619     }
2620 
2621     override void visit(TemplateThisParameter tp)
2622     {
2623         buf.writestring("this ");
2624         visit(cast(TemplateTypeParameter)tp);
2625     }
2626 
2627     override void visit(TemplateAliasParameter tp)
2628     {
2629         buf.writestring("alias ");
2630         if (tp.specType)
2631             typeToBuffer(tp.specType, tp.ident, buf, hgs);
2632         else
2633             buf.writestring(tp.ident.toString());
2634         if (tp.specAlias)
2635         {
2636             buf.writestring(" : ");
2637             objectToBuffer(tp.specAlias, buf, hgs);
2638         }
2639         if (tp.defaultAlias)
2640         {
2641             buf.writestring(" = ");
2642             objectToBuffer(tp.defaultAlias, buf, hgs);
2643         }
2644     }
2645 
2646     override void visit(TemplateValueParameter tp)
2647     {
2648         typeToBuffer(tp.valType, tp.ident, buf, hgs);
2649         if (tp.specValue)
2650         {
2651             buf.writestring(" : ");
2652             tp.specValue.expressionToBuffer(buf, hgs);
2653         }
2654         if (tp.defaultValue)
2655         {
2656             buf.writestring(" = ");
2657             tp.defaultValue.expressionToBuffer(buf, hgs);
2658         }
2659     }
2660 
2661     override void visit(TemplateTupleParameter tp)
2662     {
2663         buf.writestring(tp.ident.toString());
2664         buf.writestring("...");
2665     }
2666 }
2667 
2668 private void conditionToBuffer(Condition c, OutBuffer* buf, HdrGenState* hgs)
2669 {
2670     scope v = new ConditionPrettyPrintVisitor(buf, hgs);
2671     c.accept(v);
2672 }
2673 
2674 private extern (C++) final class ConditionPrettyPrintVisitor : Visitor
2675 {
2676     alias visit = Visitor.visit;
2677 public:
2678     OutBuffer* buf;
2679     HdrGenState* hgs;
2680 
2681     extern (D) this(OutBuffer* buf, HdrGenState* hgs)
2682     {
2683         this.buf = buf;
2684         this.hgs = hgs;
2685     }
2686 
2687     override void visit(DebugCondition c)
2688     {
2689         buf.writestring("debug (");
2690         if (c.ident)
2691             buf.writestring(c.ident.toString());
2692         else
2693             buf.print(c.level);
2694         buf.writeByte(')');
2695     }
2696 
2697     override void visit(VersionCondition c)
2698     {
2699         buf.writestring("version (");
2700         if (c.ident)
2701             buf.writestring(c.ident.toString());
2702         else
2703             buf.print(c.level);
2704         buf.writeByte(')');
2705     }
2706 
2707     override void visit(StaticIfCondition c)
2708     {
2709         buf.writestring("static if (");
2710         c.exp.expressionToBuffer(buf, hgs);
2711         buf.writeByte(')');
2712     }
2713 }
2714 
2715 void toCBuffer(const Statement s, OutBuffer* buf, HdrGenState* hgs)
2716 {
2717     scope v = new StatementPrettyPrintVisitor(buf, hgs);
2718     (cast() s).accept(v);
2719 }
2720 
2721 void toCBuffer(const Type t, OutBuffer* buf, const Identifier ident, HdrGenState* hgs)
2722 {
2723     typeToBuffer(cast() t, ident, buf, hgs);
2724 }
2725 
2726 void toCBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs)
2727 {
2728     scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
2729     s.accept(v);
2730 }
2731 
2732 // used from TemplateInstance::toChars() and TemplateMixin::toChars()
2733 void toCBufferInstance(const TemplateInstance ti, OutBuffer* buf, bool qualifyTypes = false)
2734 {
2735     HdrGenState hgs;
2736     hgs.fullQual = qualifyTypes;
2737     scope v = new DsymbolPrettyPrintVisitor(buf, &hgs);
2738     v.visit(cast() ti);
2739 }
2740 
2741 void toCBuffer(const Initializer iz, OutBuffer* buf, HdrGenState* hgs)
2742 {
2743     initializerToBuffer(cast() iz, buf, hgs);
2744 }
2745 
2746 bool stcToBuffer(OutBuffer* buf, StorageClass stc)
2747 {
2748     bool result = false;
2749     if ((stc & (STC.return_ | STC.scope_)) == (STC.return_ | STC.scope_))
2750         stc &= ~STC.scope_;
2751     if (stc & STC.scopeinferred)
2752         stc &= ~(STC.scope_ | STC.scopeinferred);
2753     while (stc)
2754     {
2755         const s = stcToString(stc);
2756         if (!s.length)
2757             break;
2758         if (result)
2759             buf.writeByte(' ');
2760         result = true;
2761         buf.writestring(s);
2762     }
2763     return result;
2764 }
2765 
2766 /*************************************************
2767  * Pick off one of the storage classes from stc,
2768  * and return a string representation of it.
2769  * stc is reduced by the one picked.
2770  */
2771 string stcToString(ref StorageClass stc)
2772 {
2773     static struct SCstring
2774     {
2775         StorageClass stc;
2776         string id;
2777     }
2778 
2779     // Note: The identifier needs to be `\0` terminated
2780     // as some code assumes it (e.g. when printing error messages)
2781     static immutable SCstring[] table =
2782     [
2783         SCstring(STC.auto_, Token.toString(TOK.auto_)),
2784         SCstring(STC.scope_, Token.toString(TOK.scope_)),
2785         SCstring(STC.static_, Token.toString(TOK.static_)),
2786         SCstring(STC.extern_, Token.toString(TOK.extern_)),
2787         SCstring(STC.const_, Token.toString(TOK.const_)),
2788         SCstring(STC.final_, Token.toString(TOK.final_)),
2789         SCstring(STC.abstract_, Token.toString(TOK.abstract_)),
2790         SCstring(STC.synchronized_, Token.toString(TOK.synchronized_)),
2791         SCstring(STC.deprecated_, Token.toString(TOK.deprecated_)),
2792         SCstring(STC.override_, Token.toString(TOK.override_)),
2793         SCstring(STC.lazy_, Token.toString(TOK.lazy_)),
2794         SCstring(STC.alias_, Token.toString(TOK.alias_)),
2795         SCstring(STC.out_, Token.toString(TOK.out_)),
2796         SCstring(STC.in_, Token.toString(TOK.in_)),
2797         SCstring(STC.manifest, Token.toString(TOK.enum_)),
2798         SCstring(STC.immutable_, Token.toString(TOK.immutable_)),
2799         SCstring(STC.shared_, Token.toString(TOK.shared_)),
2800         SCstring(STC.nothrow_, Token.toString(TOK.nothrow_)),
2801         SCstring(STC.wild, Token.toString(TOK.inout_)),
2802         SCstring(STC.pure_, Token.toString(TOK.pure_)),
2803         SCstring(STC.ref_, Token.toString(TOK.ref_)),
2804         SCstring(STC.return_, Token.toString(TOK.return_)),
2805         SCstring(STC.tls, "__thread"),
2806         SCstring(STC.gshared, Token.toString(TOK.gshared)),
2807         SCstring(STC.nogc, "@nogc"),
2808         SCstring(STC.live, "@live"),
2809         SCstring(STC.property, "@property"),
2810         SCstring(STC.safe, "@safe"),
2811         SCstring(STC.trusted, "@trusted"),
2812         SCstring(STC.system, "@system"),
2813         SCstring(STC.disable, "@disable"),
2814         SCstring(STC.future, "@__future"),
2815         SCstring(STC.local, "__local"),
2816     ];
2817     foreach (ref entry; table)
2818     {
2819         const StorageClass tbl = entry.stc;
2820         assert(tbl & STCStorageClass);
2821         if (stc & tbl)
2822         {
2823             stc &= ~tbl;
2824             return entry.id;
2825         }
2826     }
2827     //printf("stc = %llx\n", stc);
2828     return null;
2829 }
2830 
2831 /// Ditto
2832 extern (D) string trustToString(TRUST trust) pure nothrow
2833 {
2834     final switch (trust)
2835     {
2836     case TRUST.default_:
2837         return null;
2838     case TRUST.system:
2839         return "@system";
2840     case TRUST.trusted:
2841         return "@trusted";
2842     case TRUST.safe:
2843         return "@safe";
2844     }
2845 }
2846 
2847 private void linkageToBuffer(OutBuffer* buf, LINK linkage)
2848 {
2849     const s = linkageToString(linkage);
2850     if (s.length)
2851     {
2852         buf.writestring("extern (");
2853         buf.writestring(s);
2854         buf.writeByte(')');
2855     }
2856 }
2857 
2858 const(char)* linkageToChars(LINK linkage)
2859 {
2860     /// Works because we return a literal
2861     return linkageToString(linkage).ptr;
2862 }
2863 
2864 string linkageToString(LINK linkage) pure nothrow
2865 {
2866     final switch (linkage)
2867     {
2868     case LINK.default_:
2869         return null;
2870     case LINK.d:
2871         return "D";
2872     case LINK.c:
2873         return "C";
2874     case LINK.cpp:
2875         return "C++";
2876     case LINK.windows:
2877         return "Windows";
2878     case LINK.objc:
2879         return "Objective-C";
2880     case LINK.system:
2881         return "System";
2882     }
2883 }
2884 
2885 void visibilityToBuffer(OutBuffer* buf, Visibility vis)
2886 {
2887     buf.writestring(visibilityToString(vis.kind));
2888     if (vis.kind == Visibility.Kind.package_ && vis.pkg)
2889     {
2890         buf.writeByte('(');
2891         buf.writestring(vis.pkg.toPrettyChars(true));
2892         buf.writeByte(')');
2893     }
2894 }
2895 
2896 /**
2897  * Returns:
2898  *   a human readable representation of `kind`
2899  */
2900 const(char)* visibilityToChars(Visibility.Kind kind)
2901 {
2902     // Null terminated because we return a literal
2903     return visibilityToString(kind).ptr;
2904 }
2905 
2906 /// Ditto
2907 extern (D) string visibilityToString(Visibility.Kind kind) nothrow pure
2908 {
2909     final switch (kind)
2910     {
2911     case Visibility.Kind.undefined:
2912         return null;
2913     case Visibility.Kind.none:
2914         return "none";
2915     case Visibility.Kind.private_:
2916         return "private";
2917     case Visibility.Kind.package_:
2918         return "package";
2919     case Visibility.Kind.protected_:
2920         return "protected";
2921     case Visibility.Kind.public_:
2922         return "public";
2923     case Visibility.Kind.export_:
2924         return "export";
2925     }
2926 }
2927 
2928 // Print the full function signature with correct ident, attributes and template args
2929 void functionToBufferFull(TypeFunction tf, OutBuffer* buf, const Identifier ident, HdrGenState* hgs, TemplateDeclaration td)
2930 {
2931     //printf("TypeFunction::toCBuffer() this = %p\n", this);
2932     visitFuncIdentWithPrefix(tf, ident, td, buf, hgs);
2933 }
2934 
2935 // ident is inserted before the argument list and will be "function" or "delegate" for a type
2936 void functionToBufferWithIdent(TypeFunction tf, OutBuffer* buf, const(char)* ident, bool isStatic)
2937 {
2938     HdrGenState hgs;
2939     visitFuncIdentWithPostfix(tf, ident.toDString(), buf, &hgs, isStatic);
2940 }
2941 
2942 void toCBuffer(const Expression e, OutBuffer* buf, HdrGenState* hgs)
2943 {
2944     scope v = new ExpressionPrettyPrintVisitor(buf, hgs);
2945     (cast() e).accept(v);
2946 }
2947 
2948 /**************************************************
2949  * Write out argument types to buf.
2950  */
2951 void argExpTypesToCBuffer(OutBuffer* buf, Expressions* arguments)
2952 {
2953     if (!arguments || !arguments.dim)
2954         return;
2955     HdrGenState hgs;
2956     foreach (i, arg; *arguments)
2957     {
2958         if (i)
2959             buf.writestring(", ");
2960         typeToBuffer(arg.type, null, buf, &hgs);
2961     }
2962 }
2963 
2964 void toCBuffer(const TemplateParameter tp, OutBuffer* buf, HdrGenState* hgs)
2965 {
2966     scope v = new TemplateParameterPrettyPrintVisitor(buf, hgs);
2967     (cast() tp).accept(v);
2968 }
2969 
2970 void arrayObjectsToBuffer(OutBuffer* buf, Objects* objects)
2971 {
2972     if (!objects || !objects.dim)
2973         return;
2974     HdrGenState hgs;
2975     foreach (i, o; *objects)
2976     {
2977         if (i)
2978             buf.writestring(", ");
2979         objectToBuffer(o, buf, &hgs);
2980     }
2981 }
2982 
2983 /*************************************************************
2984  * Pretty print function parameters.
2985  * Params:
2986  *  pl = parameter list to print
2987  * Returns: Null-terminated string representing parameters.
2988  */
2989 extern (C++) const(char)* parametersTypeToChars(ParameterList pl)
2990 {
2991     OutBuffer buf;
2992     HdrGenState hgs;
2993     parametersToBuffer(pl, &buf, &hgs);
2994     return buf.extractChars();
2995 }
2996 
2997 /*************************************************************
2998  * Pretty print function parameter.
2999  * Params:
3000  *  parameter = parameter to print.
3001  *  tf = TypeFunction which holds parameter.
3002  *  fullQual = whether to fully qualify types.
3003  * Returns: Null-terminated string representing parameters.
3004  */
3005 const(char)* parameterToChars(Parameter parameter, TypeFunction tf, bool fullQual)
3006 {
3007     OutBuffer buf;
3008     HdrGenState hgs;
3009     hgs.fullQual = fullQual;
3010 
3011     parameterToBuffer(parameter, &buf, &hgs);
3012 
3013     if (tf.parameterList.varargs == VarArg.typesafe && parameter == tf.parameterList[tf.parameterList.parameters.dim - 1])
3014     {
3015         buf.writestring("...");
3016     }
3017     return buf.extractChars();
3018 }
3019 
3020 
3021 /*************************************************
3022  * Write ParameterList to buffer.
3023  * Params:
3024  *      pl = parameter list to serialize
3025  *      buf = buffer to write it to
3026  *      hgs = context
3027  */
3028 
3029 private void parametersToBuffer(ParameterList pl, OutBuffer* buf, HdrGenState* hgs)
3030 {
3031     buf.writeByte('(');
3032     foreach (i; 0 .. pl.length)
3033     {
3034         if (i)
3035             buf.writestring(", ");
3036         pl[i].parameterToBuffer(buf, hgs);
3037     }
3038     final switch (pl.varargs)
3039     {
3040         case VarArg.none:
3041             break;
3042 
3043         case VarArg.variadic:
3044             if (pl.length)
3045                 buf.writestring(", ");
3046 
3047             if (stcToBuffer(buf, pl.stc))
3048                 buf.writeByte(' ');
3049             goto case VarArg.typesafe;
3050 
3051         case VarArg.typesafe:
3052             buf.writestring("...");
3053             break;
3054     }
3055     buf.writeByte(')');
3056 }
3057 
3058 
3059 /***********************************************************
3060  * Write parameter `p` to buffer `buf`.
3061  * Params:
3062  *      p = parameter to serialize
3063  *      buf = buffer to write it to
3064  *      hgs = context
3065  */
3066 private void parameterToBuffer(Parameter p, OutBuffer* buf, HdrGenState* hgs)
3067 {
3068     if (p.userAttribDecl)
3069     {
3070         buf.writeByte('@');
3071 
3072         bool isAnonymous = p.userAttribDecl.atts.dim > 0 && (*p.userAttribDecl.atts)[0].op != TOK.call;
3073         if (isAnonymous)
3074             buf.writeByte('(');
3075 
3076         argsToBuffer(p.userAttribDecl.atts, buf, hgs);
3077 
3078         if (isAnonymous)
3079             buf.writeByte(')');
3080         buf.writeByte(' ');
3081     }
3082     if (p.storageClass & STC.auto_)
3083         buf.writestring("auto ");
3084     if (p.storageClass & STC.return_)
3085         buf.writestring("return ");
3086 
3087     if (p.storageClass & STC.in_)
3088         buf.writestring("in ");
3089     else if (global.params.previewIn && p.storageClass & STC.ref_)
3090         buf.writestring("ref ");
3091     else if (p.storageClass & STC.out_)
3092         buf.writestring("out ");
3093     else if (p.storageClass & STC.lazy_)
3094         buf.writestring("lazy ");
3095     else if (p.storageClass & STC.alias_)
3096         buf.writestring("alias ");
3097 
3098     if (!global.params.previewIn && p.storageClass & STC.ref_)
3099         buf.writestring("ref ");
3100 
3101     StorageClass stc = p.storageClass;
3102     if (p.type && p.type.mod & MODFlags.shared_)
3103         stc &= ~STC.shared_;
3104 
3105     if (stcToBuffer(buf, stc & (STC.const_ | STC.immutable_ | STC.wild | STC.shared_ | STC.scope_ | STC.scopeinferred)))
3106         buf.writeByte(' ');
3107 
3108     if (p.storageClass & STC.alias_)
3109     {
3110         if (p.ident)
3111             buf.writestring(p.ident.toString());
3112     }
3113     else if (p.type.ty == Tident &&
3114              (cast(TypeIdentifier)p.type).ident.toString().length > 3 &&
3115              strncmp((cast(TypeIdentifier)p.type).ident.toChars(), "__T", 3) == 0)
3116     {
3117         // print parameter name, instead of undetermined type parameter
3118         buf.writestring(p.ident.toString());
3119     }
3120     else
3121     {
3122         typeToBuffer(p.type, p.ident, buf, hgs, (stc & STC.in_) ? MODFlags.const_ : 0);
3123     }
3124 
3125     if (p.defaultArg)
3126     {
3127         buf.writestring(" = ");
3128         p.defaultArg.expToBuffer(PREC.assign, buf, hgs);
3129     }
3130 }
3131 
3132 
3133 /**************************************************
3134  * Write out argument list to buf.
3135  */
3136 private void argsToBuffer(Expressions* expressions, OutBuffer* buf, HdrGenState* hgs, Expression basis = null)
3137 {
3138     if (!expressions || !expressions.dim)
3139         return;
3140     version (all)
3141     {
3142         foreach (i, el; *expressions)
3143         {
3144             if (i)
3145                 buf.writestring(", ");
3146             if (!el)
3147                 el = basis;
3148             if (el)
3149                 expToBuffer(el, PREC.assign, buf, hgs);
3150         }
3151     }
3152     else
3153     {
3154         // Sparse style formatting, for debug use only
3155         //      [0..dim: basis, 1: e1, 5: e5]
3156         if (basis)
3157         {
3158             buf.writestring("0..");
3159             buf.print(expressions.dim);
3160             buf.writestring(": ");
3161             expToBuffer(basis, PREC.assign, buf, hgs);
3162         }
3163         foreach (i, el; *expressions)
3164         {
3165             if (el)
3166             {
3167                 if (basis)
3168                 {
3169                     buf.writestring(", ");
3170                     buf.print(i);
3171                     buf.writestring(": ");
3172                 }
3173                 else if (i)
3174                     buf.writestring(", ");
3175                 expToBuffer(el, PREC.assign, buf, hgs);
3176             }
3177         }
3178     }
3179 }
3180 
3181 private void sizeToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs)
3182 {
3183     if (e.type == Type.tsize_t)
3184     {
3185         Expression ex = (e.op == TOK.cast_ ? (cast(CastExp)e).e1 : e);
3186         ex = ex.optimize(WANTvalue);
3187         const dinteger_t uval = ex.op == TOK.int64 ? ex.toInteger() : cast(dinteger_t)-1;
3188         if (cast(sinteger_t)uval >= 0)
3189         {
3190             dinteger_t sizemax = void;
3191             if (target.ptrsize == 8)
3192                 sizemax = 0xFFFFFFFFFFFFFFFFUL;
3193             else if (target.ptrsize == 4)
3194                 sizemax = 0xFFFFFFFFU;
3195             else if (target.ptrsize == 2)
3196                 sizemax = 0xFFFFU;
3197             else
3198                 assert(0);
3199             if (uval <= sizemax && uval <= 0x7FFFFFFFFFFFFFFFUL)
3200             {
3201                 buf.print(uval);
3202                 return;
3203             }
3204         }
3205     }
3206     expToBuffer(e, PREC.assign, buf, hgs);
3207 }
3208 
3209 private void expressionToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs)
3210 {
3211     scope v = new ExpressionPrettyPrintVisitor(buf, hgs);
3212     e.accept(v);
3213 }
3214 
3215 /**************************************************
3216  * Write expression out to buf, but wrap it
3217  * in ( ) if its precedence is less than pr.
3218  */
3219 private void expToBuffer(Expression e, PREC pr, OutBuffer* buf, HdrGenState* hgs)
3220 {
3221     debug
3222     {
3223         if (precedence[e.op] == PREC.zero)
3224             printf("precedence not defined for token '%s'\n", Token.toChars(e.op));
3225     }
3226     if (e.op == 0xFF)
3227     {
3228         buf.writestring("<FF>");
3229         return;
3230     }
3231     assert(precedence[e.op] != PREC.zero);
3232     assert(pr != PREC.zero);
3233     /* Despite precedence, we don't allow a<b<c expressions.
3234      * They must be parenthesized.
3235      */
3236     if (precedence[e.op] < pr || (pr == PREC.rel && precedence[e.op] == pr)
3237         || (pr >= PREC.or && pr <= PREC.and && precedence[e.op] == PREC.rel))
3238     {
3239         buf.writeByte('(');
3240         e.expressionToBuffer(buf, hgs);
3241         buf.writeByte(')');
3242     }
3243     else
3244     {
3245         e.expressionToBuffer(buf, hgs);
3246     }
3247 }
3248 
3249 
3250 /**************************************************
3251  * An entry point to pretty-print type.
3252  */
3253 private void typeToBuffer(Type t, const Identifier ident, OutBuffer* buf, HdrGenState* hgs,
3254                           ubyte modMask = 0)
3255 {
3256     if (auto tf = t.isTypeFunction())
3257     {
3258         visitFuncIdentWithPrefix(tf, ident, null, buf, hgs);
3259         return;
3260     }
3261     visitWithMask(t, modMask, buf, hgs);
3262     if (ident)
3263     {
3264         buf.writeByte(' ');
3265         buf.writestring(ident.toString());
3266     }
3267 }
3268 
3269 private void visitWithMask(Type t, ubyte modMask, OutBuffer* buf, HdrGenState* hgs)
3270 {
3271     // Tuples and functions don't use the type constructor syntax
3272     if (modMask == t.mod || t.ty == Tfunction || t.ty == Ttuple)
3273     {
3274         typeToBufferx(t, buf, hgs);
3275     }
3276     else
3277     {
3278         ubyte m = t.mod & ~(t.mod & modMask);
3279         if (m & MODFlags.shared_)
3280         {
3281             MODtoBuffer(buf, MODFlags.shared_);
3282             buf.writeByte('(');
3283         }
3284         if (m & MODFlags.wild)
3285         {
3286             MODtoBuffer(buf, MODFlags.wild);
3287             buf.writeByte('(');
3288         }
3289         if (m & (MODFlags.const_ | MODFlags.immutable_))
3290         {
3291             MODtoBuffer(buf, m & (MODFlags.const_ | MODFlags.immutable_));
3292             buf.writeByte('(');
3293         }
3294         typeToBufferx(t, buf, hgs);
3295         if (m & (MODFlags.const_ | MODFlags.immutable_))
3296             buf.writeByte(')');
3297         if (m & MODFlags.wild)
3298             buf.writeByte(')');
3299         if (m & MODFlags.shared_)
3300             buf.writeByte(')');
3301     }
3302 }
3303 
3304 
3305 private void dumpTemplateInstance(TemplateInstance ti, OutBuffer* buf, HdrGenState* hgs)
3306 {
3307     buf.writeByte('{');
3308     buf.writenl();
3309     buf.level++;
3310 
3311     if (ti.aliasdecl)
3312     {
3313         ti.aliasdecl.dsymbolToBuffer(buf, hgs);
3314         buf.writenl();
3315     }
3316     else if (ti.members)
3317     {
3318         foreach(m;*ti.members)
3319             m.dsymbolToBuffer(buf, hgs);
3320     }
3321 
3322     buf.level--;
3323     buf.writeByte('}');
3324     buf.writenl();
3325 
3326 }
3327 
3328 private void tiargsToBuffer(TemplateInstance ti, OutBuffer* buf, HdrGenState* hgs)
3329 {
3330     buf.writeByte('!');
3331     if (ti.nest)
3332     {
3333         buf.writestring("(...)");
3334         return;
3335     }
3336     if (!ti.tiargs)
3337     {
3338         buf.writestring("()");
3339         return;
3340     }
3341     if (ti.tiargs.dim == 1)
3342     {
3343         RootObject oarg = (*ti.tiargs)[0];
3344         if (Type t = isType(oarg))
3345         {
3346             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))
3347             {
3348                 buf.writestring(t.toChars());
3349                 return;
3350             }
3351         }
3352         else if (Expression e = isExpression(oarg))
3353         {
3354             if (e.op == TOK.int64 || e.op == TOK.float64 || e.op == TOK.null_ || e.op == TOK.string_ || e.op == TOK.this_)
3355             {
3356                 buf.writestring(e.toChars());
3357                 return;
3358             }
3359         }
3360     }
3361     buf.writeByte('(');
3362     ti.nestUp();
3363     foreach (i, arg; *ti.tiargs)
3364     {
3365         if (i)
3366             buf.writestring(", ");
3367         objectToBuffer(arg, buf, hgs);
3368     }
3369     ti.nestDown();
3370     buf.writeByte(')');
3371 }
3372 
3373 /****************************************
3374  * This makes a 'pretty' version of the template arguments.
3375  * It's analogous to genIdent() which makes a mangled version.
3376  */
3377 private void objectToBuffer(RootObject oarg, OutBuffer* buf, HdrGenState* hgs)
3378 {
3379     //printf("objectToBuffer()\n");
3380     /* The logic of this should match what genIdent() does. The _dynamic_cast()
3381      * function relies on all the pretty strings to be unique for different classes
3382      * See https://issues.dlang.org/show_bug.cgi?id=7375
3383      * Perhaps it would be better to demangle what genIdent() does.
3384      */
3385     if (auto t = isType(oarg))
3386     {
3387         //printf("\tt: %s ty = %d\n", t.toChars(), t.ty);
3388         typeToBuffer(t, null, buf, hgs);
3389     }
3390     else if (auto e = isExpression(oarg))
3391     {
3392         if (e.op == TOK.variable)
3393             e = e.optimize(WANTvalue); // added to fix https://issues.dlang.org/show_bug.cgi?id=7375
3394         expToBuffer(e, PREC.assign, buf, hgs);
3395     }
3396     else if (Dsymbol s = isDsymbol(oarg))
3397     {
3398         const p = s.ident ? s.ident.toChars() : s.toChars();
3399         buf.writestring(p);
3400     }
3401     else if (auto v = isTuple(oarg))
3402     {
3403         auto args = &v.objects;
3404         foreach (i, arg; *args)
3405         {
3406             if (i)
3407                 buf.writestring(", ");
3408             objectToBuffer(arg, buf, hgs);
3409         }
3410     }
3411     else if (auto p = isParameter(oarg))
3412     {
3413         parameterToBuffer(p, buf, hgs);
3414     }
3415     else if (!oarg)
3416     {
3417         buf.writestring("NULL");
3418     }
3419     else
3420     {
3421         debug
3422         {
3423             printf("bad Object = %p\n", oarg);
3424         }
3425         assert(0);
3426     }
3427 }
3428 
3429 
3430 private void visitFuncIdentWithPostfix(TypeFunction t, const char[] ident, OutBuffer* buf, HdrGenState* hgs, bool isStatic)
3431 {
3432     if (t.inuse)
3433     {
3434         t.inuse = 2; // flag error to caller
3435         return;
3436     }
3437     t.inuse++;
3438     if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen)
3439     {
3440         linkageToBuffer(buf, t.linkage);
3441         buf.writeByte(' ');
3442     }
3443     if (t.linkage == LINK.objc && isStatic)
3444         buf.write("static ");
3445     if (t.next)
3446     {
3447         typeToBuffer(t.next, null, buf, hgs);
3448         if (ident)
3449             buf.writeByte(' ');
3450     }
3451     else if (hgs.ddoc)
3452         buf.writestring("auto ");
3453     if (ident)
3454         buf.writestring(ident);
3455     parametersToBuffer(t.parameterList, buf, hgs);
3456     /* Use postfix style for attributes
3457      */
3458     if (t.mod)
3459     {
3460         buf.writeByte(' ');
3461         MODtoBuffer(buf, t.mod);
3462     }
3463 
3464     void dg(string str)
3465     {
3466         buf.writeByte(' ');
3467         buf.writestring(str);
3468     }
3469     t.attributesApply(&dg);
3470 
3471     t.inuse--;
3472 }
3473 
3474 private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, TemplateDeclaration td,
3475     OutBuffer* buf, HdrGenState* hgs)
3476 {
3477     if (t.inuse)
3478     {
3479         t.inuse = 2; // flag error to caller
3480         return;
3481     }
3482     t.inuse++;
3483 
3484     /* Use 'storage class' (prefix) style for attributes
3485      */
3486     if (t.mod)
3487     {
3488         MODtoBuffer(buf, t.mod);
3489         buf.writeByte(' ');
3490     }
3491 
3492     void ignoreReturn(string str)
3493     {
3494         if (str != "return")
3495         {
3496             // don't write 'ref' for ctors
3497             if ((ident == Id.ctor) && str == "ref")
3498                 return;
3499             buf.writestring(str);
3500             buf.writeByte(' ');
3501         }
3502     }
3503     t.attributesApply(&ignoreReturn);
3504 
3505     if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen)
3506     {
3507         linkageToBuffer(buf, t.linkage);
3508         buf.writeByte(' ');
3509     }
3510     if (ident && ident.toHChars2() != ident.toChars())
3511     {
3512         // Don't print return type for ctor, dtor, unittest, etc
3513     }
3514     else if (t.next)
3515     {
3516         typeToBuffer(t.next, null, buf, hgs);
3517         if (ident)
3518             buf.writeByte(' ');
3519     }
3520     else if (hgs.ddoc)
3521         buf.writestring("auto ");
3522     if (ident)
3523         buf.writestring(ident.toHChars2());
3524     if (td)
3525     {
3526         buf.writeByte('(');
3527         foreach (i, p; *td.origParameters)
3528         {
3529             if (i)
3530                 buf.writestring(", ");
3531             p.templateParameterToBuffer(buf, hgs);
3532         }
3533         buf.writeByte(')');
3534     }
3535     parametersToBuffer(t.parameterList, buf, hgs);
3536     if (t.isreturn)
3537     {
3538         buf.writestring(" return");
3539     }
3540     t.inuse--;
3541 }
3542 
3543 
3544 private void initializerToBuffer(Initializer inx, OutBuffer* buf, HdrGenState* hgs)
3545 {
3546     void visitError(ErrorInitializer iz)
3547     {
3548         buf.writestring("__error__");
3549     }
3550 
3551     void visitVoid(VoidInitializer iz)
3552     {
3553         buf.writestring("void");
3554     }
3555 
3556     void visitStruct(StructInitializer si)
3557     {
3558         //printf("StructInitializer::toCBuffer()\n");
3559         buf.writeByte('{');
3560         foreach (i, const id; si.field)
3561         {
3562             if (i)
3563                 buf.writestring(", ");
3564             if (id)
3565             {
3566                 buf.writestring(id.toString());
3567                 buf.writeByte(':');
3568             }
3569             if (auto iz = si.value[i])
3570                 initializerToBuffer(iz, buf, hgs);
3571         }
3572         buf.writeByte('}');
3573     }
3574 
3575     void visitArray(ArrayInitializer ai)
3576     {
3577         buf.writeByte('[');
3578         foreach (i, ex; ai.index)
3579         {
3580             if (i)
3581                 buf.writestring(", ");
3582             if (ex)
3583             {
3584                 ex.expressionToBuffer(buf, hgs);
3585                 buf.writeByte(':');
3586             }
3587             if (auto iz = ai.value[i])
3588                 initializerToBuffer(iz, buf, hgs);
3589         }
3590         buf.writeByte(']');
3591     }
3592 
3593     void visitExp(ExpInitializer ei)
3594     {
3595         ei.exp.expressionToBuffer(buf, hgs);
3596     }
3597 
3598     final switch (inx.kind)
3599     {
3600         case InitKind.error:   return visitError (inx.isErrorInitializer ());
3601         case InitKind.void_:   return visitVoid  (inx.isVoidInitializer  ());
3602         case InitKind.struct_: return visitStruct(inx.isStructInitializer());
3603         case InitKind.array:   return visitArray (inx.isArrayInitializer ());
3604         case InitKind.exp:     return visitExp   (inx.isExpInitializer   ());
3605     }
3606 }
3607 
3608 
3609 private void typeToBufferx(Type t, OutBuffer* buf, HdrGenState* hgs)
3610 {
3611     void visitType(Type t)
3612     {
3613         printf("t = %p, ty = %d\n", t, t.ty);
3614         assert(0);
3615     }
3616 
3617     void visitError(TypeError t)
3618     {
3619         buf.writestring("_error_");
3620     }
3621 
3622     void visitBasic(TypeBasic t)
3623     {
3624         //printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod);
3625         buf.writestring(t.dstring);
3626     }
3627 
3628     void visitTraits(TypeTraits t)
3629     {
3630         //printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod);
3631         t.exp.expressionToBuffer(buf, hgs);
3632     }
3633 
3634     void visitVector(TypeVector t)
3635     {
3636         //printf("TypeVector::toCBuffer2(t.mod = %d)\n", t.mod);
3637         buf.writestring("__vector(");
3638         visitWithMask(t.basetype, t.mod, buf, hgs);
3639         buf.writestring(")");
3640     }
3641 
3642     void visitSArray(TypeSArray t)
3643     {
3644         visitWithMask(t.next, t.mod, buf, hgs);
3645         buf.writeByte('[');
3646         sizeToBuffer(t.dim, buf, hgs);
3647         buf.writeByte(']');
3648     }
3649 
3650     void visitDArray(TypeDArray t)
3651     {
3652         Type ut = t.castMod(0);
3653         if (hgs.declstring)
3654             goto L1;
3655         if (ut.equals(Type.tstring))
3656             buf.writestring("string");
3657         else if (ut.equals(Type.twstring))
3658             buf.writestring("wstring");
3659         else if (ut.equals(Type.tdstring))
3660             buf.writestring("dstring");
3661         else
3662         {
3663         L1:
3664             visitWithMask(t.next, t.mod, buf, hgs);
3665             buf.writestring("[]");
3666         }
3667     }
3668 
3669     void visitAArray(TypeAArray t)
3670     {
3671         visitWithMask(t.next, t.mod, buf, hgs);
3672         buf.writeByte('[');
3673         visitWithMask(t.index, 0, buf, hgs);
3674         buf.writeByte(']');
3675     }
3676 
3677     void visitPointer(TypePointer t)
3678     {
3679         //printf("TypePointer::toCBuffer2() next = %d\n", t.next.ty);
3680         if (t.next.ty == Tfunction)
3681             visitFuncIdentWithPostfix(cast(TypeFunction)t.next, "function", buf, hgs, false);
3682         else
3683         {
3684             visitWithMask(t.next, t.mod, buf, hgs);
3685             buf.writeByte('*');
3686         }
3687     }
3688 
3689     void visitReference(TypeReference t)
3690     {
3691         visitWithMask(t.next, t.mod, buf, hgs);
3692         buf.writeByte('&');
3693     }
3694 
3695     void visitFunction(TypeFunction t)
3696     {
3697         //printf("TypeFunction::toCBuffer2() t = %p, ref = %d\n", t, t.isref);
3698         visitFuncIdentWithPostfix(t, null, buf, hgs, false);
3699     }
3700 
3701     void visitDelegate(TypeDelegate t)
3702     {
3703         visitFuncIdentWithPostfix(cast(TypeFunction)t.next, "delegate", buf, hgs, false);
3704     }
3705 
3706     void visitTypeQualifiedHelper(TypeQualified t)
3707     {
3708         foreach (id; t.idents)
3709         {
3710             if (id.dyncast() == DYNCAST.dsymbol)
3711             {
3712                 buf.writeByte('.');
3713                 TemplateInstance ti = cast(TemplateInstance)id;
3714                 ti.dsymbolToBuffer(buf, hgs);
3715             }
3716             else if (id.dyncast() == DYNCAST.expression)
3717             {
3718                 buf.writeByte('[');
3719                 (cast(Expression)id).expressionToBuffer(buf, hgs);
3720                 buf.writeByte(']');
3721             }
3722             else if (id.dyncast() == DYNCAST.type)
3723             {
3724                 buf.writeByte('[');
3725                 typeToBufferx(cast(Type)id, buf, hgs);
3726                 buf.writeByte(']');
3727             }
3728             else
3729             {
3730                 buf.writeByte('.');
3731                 buf.writestring(id.toString());
3732             }
3733         }
3734     }
3735 
3736     void visitIdentifier(TypeIdentifier t)
3737     {
3738         buf.writestring(t.ident.toString());
3739         visitTypeQualifiedHelper(t);
3740     }
3741 
3742     void visitInstance(TypeInstance t)
3743     {
3744         t.tempinst.dsymbolToBuffer(buf, hgs);
3745         visitTypeQualifiedHelper(t);
3746     }
3747 
3748     void visitTypeof(TypeTypeof t)
3749     {
3750         buf.writestring("typeof(");
3751         t.exp.expressionToBuffer(buf, hgs);
3752         buf.writeByte(')');
3753         visitTypeQualifiedHelper(t);
3754     }
3755 
3756     void visitReturn(TypeReturn t)
3757     {
3758         buf.writestring("typeof(return)");
3759         visitTypeQualifiedHelper(t);
3760     }
3761 
3762     void visitEnum(TypeEnum t)
3763     {
3764         buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
3765     }
3766 
3767     void visitStruct(TypeStruct t)
3768     {
3769         // https://issues.dlang.org/show_bug.cgi?id=13776
3770         // Don't use ti.toAlias() to avoid forward reference error
3771         // while printing messages.
3772         TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null;
3773         if (ti && ti.aliasdecl == t.sym)
3774             buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
3775         else
3776             buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
3777     }
3778 
3779     void visitClass(TypeClass t)
3780     {
3781         // https://issues.dlang.org/show_bug.cgi?id=13776
3782         // Don't use ti.toAlias() to avoid forward reference error
3783         // while printing messages.
3784         TemplateInstance ti = t.sym.parent.isTemplateInstance();
3785         if (ti && ti.aliasdecl == t.sym)
3786             buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
3787         else
3788             buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
3789     }
3790 
3791     void visitTuple(TypeTuple t)
3792     {
3793         parametersToBuffer(ParameterList(t.arguments, VarArg.none), buf, hgs);
3794     }
3795 
3796     void visitSlice(TypeSlice t)
3797     {
3798         visitWithMask(t.next, t.mod, buf, hgs);
3799         buf.writeByte('[');
3800         sizeToBuffer(t.lwr, buf, hgs);
3801         buf.writestring(" .. ");
3802         sizeToBuffer(t.upr, buf, hgs);
3803         buf.writeByte(']');
3804     }
3805 
3806     void visitNull(TypeNull t)
3807     {
3808         buf.writestring("typeof(null)");
3809     }
3810 
3811     void visitMixin(TypeMixin t)
3812     {
3813         buf.writestring("mixin(");
3814         argsToBuffer(t.exps, buf, hgs, null);
3815         buf.writeByte(')');
3816     }
3817 
3818     void visitNoreturn(TypeNoreturn t)
3819     {
3820         buf.writestring("noreturn");
3821     }
3822 
3823 
3824     switch (t.ty)
3825     {
3826         default:        return t.isTypeBasic() ?
3827                                 visitBasic(cast(TypeBasic)t) :
3828                                 visitType(t);
3829 
3830         case Terror:     return visitError(cast(TypeError)t);
3831         case Ttraits:    return visitTraits(cast(TypeTraits)t);
3832         case Tvector:    return visitVector(cast(TypeVector)t);
3833         case Tsarray:    return visitSArray(cast(TypeSArray)t);
3834         case Tarray:     return visitDArray(cast(TypeDArray)t);
3835         case Taarray:    return visitAArray(cast(TypeAArray)t);
3836         case Tpointer:   return visitPointer(cast(TypePointer)t);
3837         case Treference: return visitReference(cast(TypeReference)t);
3838         case Tfunction:  return visitFunction(cast(TypeFunction)t);
3839         case Tdelegate:  return visitDelegate(cast(TypeDelegate)t);
3840         case Tident:     return visitIdentifier(cast(TypeIdentifier)t);
3841         case Tinstance:  return visitInstance(cast(TypeInstance)t);
3842         case Ttypeof:    return visitTypeof(cast(TypeTypeof)t);
3843         case Treturn:    return visitReturn(cast(TypeReturn)t);
3844         case Tenum:      return visitEnum(cast(TypeEnum)t);
3845         case Tstruct:    return visitStruct(cast(TypeStruct)t);
3846         case Tclass:     return visitClass(cast(TypeClass)t);
3847         case Ttuple:     return visitTuple (cast(TypeTuple)t);
3848         case Tslice:     return visitSlice(cast(TypeSlice)t);
3849         case Tnull:      return visitNull(cast(TypeNull)t);
3850         case Tmixin:     return visitMixin(cast(TypeMixin)t);
3851         case Tnoreturn:  return visitNoreturn(cast(TypeNoreturn)t);
3852     }
3853 }