Allow multiple message arguments for static assert (#14611)

Allow multiple message arguments for static assert

Signed-off-by: Razvan Nitu <razvan.nitu1305@gmail.com>
Merged-on-behalf-of: Razvan Nitu <razvan.nitu1305@gmail.com>
This commit is contained in:
Nick Treleaven 2022-12-19 14:06:48 +00:00 committed by GitHub
parent 5f2761d146
commit 8cc27077c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 100 additions and 32 deletions

View file

@ -0,0 +1,13 @@
`static assert` now supports multiple message arguments
When the condition evaluates to false, any subsequent expressions will
each be converted to string and then concatenated. The resulting string
will be printed out along with the error diagnostic.
---
enum e = 3;
static assert(false, "a = ", e);
---
Will print:
$(CONSOLE
file.d(2): Error: static assert: a = 3
)

View file

@ -452,12 +452,19 @@ struct ASTBase
extern (C++) final class StaticAssert : Dsymbol
{
Expression exp;
Expression msg;
Expressions* msg;
extern (D) this(const ref Loc loc, Expression exp, Expression msg)
{
super(Id.empty);
this.loc = loc;
super(loc, Id.empty);
this.exp = exp;
this.msg = new Expressions(1);
(*this.msg)[0] = msg;
}
extern (D) this(const ref Loc loc, Expression exp, Expressions* msg)
{
super(loc, Id.empty);
this.exp = exp;
this.msg = msg;
}

View file

@ -4607,7 +4607,7 @@ class StaticAssert final : public Dsymbol
{
public:
Expression* exp;
Expression* msg;
Array<Expression* >* msgs;
StaticAssert* syntaxCopy(Dsymbol* s) override;
void addMember(Scope* sc, ScopeDsymbol* sds) override;
bool oneMember(Dsymbol** ps, Identifier* ident) override;

View file

@ -823,10 +823,13 @@ public:
buf.writestring(s.kind());
buf.writeByte('(');
s.exp.expressionToBuffer(buf, hgs);
if (s.msg)
if (s.msgs)
{
buf.writestring(", ");
s.msg.expressionToBuffer(buf, hgs);
foreach (m; (*s.msgs)[])
{
buf.writestring(", ");
m.expressionToBuffer(buf, hgs);
}
}
buf.writestring(");");
buf.writenl();

View file

@ -1982,7 +1982,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
{
const loc = token.loc;
AST.Expression exp;
AST.Expression msg = null;
AST.Expressions* msg = null;
//printf("parseStaticAssert()\n");
nextToken();
@ -1991,15 +1991,16 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
exp = parseAssignExp();
if (token.value == TOK.comma)
{
nextToken();
if (token.value != TOK.rightParenthesis)
if (peekNext() == TOK.rightParenthesis)
{
msg = parseAssignExp();
if (token.value == TOK.comma)
nextToken();
nextToken(); // consume `,`
nextToken(); // consume `)`
}
else
msg = parseArguments();
}
check(TOK.rightParenthesis);
else
check(TOK.rightParenthesis);
check(TOK.semicolon, "static assert");
return new AST.StaticAssert(loc, exp, msg);
}

View file

@ -110,21 +110,36 @@ private extern(C++) final class Semantic2Visitor : Visitor
else if (result)
return;
if (sa.msg)
if (sa.msgs)
{
sc = sc.startCTFE();
sa.msg = sa.msg.expressionSemantic(sc);
sa.msg = resolveProperties(sc, sa.msg);
sc = sc.endCTFE();
sa.msg = sa.msg.ctfeInterpret();
if (StringExp se = sa.msg.toStringExp())
OutBuffer msgbuf;
for (size_t i = 0; i < sa.msgs.length; i++)
{
// same with pragma(msg)
const slice = se.toUTF8(sc).peekString();
error(sa.loc, "static assert: \"%.*s\"", cast(int)slice.length, slice.ptr);
Expression e = (*sa.msgs)[i];
sc = sc.startCTFE();
e = e.expressionSemantic(sc);
e = resolveProperties(sc, e);
sc = sc.endCTFE();
e = ctfeInterpretForPragmaMsg(e);
if (e.op == EXP.error)
{
errorSupplemental(sa.loc, "while evaluating `static assert` argument `%s`", (*sa.msgs)[i].toChars());
return;
}
StringExp se = e.toStringExp();
if (se)
{
const slice = se.toUTF8(sc).peekString();
// Hack to keep old formatting to avoid changing error messages everywhere
if (sa.msgs.length == 1)
msgbuf.printf("\"%.*s\"", cast(int)slice.length, slice.ptr);
else
msgbuf.printf("%.*s", cast(int)slice.length, slice.ptr);
}
else
msgbuf.printf("%s", e.toChars());
}
else
error(sa.loc, "static assert: %s", sa.msg.toChars());
error(sa.loc, "static assert: %s", msgbuf.extractChars());
}
else
error(sa.loc, "static assert: `%s` is false", sa.exp.toChars());

View file

@ -13,6 +13,7 @@
module dmd.staticassert;
import dmd.arraytypes;
import dmd.dscope;
import dmd.dsymbol;
import dmd.expression;
@ -27,19 +28,27 @@ import dmd.visitor;
extern (C++) final class StaticAssert : Dsymbol
{
Expression exp;
Expression msg;
Expressions* msgs;
extern (D) this(const ref Loc loc, Expression exp, Expression msg)
{
super(loc, Id.empty);
this.exp = exp;
this.msg = msg;
this.msgs = new Expressions(1);
(*this.msgs)[0] = msg;
}
extern (D) this(const ref Loc loc, Expression exp, Expressions* msgs)
{
super(loc, Id.empty);
this.exp = exp;
this.msgs = msgs;
}
override StaticAssert syntaxCopy(Dsymbol s)
{
assert(!s);
return new StaticAssert(loc, exp.syntaxCopy(), msg ? msg.syntaxCopy() : null);
return new StaticAssert(loc, exp.syntaxCopy(), msgs ? Expression.arraySyntaxCopy(msgs) : null);
}
override void addMember(Scope* sc, ScopeDsymbol sds)

View file

@ -18,7 +18,7 @@ class StaticAssert : public Dsymbol
{
public:
Expression *exp;
Expression *msg;
Expressions *msg;
StaticAssert *syntaxCopy(Dsymbol *s) override;
void addMember(Scope *sc, ScopeDsymbol *sds) override;

View file

@ -490,8 +490,9 @@ package mixin template ParseVisitMethods(AST)
{
//printf("Visiting StaticAssert\n");
s.exp.accept(this);
if (s.msg)
s.msg.accept(this);
if (s.msgs)
foreach (m; (*s.msgs)[])
m.accept(this);
}
override void visit(AST.EnumMember em)

View file

@ -0,0 +1,9 @@
/*
TEST_OUTPUT:
---
fail_compilation/staticassertargs.d(9): Error: static assert: abcxe3!!
---
*/
enum e = "!!";
static assert(false, "abc", ['x', 'e'], 3, e);

View file

@ -0,0 +1,10 @@
/*
TEST_OUTPUT:
---
fail_compilation/staticassertargsfail.d(10): Error: incompatible types for `('x') : (new Object)`: `char` and `object.Object`
fail_compilation/staticassertargsfail.d(10): while evaluating `static assert` argument `['x', new Object] ~ ""`
---
*/
static assert(0, "abc", ['x', new Object] ~ "");