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