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