blob: debf01d2a8e643d3a7867acc26818441d0b9747b [file] [log] [blame]
/**
* Generate $(LINK2 https://dlang.org/dmd-windows.html#interface-files, D interface files).
*
* Also used to convert AST nodes to D code in general, e.g. for error messages or `printf` debugging.
*
* Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/hdrgen.d, _hdrgen.d)
* Documentation: https://dlang.org/phobos/dmd_hdrgen.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/hdrgen.d
*/
module dmd.hdrgen;
import core.stdc.ctype;
import core.stdc.stdio;
import core.stdc.string;
import dmd.aggregate;
import dmd.aliasthis;
import dmd.arraytypes;
import dmd.astenums;
import dmd.attrib;
import dmd.cond;
import dmd.ctfeexpr;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
import dmd.dimport;
import dmd.dmodule;
import dmd.doc;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dtemplate;
import dmd.dversion;
import dmd.expression;
import dmd.func;
import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.init;
import dmd.mtype;
import dmd.nspace;
import dmd.parse;
import dmd.root.complex;
import dmd.root.ctfloat;
import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.root.string;
import dmd.statement;
import dmd.staticassert;
import dmd.target;
import dmd.tokens;
import dmd.utils;
import dmd.visitor;
struct HdrGenState
{
bool hdrgen; /// true if generating header file
bool ddoc; /// true if generating Ddoc file
bool fullDump; /// true if generating a full AST dump file
bool fullQual; /// fully qualify types when printing
int tpltMember;
int autoMember;
int forStmtInit;
int insideFuncBody;
bool declstring; // set while declaring alias for string,wstring or dstring
EnumDeclaration inEnumDecl;
}
enum TEST_EMIT_ALL = 0;
extern (C++) void genhdrfile(Module m)
{
OutBuffer buf;
buf.doindent = 1;
buf.printf("// D import file generated from '%s'", m.srcfile.toChars());
buf.writenl();
HdrGenState hgs;
hgs.hdrgen = true;
toCBuffer(m, &buf, &hgs);
writeFile(m.loc, m.hdrfile.toString(), buf[]);
}
/**
* Dumps the full contents of module `m` to `buf`.
* Params:
* buf = buffer to write to.
* m = module to visit all members of.
*/
extern (C++) void moduleToBuffer(OutBuffer* buf, Module m)
{
HdrGenState hgs;
hgs.fullDump = true;
toCBuffer(m, buf, &hgs);
}
void moduleToBuffer2(Module m, OutBuffer* buf, HdrGenState* hgs)
{
if (m.md)
{
if (m.userAttribDecl)
{
buf.writestring("@(");
argsToBuffer(m.userAttribDecl.atts, buf, hgs);
buf.writeByte(')');
buf.writenl();
}
if (m.md.isdeprecated)
{
if (m.md.msg)
{
buf.writestring("deprecated(");
m.md.msg.expressionToBuffer(buf, hgs);
buf.writestring(") ");
}
else
buf.writestring("deprecated ");
}
buf.writestring("module ");
buf.writestring(m.md.toChars());
buf.writeByte(';');
buf.writenl();
}
foreach (s; *m.members)
{
s.dsymbolToBuffer(buf, hgs);
}
}
private void statementToBuffer(Statement s, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new StatementPrettyPrintVisitor(buf, hgs);
s.accept(v);
}
private extern (C++) final class StatementPrettyPrintVisitor : Visitor
{
alias visit = Visitor.visit;
public:
OutBuffer* buf;
HdrGenState* hgs;
extern (D) this(OutBuffer* buf, HdrGenState* hgs)
{
this.buf = buf;
this.hgs = hgs;
}
override void visit(Statement s)
{
buf.writestring("Statement::toCBuffer()");
buf.writenl();
assert(0);
}
override void visit(ErrorStatement s)
{
buf.writestring("__error__");
buf.writenl();
}
override void visit(ExpStatement s)
{
if (s.exp && s.exp.op == EXP.declaration &&
(cast(DeclarationExp)s.exp).declaration)
{
// bypass visit(DeclarationExp)
(cast(DeclarationExp)s.exp).declaration.dsymbolToBuffer(buf, hgs);
return;
}
if (s.exp)
s.exp.expressionToBuffer(buf, hgs);
buf.writeByte(';');
if (!hgs.forStmtInit)
buf.writenl();
}
override void visit(CompileStatement s)
{
buf.writestring("mixin(");
argsToBuffer(s.exps, buf, hgs, null);
buf.writestring(");");
if (!hgs.forStmtInit)
buf.writenl();
}
override void visit(CompoundStatement s)
{
foreach (sx; *s.statements)
{
if (sx)
sx.accept(this);
}
}
override void visit(CompoundDeclarationStatement s)
{
bool anywritten = false;
foreach (sx; *s.statements)
{
auto ds = sx ? sx.isExpStatement() : null;
if (ds && ds.exp.op == EXP.declaration)
{
auto d = (cast(DeclarationExp)ds.exp).declaration;
assert(d.isDeclaration());
if (auto v = d.isVarDeclaration())
{
scope ppv = new DsymbolPrettyPrintVisitor(buf, hgs);
ppv.visitVarDecl(v, anywritten);
}
else
d.dsymbolToBuffer(buf, hgs);
anywritten = true;
}
}
buf.writeByte(';');
if (!hgs.forStmtInit)
buf.writenl();
}
override void visit(UnrolledLoopStatement s)
{
buf.writestring("/*unrolled*/ {");
buf.writenl();
buf.level++;
foreach (sx; *s.statements)
{
if (sx)
sx.accept(this);
}
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(ScopeStatement s)
{
buf.writeByte('{');
buf.writenl();
buf.level++;
if (s.statement)
s.statement.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(WhileStatement s)
{
buf.writestring("while (");
if (auto p = s.param)
{
// Print condition assignment
StorageClass stc = p.storageClass;
if (!p.type && !stc)
stc = STC.auto_;
if (stcToBuffer(buf, stc))
buf.writeByte(' ');
if (p.type)
typeToBuffer(p.type, p.ident, buf, hgs);
else
buf.writestring(p.ident.toString());
buf.writestring(" = ");
}
s.condition.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
if (s._body)
s._body.accept(this);
}
override void visit(DoStatement s)
{
buf.writestring("do");
buf.writenl();
if (s._body)
s._body.accept(this);
buf.writestring("while (");
s.condition.expressionToBuffer(buf, hgs);
buf.writestring(");");
buf.writenl();
}
override void visit(ForStatement s)
{
buf.writestring("for (");
if (s._init)
{
hgs.forStmtInit++;
s._init.accept(this);
hgs.forStmtInit--;
}
else
buf.writeByte(';');
if (s.condition)
{
buf.writeByte(' ');
s.condition.expressionToBuffer(buf, hgs);
}
buf.writeByte(';');
if (s.increment)
{
buf.writeByte(' ');
s.increment.expressionToBuffer(buf, hgs);
}
buf.writeByte(')');
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
if (s._body)
s._body.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
private void foreachWithoutBody(ForeachStatement s)
{
buf.writestring(Token.toString(s.op));
buf.writestring(" (");
foreach (i, p; *s.parameters)
{
if (i)
buf.writestring(", ");
if (stcToBuffer(buf, p.storageClass))
buf.writeByte(' ');
if (p.type)
typeToBuffer(p.type, p.ident, buf, hgs);
else
buf.writestring(p.ident.toString());
}
buf.writestring("; ");
s.aggr.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
}
override void visit(ForeachStatement s)
{
foreachWithoutBody(s);
buf.writeByte('{');
buf.writenl();
buf.level++;
if (s._body)
s._body.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
private void foreachRangeWithoutBody(ForeachRangeStatement s)
{
buf.writestring(Token.toString(s.op));
buf.writestring(" (");
if (s.prm.type)
typeToBuffer(s.prm.type, s.prm.ident, buf, hgs);
else
buf.writestring(s.prm.ident.toString());
buf.writestring("; ");
s.lwr.expressionToBuffer(buf, hgs);
buf.writestring(" .. ");
s.upr.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
}
override void visit(ForeachRangeStatement s)
{
foreachRangeWithoutBody(s);
buf.writeByte('{');
buf.writenl();
buf.level++;
if (s._body)
s._body.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(StaticForeachStatement s)
{
buf.writestring("static ");
if (s.sfe.aggrfe)
{
visit(s.sfe.aggrfe);
}
else
{
assert(s.sfe.rangefe);
visit(s.sfe.rangefe);
}
}
override void visit(ForwardingStatement s)
{
s.statement.accept(this);
}
override void visit(IfStatement s)
{
buf.writestring("if (");
if (Parameter p = s.prm)
{
StorageClass stc = p.storageClass;
if (!p.type && !stc)
stc = STC.auto_;
if (stcToBuffer(buf, stc))
buf.writeByte(' ');
if (p.type)
typeToBuffer(p.type, p.ident, buf, hgs);
else
buf.writestring(p.ident.toString());
buf.writestring(" = ");
}
s.condition.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
if (s.ifbody.isScopeStatement())
{
s.ifbody.accept(this);
}
else
{
buf.level++;
s.ifbody.accept(this);
buf.level--;
}
if (s.elsebody)
{
buf.writestring("else");
if (!s.elsebody.isIfStatement())
{
buf.writenl();
}
else
{
buf.writeByte(' ');
}
if (s.elsebody.isScopeStatement() || s.elsebody.isIfStatement())
{
s.elsebody.accept(this);
}
else
{
buf.level++;
s.elsebody.accept(this);
buf.level--;
}
}
}
override void visit(ConditionalStatement s)
{
s.condition.conditionToBuffer(buf, hgs);
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
if (s.ifbody)
s.ifbody.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
if (s.elsebody)
{
buf.writestring("else");
buf.writenl();
buf.writeByte('{');
buf.level++;
buf.writenl();
s.elsebody.accept(this);
buf.level--;
buf.writeByte('}');
}
buf.writenl();
}
override void visit(PragmaStatement s)
{
buf.writestring("pragma (");
buf.writestring(s.ident.toString());
if (s.args && s.args.dim)
{
buf.writestring(", ");
argsToBuffer(s.args, buf, hgs);
}
buf.writeByte(')');
if (s._body)
{
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
s._body.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
else
{
buf.writeByte(';');
buf.writenl();
}
}
override void visit(StaticAssertStatement s)
{
s.sa.dsymbolToBuffer(buf, hgs);
}
override void visit(SwitchStatement s)
{
buf.writestring(s.isFinal ? "final switch (" : "switch (");
s.condition.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
if (s._body)
{
if (!s._body.isScopeStatement())
{
buf.writeByte('{');
buf.writenl();
buf.level++;
s._body.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
else
{
s._body.accept(this);
}
}
}
override void visit(CaseStatement s)
{
buf.writestring("case ");
s.exp.expressionToBuffer(buf, hgs);
buf.writeByte(':');
buf.writenl();
s.statement.accept(this);
}
override void visit(CaseRangeStatement s)
{
buf.writestring("case ");
s.first.expressionToBuffer(buf, hgs);
buf.writestring(": .. case ");
s.last.expressionToBuffer(buf, hgs);
buf.writeByte(':');
buf.writenl();
s.statement.accept(this);
}
override void visit(DefaultStatement s)
{
buf.writestring("default:");
buf.writenl();
s.statement.accept(this);
}
override void visit(GotoDefaultStatement s)
{
buf.writestring("goto default;");
buf.writenl();
}
override void visit(GotoCaseStatement s)
{
buf.writestring("goto case");
if (s.exp)
{
buf.writeByte(' ');
s.exp.expressionToBuffer(buf, hgs);
}
buf.writeByte(';');
buf.writenl();
}
override void visit(SwitchErrorStatement s)
{
buf.writestring("SwitchErrorStatement::toCBuffer()");
buf.writenl();
}
override void visit(ReturnStatement s)
{
buf.writestring("return ");
if (s.exp)
s.exp.expressionToBuffer(buf, hgs);
buf.writeByte(';');
buf.writenl();
}
override void visit(BreakStatement s)
{
buf.writestring("break");
if (s.ident)
{
buf.writeByte(' ');
buf.writestring(s.ident.toString());
}
buf.writeByte(';');
buf.writenl();
}
override void visit(ContinueStatement s)
{
buf.writestring("continue");
if (s.ident)
{
buf.writeByte(' ');
buf.writestring(s.ident.toString());
}
buf.writeByte(';');
buf.writenl();
}
override void visit(SynchronizedStatement s)
{
buf.writestring("synchronized");
if (s.exp)
{
buf.writeByte('(');
s.exp.expressionToBuffer(buf, hgs);
buf.writeByte(')');
}
if (s._body)
{
buf.writeByte(' ');
s._body.accept(this);
}
}
override void visit(WithStatement s)
{
buf.writestring("with (");
s.exp.expressionToBuffer(buf, hgs);
buf.writestring(")");
buf.writenl();
if (s._body)
s._body.accept(this);
}
override void visit(TryCatchStatement s)
{
buf.writestring("try");
buf.writenl();
if (s._body)
{
if (s._body.isScopeStatement())
{
s._body.accept(this);
}
else
{
buf.level++;
s._body.accept(this);
buf.level--;
}
}
foreach (c; *s.catches)
{
visit(c);
}
}
override void visit(TryFinallyStatement s)
{
buf.writestring("try");
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
s._body.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
buf.writestring("finally");
buf.writenl();
if (s.finalbody.isScopeStatement())
{
s.finalbody.accept(this);
}
else
{
buf.level++;
s.finalbody.accept(this);
buf.level--;
}
}
override void visit(ScopeGuardStatement s)
{
buf.writestring(Token.toString(s.tok));
buf.writeByte(' ');
if (s.statement)
s.statement.accept(this);
}
override void visit(ThrowStatement s)
{
buf.writestring("throw ");
s.exp.expressionToBuffer(buf, hgs);
buf.writeByte(';');
buf.writenl();
}
override void visit(DebugStatement s)
{
if (s.statement)
{
s.statement.accept(this);
}
}
override void visit(GotoStatement s)
{
buf.writestring("goto ");
buf.writestring(s.ident.toString());
buf.writeByte(';');
buf.writenl();
}
override void visit(LabelStatement s)
{
buf.writestring(s.ident.toString());
buf.writeByte(':');
buf.writenl();
if (s.statement)
s.statement.accept(this);
}
override void visit(AsmStatement s)
{
buf.writestring("asm { ");
Token* t = s.tokens;
buf.level++;
while (t)
{
buf.writestring(t.toChars());
if (t.next &&
t.value != TOK.min &&
t.value != TOK.comma && t.next.value != TOK.comma &&
t.value != TOK.leftBracket && t.next.value != TOK.leftBracket &&
t.next.value != TOK.rightBracket &&
t.value != TOK.leftParenthesis && t.next.value != TOK.leftParenthesis &&
t.next.value != TOK.rightParenthesis &&
t.value != TOK.dot && t.next.value != TOK.dot)
{
buf.writeByte(' ');
}
t = t.next;
}
buf.level--;
buf.writestring("; }");
buf.writenl();
}
override void visit(ImportStatement s)
{
foreach (imp; *s.imports)
{
imp.dsymbolToBuffer(buf, hgs);
}
}
void visit(Catch c)
{
buf.writestring("catch");
if (c.type)
{
buf.writeByte('(');
typeToBuffer(c.type, c.ident, buf, hgs);
buf.writeByte(')');
}
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
if (c.handler)
c.handler.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
}
private void dsymbolToBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
s.accept(v);
}
private extern (C++) final class DsymbolPrettyPrintVisitor : Visitor
{
alias visit = Visitor.visit;
public:
OutBuffer* buf;
HdrGenState* hgs;
extern (D) this(OutBuffer* buf, HdrGenState* hgs)
{
this.buf = buf;
this.hgs = hgs;
}
////////////////////////////////////////////////////////////////////////////
override void visit(Dsymbol s)
{
buf.writestring(s.toChars());
}
override void visit(StaticAssert s)
{
buf.writestring(s.kind());
buf.writeByte('(');
s.exp.expressionToBuffer(buf, hgs);
if (s.msg)
{
buf.writestring(", ");
s.msg.expressionToBuffer(buf, hgs);
}
buf.writestring(");");
buf.writenl();
}
override void visit(DebugSymbol s)
{
buf.writestring("debug = ");
if (s.ident)
buf.writestring(s.ident.toString());
else
buf.print(s.level);
buf.writeByte(';');
buf.writenl();
}
override void visit(VersionSymbol s)
{
buf.writestring("version = ");
if (s.ident)
buf.writestring(s.ident.toString());
else
buf.print(s.level);
buf.writeByte(';');
buf.writenl();
}
override void visit(EnumMember em)
{
if (em.type)
typeToBuffer(em.type, em.ident, buf, hgs);
else
buf.writestring(em.ident.toString());
if (em.value)
{
buf.writestring(" = ");
em.value.expressionToBuffer(buf, hgs);
}
}
override void visit(Import imp)
{
if (hgs.hdrgen && imp.id == Id.object)
return; // object is imported by default
if (imp.isstatic)
buf.writestring("static ");
buf.writestring("import ");
if (imp.aliasId)
{
buf.printf("%s = ", imp.aliasId.toChars());
}
foreach (const pid; imp.packages)
{
buf.printf("%s.", pid.toChars());
}
buf.writestring(imp.id.toString());
if (imp.names.dim)
{
buf.writestring(" : ");
foreach (const i, const name; imp.names)
{
if (i)
buf.writestring(", ");
const _alias = imp.aliases[i];
if (_alias)
buf.printf("%s = %s", _alias.toChars(), name.toChars());
else
buf.writestring(name.toChars());
}
}
buf.writeByte(';');
buf.writenl();
}
override void visit(AliasThis d)
{
buf.writestring("alias ");
buf.writestring(d.ident.toString());
buf.writestring(" this;\n");
}
override void visit(AttribDeclaration d)
{
bool hasSTC;
if (auto stcd = d.isStorageClassDeclaration)
{
hasSTC = stcToBuffer(buf, stcd.stc);
}
if (!d.decl)
{
buf.writeByte(';');
buf.writenl();
return;
}
if (d.decl.dim == 0 || (hgs.hdrgen && d.decl.dim == 1 && (*d.decl)[0].isUnitTestDeclaration()))
{
// hack for bugzilla 8081
if (hasSTC) buf.writeByte(' ');
buf.writestring("{}");
}
else if (d.decl.dim == 1)
{
if (hasSTC) buf.writeByte(' ');
(*d.decl)[0].accept(this);
return;
}
else
{
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (de; *d.decl)
de.accept(this);
buf.level--;
buf.writeByte('}');
}
buf.writenl();
}
override void visit(StorageClassDeclaration d)
{
visit(cast(AttribDeclaration)d);
}
override void visit(DeprecatedDeclaration d)
{
buf.writestring("deprecated(");
d.msg.expressionToBuffer(buf, hgs);
buf.writestring(") ");
visit(cast(AttribDeclaration)d);
}
override void visit(LinkDeclaration d)
{
buf.writestring("extern (");
buf.writestring(linkageToString(d.linkage));
buf.writestring(") ");
visit(cast(AttribDeclaration)d);
}
override void visit(CPPMangleDeclaration d)
{
string s;
final switch (d.cppmangle)
{
case CPPMANGLE.asClass:
s = "class";
break;
case CPPMANGLE.asStruct:
s = "struct";
break;
case CPPMANGLE.def:
break;
}
buf.writestring("extern (C++, ");
buf.writestring(s);
buf.writestring(") ");
visit(cast(AttribDeclaration)d);
}
override void visit(VisibilityDeclaration d)
{
visibilityToBuffer(buf, d.visibility);
AttribDeclaration ad = cast(AttribDeclaration)d;
if (ad.decl.dim <= 1)
buf.writeByte(' ');
if (ad.decl.dim == 1 && (*ad.decl)[0].isVisibilityDeclaration)
visit(cast(AttribDeclaration)(*ad.decl)[0]);
else
visit(cast(AttribDeclaration)d);
}
override void visit(AlignDeclaration d)
{
if (d.exps)
{
foreach (i, exp; (*d.exps)[])
{
if (i)
buf.writeByte(' ');
buf.printf("align (%s)", exp.toChars());
}
if (d.decl && d.decl.dim < 2)
buf.writeByte(' ');
}
else
buf.writestring("align ");
visit(d.isAttribDeclaration());
}
override void visit(AnonDeclaration d)
{
buf.writestring(d.isunion ? "union" : "struct");
buf.writenl();
buf.writestring("{");
buf.writenl();
buf.level++;
if (d.decl)
{
foreach (de; *d.decl)
de.accept(this);
}
buf.level--;
buf.writestring("}");
buf.writenl();
}
override void visit(PragmaDeclaration d)
{
buf.writestring("pragma (");
buf.writestring(d.ident.toString());
if (d.args && d.args.dim)
{
buf.writestring(", ");
argsToBuffer(d.args, buf, hgs);
}
buf.writeByte(')');
// https://issues.dlang.org/show_bug.cgi?id=14690
// Unconditionally perform a full output dump
// for `pragma(inline)` declarations.
bool savedFullDump = global.params.dihdr.fullOutput;
if (d.ident == Id.Pinline)
global.params.dihdr.fullOutput = true;
visit(cast(AttribDeclaration)d);
global.params.dihdr.fullOutput = savedFullDump;
}
override void visit(ConditionalDeclaration d)
{
d.condition.conditionToBuffer(buf, hgs);
if (d.decl || d.elsedecl)
{
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
if (d.decl)
{
foreach (de; *d.decl)
de.accept(this);
}
buf.level--;
buf.writeByte('}');
if (d.elsedecl)
{
buf.writenl();
buf.writestring("else");
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (de; *d.elsedecl)
de.accept(this);
buf.level--;
buf.writeByte('}');
}
}
else
buf.writeByte(':');
buf.writenl();
}
override void visit(StaticForeachDeclaration s)
{
void foreachWithoutBody(ForeachStatement s)
{
buf.writestring(Token.toString(s.op));
buf.writestring(" (");
foreach (i, p; *s.parameters)
{
if (i)
buf.writestring(", ");
if (stcToBuffer(buf, p.storageClass))
buf.writeByte(' ');
if (p.type)
typeToBuffer(p.type, p.ident, buf, hgs);
else
buf.writestring(p.ident.toString());
}
buf.writestring("; ");
s.aggr.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
}
void foreachRangeWithoutBody(ForeachRangeStatement s)
{
/* s.op ( prm ; lwr .. upr )
*/
buf.writestring(Token.toString(s.op));
buf.writestring(" (");
if (s.prm.type)
typeToBuffer(s.prm.type, s.prm.ident, buf, hgs);
else
buf.writestring(s.prm.ident.toString());
buf.writestring("; ");
s.lwr.expressionToBuffer(buf, hgs);
buf.writestring(" .. ");
s.upr.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
}
buf.writestring("static ");
if (s.sfe.aggrfe)
{
foreachWithoutBody(s.sfe.aggrfe);
}
else
{
assert(s.sfe.rangefe);
foreachRangeWithoutBody(s.sfe.rangefe);
}
buf.writeByte('{');
buf.writenl();
buf.level++;
visit(cast(AttribDeclaration)s);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(CompileDeclaration d)
{
buf.writestring("mixin(");
argsToBuffer(d.exps, buf, hgs, null);
buf.writestring(");");
buf.writenl();
}
override void visit(UserAttributeDeclaration d)
{
buf.writestring("@(");
argsToBuffer(d.atts, buf, hgs);
buf.writeByte(')');
visit(cast(AttribDeclaration)d);
}
override void visit(TemplateDeclaration d)
{
version (none)
{
// Should handle template functions for doc generation
if (onemember && onemember.isFuncDeclaration())
buf.writestring("foo ");
}
if ((hgs.hdrgen || hgs.fullDump) && visitEponymousMember(d))
return;
if (hgs.ddoc)
buf.writestring(d.kind());
else
buf.writestring("template");
buf.writeByte(' ');
buf.writestring(d.ident.toString());
buf.writeByte('(');
visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
buf.writeByte(')');
visitTemplateConstraint(d.constraint);
if (hgs.hdrgen || hgs.fullDump)
{
hgs.tpltMember++;
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (s; *d.members)
s.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
hgs.tpltMember--;
}
}
bool visitEponymousMember(TemplateDeclaration d)
{
if (!d.members || d.members.dim != 1)
return false;
Dsymbol onemember = (*d.members)[0];
if (onemember.ident != d.ident)
return false;
if (FuncDeclaration fd = onemember.isFuncDeclaration())
{
assert(fd.type);
if (stcToBuffer(buf, fd.storage_class))
buf.writeByte(' ');
functionToBufferFull(cast(TypeFunction)fd.type, buf, d.ident, hgs, d);
visitTemplateConstraint(d.constraint);
hgs.tpltMember++;
bodyToBuffer(fd);
hgs.tpltMember--;
return true;
}
if (AggregateDeclaration ad = onemember.isAggregateDeclaration())
{
buf.writestring(ad.kind());
buf.writeByte(' ');
buf.writestring(ad.ident.toString());
buf.writeByte('(');
visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
buf.writeByte(')');
visitTemplateConstraint(d.constraint);
visitBaseClasses(ad.isClassDeclaration());
hgs.tpltMember++;
if (ad.members)
{
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (s; *ad.members)
s.accept(this);
buf.level--;
buf.writeByte('}');
}
else
buf.writeByte(';');
buf.writenl();
hgs.tpltMember--;
return true;
}
if (VarDeclaration vd = onemember.isVarDeclaration())
{
if (d.constraint)
return false;
if (stcToBuffer(buf, vd.storage_class))
buf.writeByte(' ');
if (vd.type)
typeToBuffer(vd.type, vd.ident, buf, hgs);
else
buf.writestring(vd.ident.toString());
buf.writeByte('(');
visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
buf.writeByte(')');
if (vd._init)
{
buf.writestring(" = ");
ExpInitializer ie = vd._init.isExpInitializer();
if (ie && (ie.exp.op == EXP.construct || ie.exp.op == EXP.blit))
(cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
else
vd._init.initializerToBuffer(buf, hgs);
}
buf.writeByte(';');
buf.writenl();
return true;
}
return false;
}
void visitTemplateParameters(TemplateParameters* parameters)
{
if (!parameters || !parameters.dim)
return;
foreach (i, p; *parameters)
{
if (i)
buf.writestring(", ");
p.templateParameterToBuffer(buf, hgs);
}
}
void visitTemplateConstraint(Expression constraint)
{
if (!constraint)
return;
buf.writestring(" if (");
constraint.expressionToBuffer(buf, hgs);
buf.writeByte(')');
}
override void visit(TemplateInstance ti)
{
buf.writestring(ti.name.toChars());
tiargsToBuffer(ti, buf, hgs);
if (hgs.fullDump)
{
buf.writenl();
dumpTemplateInstance(ti, buf, hgs);
}
}
override void visit(TemplateMixin tm)
{
buf.writestring("mixin ");
typeToBuffer(tm.tqual, null, buf, hgs);
tiargsToBuffer(tm, buf, hgs);
if (tm.ident && memcmp(tm.ident.toChars(), cast(const(char)*)"__mixin", 7) != 0)
{
buf.writeByte(' ');
buf.writestring(tm.ident.toString());
}
buf.writeByte(';');
buf.writenl();
if (hgs.fullDump)
dumpTemplateInstance(tm, buf, hgs);
}
override void visit(EnumDeclaration d)
{
auto oldInEnumDecl = hgs.inEnumDecl;
scope(exit) hgs.inEnumDecl = oldInEnumDecl;
hgs.inEnumDecl = d;
buf.writestring("enum ");
if (d.ident)
{
buf.writestring(d.ident.toString());
}
if (d.memtype)
{
buf.writestring(" : ");
typeToBuffer(d.memtype, null, buf, hgs);
}
if (!d.members)
{
buf.writeByte(';');
buf.writenl();
return;
}
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (em; *d.members)
{
if (!em)
continue;
em.accept(this);
buf.writeByte(',');
buf.writenl();
}
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(Nspace d)
{
buf.writestring("extern (C++, ");
buf.writestring(d.ident.toString());
buf.writeByte(')');
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (s; *d.members)
s.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(StructDeclaration d)
{
buf.writestring(d.kind());
buf.writeByte(' ');
if (!d.isAnonymous())
buf.writestring(d.toChars());
if (!d.members)
{
buf.writeByte(';');
buf.writenl();
return;
}
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (s; *d.members)
s.accept(this);
buf.level--;
buf.writeByte('}');
buf.writenl();
}
override void visit(ClassDeclaration d)
{
if (!d.isAnonymous())
{
buf.writestring(d.kind());
buf.writeByte(' ');
buf.writestring(d.ident.toString());
}
visitBaseClasses(d);
if (d.members)
{
buf.writenl();
buf.writeByte('{');
buf.writenl();
buf.level++;
foreach (s; *d.members)
s.accept(this);
buf.level--;
buf.writeByte('}');
}
else
buf.writeByte(';');
buf.writenl();
}
void visitBaseClasses(ClassDeclaration d)
{
if (!d || !d.baseclasses.dim)
return;
if (!d.isAnonymous())
buf.writestring(" : ");
foreach (i, b; *d.baseclasses)
{
if (i)
buf.writestring(", ");
typeToBuffer(b.type, null, buf, hgs);
}
}
override void visit(AliasDeclaration d)
{
if (d.storage_class & STC.local)
return;
buf.writestring("alias ");
if (d.aliassym)
{
buf.writestring(d.ident.toString());
buf.writestring(" = ");
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
/*
https://issues.dlang.org/show_bug.cgi?id=23223
https://issues.dlang.org/show_bug.cgi?id=23222
This special case (initially just for modules) avoids some segfaults
and nicer -vcg-ast output.
*/
if (d.aliassym.isModule())
{
buf.writestring(d.aliassym.ident.toString());
}
else
{
d.aliassym.accept(this);
}
}
else if (d.type.ty == Tfunction)
{
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
typeToBuffer(d.type, d.ident, buf, hgs);
}
else if (d.ident)
{
hgs.declstring = (d.ident == Id.string || d.ident == Id.wstring || d.ident == Id.dstring);
buf.writestring(d.ident.toString());
buf.writestring(" = ");
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
typeToBuffer(d.type, null, buf, hgs);
hgs.declstring = false;
}
buf.writeByte(';');
buf.writenl();
}
override void visit(AliasAssign d)
{
buf.writestring(d.ident.toString());
buf.writestring(" = ");
if (d.aliassym)
d.aliassym.accept(this);
else // d.type
typeToBuffer(d.type, null, buf, hgs);
buf.writeByte(';');
buf.writenl();
}
override void visit(VarDeclaration d)
{
if (d.storage_class & STC.local)
return;
visitVarDecl(d, false);
buf.writeByte(';');
buf.writenl();
}
void visitVarDecl(VarDeclaration v, bool anywritten)
{
if (anywritten)
{
buf.writestring(", ");
buf.writestring(v.ident.toString());
}
else
{
if (stcToBuffer(buf, v.storage_class))
buf.writeByte(' ');
if (v.type)
typeToBuffer(v.type, v.ident, buf, hgs);
else
buf.writestring(v.ident.toString());
}
if (v._init)
{
buf.writestring(" = ");
auto ie = v._init.isExpInitializer();
if (ie && (ie.exp.op == EXP.construct || ie.exp.op == EXP.blit))
(cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
else
v._init.initializerToBuffer(buf, hgs);
}
}
override void visit(FuncDeclaration f)
{
//printf("FuncDeclaration::toCBuffer() '%s'\n", f.toChars());
if (stcToBuffer(buf, f.storage_class))
buf.writeByte(' ');
auto tf = cast(TypeFunction)f.type;
typeToBuffer(tf, f.ident, buf, hgs);
if (hgs.hdrgen)
{
// if the return type is missing (e.g. ref functions or auto)
if (!tf.next || f.storage_class & STC.auto_)
{
hgs.autoMember++;
bodyToBuffer(f);
hgs.autoMember--;
}
else if (hgs.tpltMember == 0 && global.params.dihdr.fullOutput == false && !hgs.insideFuncBody)
{
if (!f.fbody)
{
// this can happen on interfaces / abstract functions, see `allowsContractWithoutBody`
if (f.fensures || f.frequires)
buf.writenl();
contractsToBuffer(f);
}
buf.writeByte(';');
buf.writenl();
}
else
bodyToBuffer(f);
}
else
bodyToBuffer(f);
}
/// Returns: whether `do` is needed to write the function body
bool contractsToBuffer(FuncDeclaration f)
{
bool requireDo = false;
// in{}
if (f.frequires)
{
foreach (frequire; *f.frequires)
{
buf.writestring("in");
if (auto es = frequire.isExpStatement())
{
assert(es.exp && es.exp.op == EXP.assert_);
buf.writestring(" (");
(cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
requireDo = false;
}
else
{
buf.writenl();
frequire.statementToBuffer(buf, hgs);
requireDo = true;
}
}
}
// out{}
if (f.fensures)
{
foreach (fensure; *f.fensures)
{
buf.writestring("out");
if (auto es = fensure.ensure.isExpStatement())
{
assert(es.exp && es.exp.op == EXP.assert_);
buf.writestring(" (");
if (fensure.id)
{
buf.writestring(fensure.id.toString());
}
buf.writestring("; ");
(cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
buf.writeByte(')');
buf.writenl();
requireDo = false;
}
else
{
if (fensure.id)
{
buf.writeByte('(');
buf.writestring(fensure.id.toString());
buf.writeByte(')');
}
buf.writenl();
fensure.ensure.statementToBuffer(buf, hgs);
requireDo = true;
}
}
}
return requireDo;
}
void bodyToBuffer(FuncDeclaration f)
{
if (!f.fbody || (hgs.hdrgen && global.params.dihdr.fullOutput == false && !hgs.autoMember && !hgs.tpltMember && !hgs.insideFuncBody))
{
if (!f.fbody && (f.fensures || f.frequires))
{
buf.writenl();
contractsToBuffer(f);
}
buf.writeByte(';');
buf.writenl();
return;
}
// there is no way to know if a function is nested
// or not after parsing. We need scope information
// for that, which is avaible during semantic
// analysis. To overcome that, a simple mechanism
// is implemented: everytime we print a function
// body (templated or not) we increment a counter.
// We decredement the counter when we stop
// printing the function body.
++hgs.insideFuncBody;
scope(exit) { --hgs.insideFuncBody; }
const savetlpt = hgs.tpltMember;
const saveauto = hgs.autoMember;
hgs.tpltMember = 0;
hgs.autoMember = 0;
buf.writenl();
bool requireDo = contractsToBuffer(f);
if (requireDo)
{
buf.writestring("do");
buf.writenl();
}
buf.writeByte('{');
buf.writenl();
buf.level++;
f.fbody.statementToBuffer(buf, hgs);
buf.level--;
buf.writeByte('}');
buf.writenl();
hgs.tpltMember = savetlpt;
hgs.autoMember = saveauto;
}
override void visit(FuncLiteralDeclaration f)
{
if (f.type.ty == Terror)
{
buf.writestring("__error");
return;
}
if (f.tok != TOK.reserved)
{
buf.writestring(f.kind());
buf.writeByte(' ');
}
TypeFunction tf = cast(TypeFunction)f.type;
if (!f.inferRetType && tf.next)
typeToBuffer(tf.next, null, buf, hgs);
parametersToBuffer(tf.parameterList, buf, hgs);
// https://issues.dlang.org/show_bug.cgi?id=20074
void printAttribute(string str)
{
buf.writeByte(' ');
buf.writestring(str);
}
tf.attributesApply(&printAttribute);
CompoundStatement cs = f.fbody.isCompoundStatement();
Statement s1;
if (f.semanticRun >= PASS.semantic3done && cs)
{
s1 = (*cs.statements)[cs.statements.dim - 1];
}
else
s1 = !cs ? f.fbody : null;
ReturnStatement rs = s1 ? s1.endsWithReturnStatement() : null;
if (rs && rs.exp)
{
buf.writestring(" => ");
rs.exp.expressionToBuffer(buf, hgs);
}
else
{
hgs.tpltMember++;
bodyToBuffer(f);
hgs.tpltMember--;
}
}
override void visit(PostBlitDeclaration d)
{
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
buf.writestring("this(this)");
bodyToBuffer(d);
}
override void visit(DtorDeclaration d)
{
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
buf.writestring("~this()");
bodyToBuffer(d);
}
override void visit(StaticCtorDeclaration d)
{
if (stcToBuffer(buf, d.storage_class & ~STC.static_))
buf.writeByte(' ');
if (d.isSharedStaticCtorDeclaration())
buf.writestring("shared ");
buf.writestring("static this()");
if (hgs.hdrgen && !hgs.tpltMember)
{
buf.writeByte(';');
buf.writenl();
}
else
bodyToBuffer(d);
}
override void visit(StaticDtorDeclaration d)
{
if (stcToBuffer(buf, d.storage_class & ~STC.static_))
buf.writeByte(' ');
if (d.isSharedStaticDtorDeclaration())
buf.writestring("shared ");
buf.writestring("static ~this()");
if (hgs.hdrgen && !hgs.tpltMember)
{
buf.writeByte(';');
buf.writenl();
}
else
bodyToBuffer(d);
}
override void visit(InvariantDeclaration d)
{
if (hgs.hdrgen)
return;
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
buf.writestring("invariant");
if(auto es = d.fbody.isExpStatement())
{
assert(es.exp && es.exp.op == EXP.assert_);
buf.writestring(" (");
(cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
buf.writestring(");");
buf.writenl();
}
else
{
bodyToBuffer(d);
}
}
override void visit(UnitTestDeclaration d)
{
if (hgs.hdrgen)
return;
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
buf.writestring("unittest");
bodyToBuffer(d);
}
override void visit(BitFieldDeclaration d)
{
if (stcToBuffer(buf, d.storage_class))
buf.writeByte(' ');
Identifier id = d.isAnonymous() ? null : d.ident;
typeToBuffer(d.type, id, buf, hgs);
buf.writestring(" : ");
d.width.expressionToBuffer(buf, hgs);
buf.writeByte(';');
buf.writenl();
}
override void visit(NewDeclaration d)
{
if (stcToBuffer(buf, d.storage_class & ~STC.static_))
buf.writeByte(' ');
buf.writestring("new();");
}
override void visit(Module m)
{
moduleToBuffer2(m, buf, hgs);
}
}
/*********************************************
* Print expression to buffer.
*/
private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hgs)
{
void visit(Expression e)
{
buf.writestring(EXPtoString(e.op));
}
void visitInteger(IntegerExp e)
{
const dinteger_t v = e.toInteger();
if (e.type)
{
Type t = e.type;
L1:
switch (t.ty)
{
case Tenum:
{
TypeEnum te = cast(TypeEnum)t;
auto sym = te.sym;
if (sym && sym.members && (!hgs.inEnumDecl || hgs.inEnumDecl != sym))
{
foreach (em; *sym.members)
{
if ((cast(EnumMember)em).value.toInteger == v)
{
buf.printf("%s.%s", sym.toChars(), em.ident.toChars());
return ;
}
}
}
buf.printf("cast(%s)", te.sym.toChars());
t = te.sym.memtype;
goto L1;
}
case Tchar:
case Twchar:
case Tdchar:
{
const o = buf.length;
writeSingleCharLiteral(*buf, cast(dchar) v);
if (hgs.ddoc)
escapeDdocString(buf, o);
break;
}
case Tint8:
buf.writestring("cast(byte)");
goto L2;
case Tint16:
buf.writestring("cast(short)");
goto L2;
case Tint32:
L2:
buf.printf("%d", cast(int)v);
break;
case Tuns8:
buf.writestring("cast(ubyte)");
goto case Tuns32;
case Tuns16:
buf.writestring("cast(ushort)");
goto case Tuns32;
case Tuns32:
buf.printf("%uu", cast(uint)v);
break;
case Tint64:
if (v == long.min)
{
// https://issues.dlang.org/show_bug.cgi?id=23173
// This is a special case because - is not part of the
// integer literal and 9223372036854775808L overflows a long
buf.writestring("cast(long)-9223372036854775808");
}
else
{
buf.printf("%lldL", v);
}
break;
case Tuns64:
buf.printf("%lluLU", v);
break;
case Tbool:
buf.writestring(v ? "true" : "false");
break;
case Tpointer:
buf.writestring("cast(");
buf.writestring(t.toChars());
buf.writeByte(')');
if (target.ptrsize == 8)
goto case Tuns64;
else if (target.ptrsize == 4 ||
target.ptrsize == 2)
goto case Tuns32;
else
assert(0);
case Tvoid:
buf.writestring("cast(void)0");
break;
default:
/* This can happen if errors, such as
* the type is painted on like in fromConstInitializer().
*/
if (!global.errors)
{
assert(0);
}
break;
}
}
else if (v & 0x8000000000000000L)
buf.printf("0x%llx", v);
else
buf.print(v);
}
void visitError(ErrorExp e)
{
buf.writestring("__error");
}
void visitVoidInit(VoidInitExp e)
{
buf.writestring("__void");
}
void floatToBuffer(Type type, real_t value)
{
.floatToBuffer(type, value, buf, hgs.hdrgen);
}
void visitReal(RealExp e)
{
floatToBuffer(e.type, e.value);
}
void visitComplex(ComplexExp e)
{
/* Print as:
* (re+imi)
*/
buf.writeByte('(');
floatToBuffer(e.type, creall(e.value));
buf.writeByte('+');
floatToBuffer(e.type, cimagl(e.value));
buf.writestring("i)");
}
void visitIdentifier(IdentifierExp e)
{
if (hgs.hdrgen || hgs.ddoc)
buf.writestring(e.ident.toHChars2());
else
buf.writestring(e.ident.toString());
}
void visitDsymbol(DsymbolExp e)
{
buf.writestring(e.s.toChars());
}
void visitThis(ThisExp e)
{
buf.writestring("this");
}
void visitSuper(SuperExp e)
{
buf.writestring("super");
}
void visitNull(NullExp e)
{
buf.writestring("null");
}
void visitString(StringExp e)
{
buf.writeByte('"');
const o = buf.length;
foreach (i; 0 .. e.len)
{
writeCharLiteral(*buf, e.getCodeUnit(i));
}
if (hgs.ddoc)
escapeDdocString(buf, o);
buf.writeByte('"');
if (e.postfix)
buf.writeByte(e.postfix);
}
void visitArrayLiteral(ArrayLiteralExp e)
{
buf.writeByte('[');
argsToBuffer(e.elements, buf, hgs, e.basis);
buf.writeByte(']');
}
void visitAssocArrayLiteral(AssocArrayLiteralExp e)
{
buf.writeByte('[');
foreach (i, key; *e.keys)
{
if (i)
buf.writestring(", ");
expToBuffer(key, PREC.assign, buf, hgs);
buf.writeByte(':');
auto value = (*e.values)[i];
expToBuffer(value, PREC.assign, buf, hgs);
}
buf.writeByte(']');
}
void visitStructLiteral(StructLiteralExp e)
{
buf.writestring(e.sd.toChars());
buf.writeByte('(');
// CTFE can generate struct literals that contain an AddrExp pointing
// to themselves, need to avoid infinite recursion:
// struct S { this(int){ this.s = &this; } S* s; }
// const foo = new S(0);
if (e.stageflags & stageToCBuffer)
buf.writestring("<recursion>");
else
{
const old = e.stageflags;
e.stageflags |= stageToCBuffer;
argsToBuffer(e.elements, buf, hgs);
e.stageflags = old;
}
buf.writeByte(')');
}
void visitCompoundLiteral(CompoundLiteralExp e)
{
buf.writeByte('(');
typeToBuffer(e.type, null, buf, hgs);
buf.writeByte(')');
e.initializer.initializerToBuffer(buf, hgs);
}
void visitType(TypeExp e)
{
typeToBuffer(e.type, null, buf, hgs);
}
void visitScope(ScopeExp e)
{
if (e.sds.isTemplateInstance())
{
e.sds.dsymbolToBuffer(buf, hgs);
}
else if (hgs !is null && hgs.ddoc)
{
// fixes bug 6491
if (auto m = e.sds.isModule())
buf.writestring(m.md.toChars());
else
buf.writestring(e.sds.toChars());
}
else
{
buf.writestring(e.sds.kind());
buf.writeByte(' ');
buf.writestring(e.sds.toChars());
}
}
void visitTemplate(TemplateExp e)
{
buf.writestring(e.td.toChars());
}
void visitNew(NewExp e)
{
if (e.thisexp)
{
expToBuffer(e.thisexp, PREC.primary, buf, hgs);
buf.writeByte('.');
}
buf.writestring("new ");
typeToBuffer(e.newtype, null, buf, hgs);
if (e.arguments && e.arguments.dim)
{
buf.writeByte('(');
argsToBuffer(e.arguments, buf, hgs);
buf.writeByte(')');
}
}
void visitNewAnonClass(NewAnonClassExp e)
{
if (e.thisexp)
{
expToBuffer(e.thisexp, PREC.primary, buf, hgs);
buf.writeByte('.');
}
buf.writestring("new");
buf.writestring(" class ");
if (e.arguments && e.arguments.dim)
{
buf.writeByte('(');
argsToBuffer(e.arguments, buf, hgs);
buf.writeByte(')');
}
if (e.cd)
e.cd.dsymbolToBuffer(buf, hgs);
}
void visitSymOff(SymOffExp e)
{
if (e.offset)
buf.printf("(& %s%+lld)", e.var.toChars(), e.offset);
else if (e.var.isTypeInfoDeclaration())
buf.writestring(e.var.toChars());
else
buf.printf("& %s", e.var.toChars());
}
void visitVar(VarExp e)
{
buf.writestring(e.var.toChars());
}
void visitOver(OverExp e)
{
buf.writestring(e.vars.ident.toString());
}
void visitTuple(TupleExp e)
{
if (e.e0)
{
buf.writeByte('(');
e.e0.expressionPrettyPrint(buf, hgs);
buf.writestring(", tuple(");
argsToBuffer(e.exps, buf, hgs);
buf.writestring("))");
}
else
{
buf.writestring("tuple(");
argsToBuffer(e.exps, buf, hgs);
buf.writeByte(')');
}
}
void visitFunc(FuncExp e)
{
e.fd.dsymbolToBuffer(buf, hgs);
//buf.writestring(e.fd.toChars());
}
void visitDeclaration(DeclarationExp e)
{
/* Normal dmd execution won't reach here - regular variable declarations
* are handled in visit(ExpStatement), so here would be used only when
* we'll directly call Expression.toChars() for debugging.
*/
if (e.declaration)
{
if (auto var = e.declaration.isVarDeclaration())
{
// For debugging use:
// - Avoid printing newline.
// - Intentionally use the format (Type var;)
// which isn't correct as regular D code.
buf.writeByte('(');
scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
v.visitVarDecl(var, false);
buf.writeByte(';');
buf.writeByte(')');
}
else e.declaration.dsymbolToBuffer(buf, hgs);
}
}
void visitTypeid(TypeidExp e)
{
buf.writestring("typeid(");
objectToBuffer(e.obj, buf, hgs);
buf.writeByte(')');
}
void visitTraits(TraitsExp e)
{
buf.writestring("__traits(");
if (e.ident)
buf.writestring(e.ident.toString());
if (e.args)
{
foreach (arg; *e.args)
{
buf.writestring(", ");
objectToBuffer(arg, buf, hgs);
}
}
buf.writeByte(')');
}
void visitHalt(HaltExp e)
{
buf.writestring("halt");
}
void visitIs(IsExp e)
{
buf.writestring("is(");
typeToBuffer(e.targ, e.id, buf, hgs);
if (e.tok2 != TOK.reserved)
{
buf.printf(" %s %s", Token.toChars(e.tok), Token.toChars(e.tok2));
}
else if (e.tspec)
{
if (e.tok == TOK.colon)
buf.writestring(" : ");
else
buf.writestring(" == ");
typeToBuffer(e.tspec, null, buf, hgs);
}
if (e.parameters && e.parameters.dim)
{
buf.writestring(", ");
scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
v.visitTemplateParameters(e.parameters);
}
buf.writeByte(')');
}
void visitUna(UnaExp e)
{
buf.writestring(EXPtoString(e.op));
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
void visitBin(BinExp e)
{
expToBuffer(e.e1, precedence[e.op], buf, hgs);
buf.writeByte(' ');
buf.writestring(EXPtoString(e.op));
buf.writeByte(' ');
expToBuffer(e.e2, cast(PREC)(precedence[e.op] + 1), buf, hgs);
}
void visitComma(CommaExp e)
{
// CommaExp is generated by the compiler so it shouldn't
// appear in error messages or header files.
// For now, this treats the case where the compiler
// generates CommaExp for temporaries by calling
// the `sideeffect.copyToTemp` function.
auto ve = e.e2.isVarExp();
// not a CommaExp introduced for temporaries, go on
// the old path
if (!ve || !(ve.var.storage_class & STC.temp))
{
visitBin(cast(BinExp)e);
return;
}
// CommaExp that contain temporaries inserted via
// `copyToTemp` are usually of the form
// ((T __temp = exp), __tmp).
// Asserts are here to easily spot
// missing cases where CommaExp
// are used for other constructs
auto vd = ve.var.isVarDeclaration();
assert(vd && vd._init);
if (auto ei = vd._init.isExpInitializer())
{
Expression commaExtract;
auto exp = ei.exp;
if (auto ce = exp.isConstructExp())
commaExtract = ce.e2;
else if (auto se = exp.isStructLiteralExp())
commaExtract = se;
if (commaExtract)
{
expToBuffer(commaExtract, precedence[exp.op], buf, hgs);
return;
}
}
// not one of the known cases, go on the old path
visitBin(cast(BinExp)e);
return;
}
void visitMixin(MixinExp e)
{
buf.writestring("mixin(");
argsToBuffer(e.exps, buf, hgs, null);
buf.writeByte(')');
}
void visitImport(ImportExp e)
{
buf.writestring("import(");
expToBuffer(e.e1, PREC.assign, buf, hgs);
buf.writeByte(')');
}
void visitAssert(AssertExp e)
{
buf.writestring("assert(");
expToBuffer(e.e1, PREC.assign, buf, hgs);
if (e.msg)
{
buf.writestring(", ");
expToBuffer(e.msg, PREC.assign, buf, hgs);
}
buf.writeByte(')');
}
void visitThrow(ThrowExp e)
{
buf.writestring("throw ");
expToBuffer(e.e1, PREC.unary, buf, hgs);
}
void visitDotId(DotIdExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
if (e.arrow)
buf.writestring("->");
else
buf.writeByte('.');
buf.writestring(e.ident.toString());
}
void visitDotTemplate(DotTemplateExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('.');
buf.writestring(e.td.toChars());
}
void visitDotVar(DotVarExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('.');
buf.writestring(e.var.toChars());
}
void visitDotTemplateInstance(DotTemplateInstanceExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('.');
e.ti.dsymbolToBuffer(buf, hgs);
}
void visitDelegate(DelegateExp e)
{
buf.writeByte('&');
if (!e.func.isNested() || e.func.needThis())
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('.');
}
buf.writestring(e.func.toChars());
}
void visitDotType(DotTypeExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('.');
buf.writestring(e.sym.toChars());
}
void visitCall(CallExp e)
{
if (e.e1.op == EXP.type)
{
/* Avoid parens around type to prevent forbidden cast syntax:
* (sometype)(arg1)
* This is ok since types in constructor calls
* can never depend on parens anyway
*/
e.e1.expressionPrettyPrint(buf, hgs);
}
else
expToBuffer(e.e1, precedence[e.op], buf, hgs);
buf.writeByte('(');
argsToBuffer(e.arguments, buf, hgs);
buf.writeByte(')');
}
void visitPtr(PtrExp e)
{
buf.writeByte('*');
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
void visitDelete(DeleteExp e)
{
buf.writestring("delete ");
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
void visitCast(CastExp e)
{
buf.writestring("cast(");
if (e.to)
typeToBuffer(e.to, null, buf, hgs);
else
{
MODtoBuffer(buf, e.mod);
}
buf.writeByte(')');
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
void visitVector(VectorExp e)
{
buf.writestring("cast(");
typeToBuffer(e.to, null, buf, hgs);
buf.writeByte(')');
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
void visitVectorArray(VectorArrayExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writestring(".array");
}
void visitSlice(SliceExp e)
{
expToBuffer(e.e1, precedence[e.op], buf, hgs);
buf.writeByte('[');
if (e.upr || e.lwr)
{
if (e.lwr)
sizeToBuffer(e.lwr, buf, hgs);
else
buf.writeByte('0');
buf.writestring("..");
if (e.upr)
sizeToBuffer(e.upr, buf, hgs);
else
buf.writeByte('$');
}
buf.writeByte(']');
}
void visitArrayLength(ArrayLengthExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writestring(".length");
}
void visitInterval(IntervalExp e)
{
expToBuffer(e.lwr, PREC.assign, buf, hgs);
buf.writestring("..");
expToBuffer(e.upr, PREC.assign, buf, hgs);
}
void visitDelegatePtr(DelegatePtrExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writestring(".ptr");
}
void visitDelegateFuncptr(DelegateFuncptrExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writestring(".funcptr");
}
void visitArray(ArrayExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('[');
argsToBuffer(e.arguments, buf, hgs);
buf.writeByte(']');
}
void visitDot(DotExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('.');
expToBuffer(e.e2, PREC.primary, buf, hgs);
}
void visitIndex(IndexExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writeByte('[');
sizeToBuffer(e.e2, buf, hgs);
buf.writeByte(']');
}
void visitPost(PostExp e)
{
expToBuffer(e.e1, precedence[e.op], buf, hgs);
buf.writestring(EXPtoString(e.op));
}
void visitPre(PreExp e)
{
buf.writestring(EXPtoString(e.op));
expToBuffer(e.e1, precedence[e.op], buf, hgs);
}
void visitRemove(RemoveExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
buf.writestring(".remove(");
expToBuffer(e.e2, PREC.assign, buf, hgs);
buf.writeByte(')');
}
void visitCond(CondExp e)
{
expToBuffer(e.econd, PREC.oror, buf, hgs);
buf.writestring(" ? ");
expToBuffer(e.e1, PREC.expr, buf, hgs);
buf.writestring(" : ");
expToBuffer(e.e2, PREC.cond, buf, hgs);
}
void visitDefaultInit(DefaultInitExp e)
{
buf.writestring(EXPtoString(e.op));
}
void visitClassReference(ClassReferenceExp e)
{
buf.writestring(e.value.toChars());
}
switch (e.op)
{
default:
if (auto be = e.isBinExp())
return visitBin(be);
else if (auto ue = e.isUnaExp())
return visitUna(ue);
else if (auto de = e.isDefaultInitExp())
return visitDefaultInit(e.isDefaultInitExp());
return visit(e);
case EXP.int64: return visitInteger(e.isIntegerExp());
case EXP.error: return visitError(e.isErrorExp());
case EXP.void_: return visitVoidInit(e.isVoidInitExp());
case EXP.float64: return visitReal(e.isRealExp());
case EXP.complex80: return visitComplex(e.isComplexExp());
case EXP.identifier: return visitIdentifier(e.isIdentifierExp());
case EXP.dSymbol: return visitDsymbol(e.isDsymbolExp());
case EXP.this_: return visitThis(e.isThisExp());
case EXP.super_: return visitSuper(e.isSuperExp());
case EXP.null_: return visitNull(e.isNullExp());
case EXP.string_: return visitString(e.isStringExp());
case EXP.arrayLiteral: return visitArrayLiteral(e.isArrayLiteralExp());
case EXP.assocArrayLiteral: return visitAssocArrayLiteral(e.isAssocArrayLiteralExp());
case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp());
case EXP.compoundLiteral: return visitCompoundLiteral(e.isCompoundLiteralExp());
case EXP.type: return visitType(e.isTypeExp());
case EXP.scope_: return visitScope(e.isScopeExp());
case EXP.template_: return visitTemplate(e.isTemplateExp());
case EXP.new_: return visitNew(e.isNewExp());
case EXP.newAnonymousClass: return visitNewAnonClass(e.isNewAnonClassExp());
case EXP.symbolOffset: return visitSymOff(e.isSymOffExp());
case EXP.variable: return visitVar(e.isVarExp());
case EXP.overloadSet: return visitOver(e.isOverExp());
case EXP.tuple: return visitTuple(e.isTupleExp());
case EXP.function_: return visitFunc(e.isFuncExp());
case EXP.declaration: return visitDeclaration(e.isDeclarationExp());
case EXP.typeid_: return visitTypeid(e.isTypeidExp());
case EXP.traits: return visitTraits(e.isTraitsExp());
case EXP.halt: return visitHalt(e.isHaltExp());
case EXP.is_: return visitIs(e.isExp());
case EXP.comma: return visitComma(e.isCommaExp());
case EXP.mixin_: return visitMixin(e.isMixinExp());
case EXP.import_: return visitImport(e.isImportExp());
case EXP.assert_: return visitAssert(e.isAssertExp());
case EXP.throw_: return visitThrow(e.isThrowExp());
case EXP.dotIdentifier: return visitDotId(e.isDotIdExp());
case EXP.dotTemplateDeclaration: return visitDotTemplate(e.isDotTemplateExp());
case EXP.dotVariable: return visitDotVar(e.isDotVarExp());
case EXP.dotTemplateInstance: return visitDotTemplateInstance(e.isDotTemplateInstanceExp());
case EXP.delegate_: return visitDelegate(e.isDelegateExp());
case EXP.dotType: return visitDotType(e.isDotTypeExp());
case EXP.call: return visitCall(e.isCallExp());
case EXP.star: return visitPtr(e.isPtrExp());
case EXP.delete_: return visitDelete(e.isDeleteExp());
case EXP.cast_: return visitCast(e.isCastExp());
case EXP.vector: return visitVector(e.isVectorExp());
case EXP.vectorArray: return visitVectorArray(e.isVectorArrayExp());
case EXP.slice: return visitSlice(e.isSliceExp());
case EXP.arrayLength: return visitArrayLength(e.isArrayLengthExp());
case EXP.interval: return visitInterval(e.isIntervalExp());
case EXP.delegatePointer: return visitDelegatePtr(e.isDelegatePtrExp());
case EXP.delegateFunctionPointer: return visitDelegateFuncptr(e.isDelegateFuncptrExp());
case EXP.array: return visitArray(e.isArrayExp());
case EXP.dot: return visitDot(e.isDotExp());
case EXP.index: return visitIndex(e.isIndexExp());
case EXP.minusMinus:
case EXP.plusPlus: return visitPost(e.isPostExp());
case EXP.preMinusMinus:
case EXP.prePlusPlus: return visitPre(e.isPreExp());
case EXP.remove: return visitRemove(e.isRemoveExp());
case EXP.question: return visitCond(e.isCondExp());
case EXP.classReference: return visitClassReference(e.isClassReferenceExp());
}
}
/**
* Formats `value` as a literal of type `type` into `buf`.
*
* Params:
* type = literal type (e.g. Tfloat)
* value = value to print
* buf = target buffer
* allowHex = whether hex floating point literals may be used
* for greater accuracy
*/
void floatToBuffer(Type type, const real_t value, OutBuffer* buf, const bool allowHex)
{
/** sizeof(value)*3 is because each byte of mantissa is max
of 256 (3 characters). The string will be "-M.MMMMe-4932".
(ie, 8 chars more than mantissa). Plus one for trailing \0.
Plus one for rounding. */
const(size_t) BUFFER_LEN = value.sizeof * 3 + 8 + 1 + 1;
char[BUFFER_LEN] buffer = void;
CTFloat.sprint(buffer.ptr, 'g', value);
assert(strlen(buffer.ptr) < BUFFER_LEN);
if (allowHex)
{
bool isOutOfRange;
real_t r = CTFloat.parse(buffer.ptr, isOutOfRange);
//assert(!isOutOfRange); // test/compilable/test22725.c asserts here
if (r != value) // if exact duplication
CTFloat.sprint(buffer.ptr, 'a', value);
}
buf.writestring(buffer.ptr);
if (buffer.ptr[strlen(buffer.ptr) - 1] == '.')
buf.remove(buf.length() - 1, 1);
if (type)
{
Type t = type.toBasetype();
switch (t.ty)
{
case Tfloat32:
case Timaginary32:
case Tcomplex32:
buf.writeByte('F');
break;
case Tfloat80:
case Timaginary80:
case Tcomplex80:
buf.writeByte('L');
break;
default:
break;
}
if (t.isimaginary())
buf.writeByte('i');
}
}
private void templateParameterToBuffer(TemplateParameter tp, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new TemplateParameterPrettyPrintVisitor(buf, hgs);
tp.accept(v);
}
private extern (C++) final class TemplateParameterPrettyPrintVisitor : Visitor
{
alias visit = Visitor.visit;
public:
OutBuffer* buf;
HdrGenState* hgs;
extern (D) this(OutBuffer* buf, HdrGenState* hgs)
{
this.buf = buf;
this.hgs = hgs;
}
override void visit(TemplateTypeParameter tp)
{
buf.writestring(tp.ident.toString());
if (tp.specType)
{
buf.writestring(" : ");
typeToBuffer(tp.specType, null, buf, hgs);
}
if (tp.defaultType)
{
buf.writestring(" = ");
typeToBuffer(tp.defaultType, null, buf, hgs);
}
}
override void visit(TemplateThisParameter tp)
{
buf.writestring("this ");
visit(cast(TemplateTypeParameter)tp);
}
override void visit(TemplateAliasParameter tp)
{
buf.writestring("alias ");
if (tp.specType)
typeToBuffer(tp.specType, tp.ident, buf, hgs);
else
buf.writestring(tp.ident.toString());
if (tp.specAlias)
{
buf.writestring(" : ");
objectToBuffer(tp.specAlias, buf, hgs);
}
if (tp.defaultAlias)
{
buf.writestring(" = ");
objectToBuffer(tp.defaultAlias, buf, hgs);
}
}
override void visit(TemplateValueParameter tp)
{
typeToBuffer(tp.valType, tp.ident, buf, hgs);
if (tp.specValue)
{
buf.writestring(" : ");
tp.specValue.expressionToBuffer(buf, hgs);
}
if (tp.defaultValue)
{
buf.writestring(" = ");
tp.defaultValue.expressionToBuffer(buf, hgs);
}
}
override void visit(TemplateTupleParameter tp)
{
buf.writestring(tp.ident.toString());
buf.writestring("...");
}
}
private void conditionToBuffer(Condition c, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new ConditionPrettyPrintVisitor(buf, hgs);
c.accept(v);
}
private extern (C++) final class ConditionPrettyPrintVisitor : Visitor
{
alias visit = Visitor.visit;
public:
OutBuffer* buf;
HdrGenState* hgs;
extern (D) this(OutBuffer* buf, HdrGenState* hgs)
{
this.buf = buf;
this.hgs = hgs;
}
override void visit(DebugCondition c)
{
buf.writestring("debug (");
if (c.ident)
buf.writestring(c.ident.toString());
else
buf.print(c.level);
buf.writeByte(')');
}
override void visit(VersionCondition c)
{
buf.writestring("version (");
if (c.ident)
buf.writestring(c.ident.toString());
else
buf.print(c.level);
buf.writeByte(')');
}
override void visit(StaticIfCondition c)
{
buf.writestring("static if (");
c.exp.expressionToBuffer(buf, hgs);
buf.writeByte(')');
}
}
void toCBuffer(const Statement s, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new StatementPrettyPrintVisitor(buf, hgs);
(cast() s).accept(v);
}
void toCBuffer(const Type t, OutBuffer* buf, const Identifier ident, HdrGenState* hgs)
{
typeToBuffer(cast() t, ident, buf, hgs);
}
void toCBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs)
{
scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
s.accept(v);
}
// used from TemplateInstance::toChars() and TemplateMixin::toChars()
void toCBufferInstance(const TemplateInstance ti, OutBuffer* buf, bool qualifyTypes = false)
{
HdrGenState hgs;
hgs.fullQual = qualifyTypes;
scope v = new DsymbolPrettyPrintVisitor(buf, &hgs);
v.visit(cast() ti);
}
void toCBuffer(const Initializer iz, OutBuffer* buf, HdrGenState* hgs)
{
initializerToBuffer(cast() iz, buf, hgs);
}
bool stcToBuffer(OutBuffer* buf, StorageClass stc)
{
//printf("stc: %llx\n", stc);
bool result = false;
if (stc & STC.scopeinferred)
{
//buf.writestring("scope-inferred ");
stc &= ~(STC.scope_ | STC.scopeinferred);
}
if (stc & STC.returninferred)
{
//buf.writestring((stc & STC.returnScope) ? "return-scope-inferred " : "return-ref-inferred ");
stc &= ~(STC.return_ | STC.returninferred);
}
/* Put scope ref return into a standard order
*/
string rrs;
const isout = (stc & STC.out_) != 0;
//printf("bsr = %d %llx\n", buildScopeRef(stc), stc);
final switch (buildScopeRef(stc))
{
case ScopeRef.None:
case ScopeRef.Scope:
case ScopeRef.Ref:
case ScopeRef.Return:
break;
case ScopeRef.ReturnScope: rrs = "return scope"; goto L1;
case ScopeRef.ReturnRef: rrs = isout ? "return out" : "return ref"; goto L1;
case ScopeRef.RefScope: rrs = isout ? "out scope" : "ref scope"; goto L1;
case ScopeRef.ReturnRef_Scope: rrs = isout ? "return out scope" : "return ref scope"; goto L1;
case ScopeRef.Ref_ReturnScope: rrs = isout ? "out return scope" : "ref return scope"; goto L1;
L1:
buf.writestring(rrs);
result = true;
stc &= ~(STC.out_ | STC.scope_ | STC.ref_ | STC.return_);
break;
}
while (stc)
{
const s = stcToString(stc);
if (!s.length)
break;
if (result)
buf.writeByte(' ');
result = true;
buf.writestri