Skip to content

Commit

Permalink
frontend: propagate error as null instead of exit(1)
Browse files Browse the repository at this point in the history
This patch propagates null when Module AST node can't be constructed correctly
instead of doing fatal(), which terminates the program. This is useful if users
use DMD frontend as a library. This patch also increases coverage on the
affected lines.

Fixes issue 22751.

Signed-off-by: Luís Ferreira <contact@lsferreira.net>
  • Loading branch information
ljmf00 authored and dlang-bot committed Feb 17, 2022
1 parent 38d13db commit 7bf49c8
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 10 deletions.
20 changes: 11 additions & 9 deletions src/dmd/dmodule.d
Expand Up @@ -524,7 +524,7 @@ extern (C++) final class Module : Package
buf.printf("%s\t(%s)", ident.toChars(), m.srcfile.toChars());
message("import %s", buf.peekChars());
}
m = m.parse();
if((m = m.parse()) is null) return null;

// Call onImport here because if the module is going to be compiled then we
// need to determine it early because it affects semantic analysis. This is
Expand Down Expand Up @@ -727,7 +727,7 @@ extern (C++) final class Module : Package
if (buf.length & 3)
{
error("odd length of UTF-32 char source %llu", cast(ulong) buf.length);
fatal();
return null;
}

const (uint)[] eBuf = cast(const(uint)[])buf;
Expand All @@ -743,7 +743,7 @@ extern (C++) final class Module : Package
if (u > 0x10FFFF)
{
error("UTF-32 value %08x greater than 0x10FFFF", u);
fatal();
return null;
}
dbuf.writeUTF8(u);
}
Expand Down Expand Up @@ -773,7 +773,7 @@ extern (C++) final class Module : Package
if (buf.length & 1)
{
error("odd length of UTF-16 char source %llu", cast(ulong) buf.length);
fatal();
return null;
}

const (ushort)[] eBuf = cast(const(ushort)[])buf;
Expand All @@ -793,26 +793,26 @@ extern (C++) final class Module : Package
if (i >= eBuf.length)
{
error("surrogate UTF-16 high value %04x at end of file", u);
fatal();
return null;
}
const u2 = readNext(&eBuf[i]);
if (u2 < 0xDC00 || 0xE000 <= u2)
{
error("surrogate UTF-16 low value %04x out of range", u2);
fatal();
return null;
}
u = (u - 0xD7C0) << 10;
u |= (u2 - 0xDC00);
}
else if (u >= 0xDC00 && u <= 0xDFFF)
{
error("unpaired surrogate UTF-16 value %04x", u);
fatal();
return null;
}
else if (u == 0xFFFE || u == 0xFFFF)
{
error("illegal UTF-16 value %04x", u);
fatal();
return null;
}
dbuf.writeUTF8(u);
}
Expand Down Expand Up @@ -899,7 +899,7 @@ extern (C++) final class Module : Package
if (buf[0] >= 0x80)
{
error("source file must start with BOM or ASCII character, not \\x%02X", buf[0]);
fatal();
return null;
}
}
}
Expand Down Expand Up @@ -929,6 +929,8 @@ extern (C++) final class Module : Package
? UTF32ToUTF8!(Endian.little)(buf)
: UTF32ToUTF8!(Endian.big)(buf);
}
// an error happened on UTF conversion
if (buf is null) return null;
}

/* If it starts with the string "Ddoc", then it's a documentation
Expand Down
2 changes: 1 addition & 1 deletion src/dmd/frontend.d
Expand Up @@ -407,7 +407,7 @@ Tuple!(Module, "module_", Diagnostics, "diagnostics") parseModule(AST = ASTCodeg
}
}

m.parseModule!AST();
m = m.parseModule!AST();

Diagnostics diagnostics = {
errors: global.errors,
Expand Down
61 changes: 61 additions & 0 deletions test/unit/frontend.d
Expand Up @@ -102,6 +102,67 @@ unittest
assert(t.module_.parent !is null);
}

@("parseModule - invalid module")
unittest
{
import std.conv : to;
import dmd.frontend;

import dmd.common.outbuffer;
import dmd.globals : Loc;
import dmd.console : Color;
import core.stdc.stdarg : va_list;

string[] diagnosticMessages;

nothrow bool diagnosticHandler(const ref Loc loc, Color headerColor, const(char)* header,
const(char)* format, va_list ap, const(char)* p1, const(char)* p2)
{
OutBuffer tmp;
tmp.vprintf(format, ap);
diagnosticMessages ~= to!string(tmp.peekChars());
return true;
}
initDMD(&diagnosticHandler);

auto bom = parseModule("foo/bar.d", [0x84, 0xC3]);
assert(bom.diagnostics.hasErrors);
assert(bom.module_ is null);

auto odd16 = parseModule("foo/bar.d", [0xFE, 0xFF, 0x84, 0x00]);
assert(odd16.diagnostics.hasErrors);
assert(odd16.module_ is null);

auto odd32 = parseModule("foo/bar.d", [0x00, 0x00, 0xFE, 0xFF, 0x84, 0x81]);
assert(odd32.diagnostics.hasErrors);
assert(odd32.module_ is null);

auto utf32gt = parseModule("foo/bar.d", [0x00, 0x00, 0xFE, 0xFF, 0x84, 0x81, 0x00]);
assert(utf32gt.diagnostics.hasErrors);
assert(utf32gt.module_ is null);

auto ill16 = parseModule("foo/bar.d", [0xFE, 0xFF, 0xFF, 0xFF, 0x00]);
assert(ill16.diagnostics.hasErrors);
assert(ill16.module_ is null);

auto unp16 = parseModule("foo/bar.d", [0xFE, 0xFF, 0xDC, 0x00, 0x00]);
assert(unp16.diagnostics.hasErrors);
assert(unp16.module_ is null);

auto sur16 = parseModule("foo/bar.d", [0xFE, 0xFF, 0xD8, 0x00, 0xE0, 0x00, 0x00]);
assert(sur16.diagnostics.hasErrors);
assert(sur16.module_ is null);

assert(diagnosticMessages.length == 7);
assert(diagnosticMessages[0] == "source file must start with BOM or ASCII character, not \\x84");
assert(diagnosticMessages[1] == "odd length of UTF-16 char source 3");
assert(diagnosticMessages[2] == "odd length of UTF-32 char source 3");
assert(diagnosticMessages[3] == "UTF-32 value 84810000 greater than 0x10FFFF");
assert(diagnosticMessages[4] == "illegal UTF-16 value ffff");
assert(diagnosticMessages[5] == "unpaired surrogate UTF-16 value dc00");
assert(diagnosticMessages[6] == "surrogate UTF-16 low value e000 out of range");
}

@("initDMD - contract checking")
unittest
{
Expand Down

0 comments on commit 7bf49c8

Please sign in to comment.