@@ -1208,8 +1208,7 @@ private pure nothrow @safe:
12081208 {
12091209 if (range.index >= range.bytes.length)
12101210 {
1211- error(" Error: unterminated string literal" );
1212- token = Token (tok! " " );
1211+ error(token, " Error: unterminated string literal" );
12131212 return ;
12141213 }
12151214 version (X86_64 )
@@ -1254,8 +1253,7 @@ private pure nothrow @safe:
12541253 {
12551254 if (range.index >= range.bytes.length)
12561255 {
1257- error(" Error: unterminated string literal" );
1258- token = Token (tok! " " );
1256+ error(token, " Error: unterminated string literal" );
12591257 return ;
12601258 }
12611259 version (X86_64 )
@@ -1280,17 +1278,15 @@ private pure nothrow @safe:
12801278 range.popFront();
12811279 if (range.index >= range.bytes.length)
12821280 {
1283- error(" Error: unterminated string literal" );
1284- token = Token (tok! " " );
1281+ error(token, " Error: unterminated string literal" );
12851282 return ;
12861283 }
12871284 range.popFront();
12881285 while (true )
12891286 {
12901287 if (range.index >= range.bytes.length)
12911288 {
1292- error(" Error: unterminated string literal" );
1293- token = Token (tok! " " );
1289+ error(token, " Error: unterminated string literal" );
12941290 return ;
12951291 }
12961292 else if (range.bytes[range.index] == ' "' )
@@ -1388,8 +1384,7 @@ private pure nothrow @safe:
13881384 }
13891385 else
13901386 {
1391- error(" Error: `\" ` expected to end delimited string literal" );
1392- token = Token (tok! " " );
1387+ error(token, " Error: `\" ` expected to end delimited string literal" );
13931388 return ;
13941389 }
13951390 }
@@ -1464,6 +1459,13 @@ private pure nothrow @safe:
14641459 }
14651460
14661461 advance(_front);
1462+
1463+ if (range.index >= range.bytes.length)
1464+ {
1465+ error(token, " Error: unterminated token string literal" );
1466+ return ;
1467+ }
1468+
14671469 while (depth > 0 && ! empty)
14681470 {
14691471 auto t = front();
@@ -1503,8 +1505,7 @@ private pure nothrow @safe:
15031505 {
15041506 if (range.index >= range.bytes.length)
15051507 {
1506- error(" Error: unterminated hex string literal" );
1507- token = Token (tok! " " );
1508+ error(token, " Error: unterminated hex string literal" );
15081509 return ;
15091510 }
15101511 else if (isWhitespace())
@@ -1520,8 +1521,7 @@ private pure nothrow @safe:
15201521 range.popFront();
15211522 break loop;
15221523 default :
1523- error(" Error: invalid character in hex string" );
1524- token = Token (tok! " " );
1524+ error(token, " Error: invalid character in hex string" );
15251525 return ;
15261526 }
15271527 }
@@ -1706,8 +1706,7 @@ private pure nothrow @safe:
17061706 else
17071707 {
17081708 err:
1709- error(" Error: Expected `'` to end character literal" );
1710- token = Token (tok! " " );
1709+ error(token, " Error: Expected `'` to end character literal" );
17111710 }
17121711 }
17131712
@@ -1848,6 +1847,12 @@ private pure nothrow @safe:
18481847 auto mark = range.mark();
18491848 };
18501849
1850+ void error (ref Token token, string message)
1851+ {
1852+ token.type = tok! " " ;
1853+ error(message);
1854+ }
1855+
18511856 void error (string message)
18521857 {
18531858 _messages ~= Message(range.line, range.column, message, true );
@@ -2449,3 +2454,56 @@ unittest
24492454 immutable t2 = e2.tok;
24502455 immutable t3 = e3.tok;
24512456}
2457+
2458+ // / empty '' is invalid syntax, but should still get parsed properly, with an
2459+ // / error token and proper location info
2460+ unittest
2461+ {
2462+ import std.conv : to;
2463+ import std.exception : enforce;
2464+
2465+ static immutable src = ` module foo.bar;
2466+
2467+ void main() {
2468+ x = '';
2469+ }
2470+ ` ;
2471+
2472+ LexerConfig cf;
2473+ StringCache ca = StringCache(16 );
2474+
2475+ const tokens = getTokensForParser(src, cf, &ca);
2476+
2477+ int i;
2478+ assert (tokens[i++ ].type == tok! " module" );
2479+ assert (tokens[i++ ].type == tok! " identifier" );
2480+ assert (tokens[i++ ].type == tok! " ." );
2481+ assert (tokens[i++ ].type == tok! " identifier" );
2482+ assert (tokens[i++ ].type == tok! " ;" );
2483+ assert (tokens[i++ ].type == tok! " void" );
2484+ assert (tokens[i++ ].type == tok! " identifier" );
2485+ assert (tokens[i++ ].type == tok! " (" );
2486+ assert (tokens[i++ ].type == tok! " )" );
2487+ assert (tokens[i++ ].type == tok! " {" );
2488+ assert (tokens[i++ ].type == tok! " identifier" );
2489+ assert (tokens[i++ ].type == tok! " =" );
2490+ assert (tokens[i].type == tok! " " );
2491+ assert (tokens[i].line == tokens[i - 1 ].line);
2492+ assert (tokens[i].column == tokens[i - 1 ].column + 2 );
2493+ i++ ;
2494+ assert (tokens[i++ ].type == tok! " ;" );
2495+ assert (tokens[i++ ].type == tok! " }" );
2496+
2497+ void checkInvalidTrailingString (const Token [] tokens)
2498+ {
2499+ assert (tokens.length == 3 );
2500+ assert (tokens[2 ].index != 0 );
2501+ assert (tokens[2 ].column >= 4 );
2502+ assert (tokens[2 ].type == tok! " " );
2503+ }
2504+
2505+ checkInvalidTrailingString(getTokensForParser(` x = "foo` , cf, &ca));
2506+ checkInvalidTrailingString(getTokensForParser(` x = r"foo` , cf, &ca));
2507+ checkInvalidTrailingString(getTokensForParser(" x = `foo" , cf, &ca));
2508+ checkInvalidTrailingString(getTokensForParser(" x = q{foo" , cf, &ca));
2509+ }
0 commit comments