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