Skip to content

Commit 8805d9c

Browse files
emit better errors for strings with bad indentation
Co-Authored-By: John Bartholomew <jpa.bartholomew@gmail.com>
1 parent 9201aeb commit 8805d9c

File tree

5 files changed

+80
-19
lines changed

5 files changed

+80
-19
lines changed

core/lexer.cpp

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -445,13 +445,13 @@ std::string lex_number(const char *&c, const std::string &filename, const Locati
445445
case '7':
446446
case '8':
447447
case '9': state = AFTER_EXP_DIGIT; break;
448-
448+
449449
case '_': state = AFTER_EXP_UNDERSCORE; goto skip_char;
450450

451451
default: goto end;
452452
}
453453
break;
454-
454+
455455
case AFTER_EXP_UNDERSCORE:
456456
switch (*c) {
457457
// The only valid transition from _ is to a digit.
@@ -496,17 +496,26 @@ static int whitespace_check(const char *a, const char *b)
496496
return i;
497497
}
498498

499-
/*
500-
static void add_whitespace(Fodder &fodder, const char *s, size_t n)
501-
{
502-
std::string ws(s, n);
503-
if (fodder.size() == 0 || fodder.back().kind != FodderElement::WHITESPACE) {
504-
fodder.emplace_back(FodderElement::WHITESPACE, ws);
499+
static void describe_whitespace(std::stringstream& msg, const std::string& ws) {
500+
int spaces = 0;
501+
int tabs = 0;
502+
for (char c : ws) {
503+
if (c == ' ')
504+
spaces++;
505+
else if (c == '\t')
506+
tabs++;
507+
}
508+
if (spaces > 0 && tabs > 0) {
509+
msg << spaces << (spaces == 1 ? " space" : " spaces") << " and " << tabs
510+
<< (tabs == 1 ? " tab" : " tabs");
511+
} else if (spaces > 0) {
512+
msg << spaces << (spaces == 1 ? " space" : " spaces");
513+
} else if (tabs > 0) {
514+
msg << tabs << (tabs == 1 ? " tab" : " tabs");
505515
} else {
506-
fodder.back().data += ws;
516+
msg << "no indentation";
507517
}
508518
}
509-
*/
510519

511520
Tokens jsonnet_lex(const std::string &filename, const char *input)
512521
{
@@ -840,17 +849,48 @@ Tokens jsonnet_lex(const std::string &filename, const char *input)
840849
// Examine next line
841850
ws_chars = whitespace_check(first_line, c);
842851
if (ws_chars == 0) {
843-
// End of text block
844-
// Skip over any whitespace
852+
// End of text block (or indentation error).
853+
// Count actual whitespace on this line.
854+
int actual_ws = 0;
855+
while (c[actual_ws] == ' ' ||
856+
c[actual_ws] == '\t') {
857+
actual_ws++;
858+
}
859+
860+
// Check if this is the terminator |||
861+
bool is_terminator = (
862+
c[actual_ws] == '|' &&
863+
c[actual_ws + 1] == '|' &&
864+
c[actual_ws + 2] == '|');
865+
866+
if (!is_terminator) {
867+
// Not a terminator - check if it's an
868+
// indentation issue.
869+
if (actual_ws > 0) {
870+
// Has whitespace but doesn't match expected
871+
// indentation.
872+
std::stringstream msg;
873+
msg << "text block indentation mismatch: "
874+
"expected at least ";
875+
describe_whitespace(msg, string_block_indent);
876+
msg << ", found ";
877+
describe_whitespace(msg, std::string(c, actual_ws));
878+
throw StaticError(filename, begin, msg.str());
879+
} else {
880+
// No whitespace and no ||| - missing
881+
// terminator.
882+
auto msg =
883+
"text block not terminated with |||";
884+
throw StaticError(filename, begin, msg);
885+
}
886+
}
887+
888+
// Valid termination - skip over any whitespace.
845889
while (*c == ' ' || *c == '\t') {
846890
string_block_term_indent += *c;
847891
++c;
848892
}
849-
// Expect |||
850-
if (!(*c == '|' && *(c + 1) == '|' && *(c + 2) == '|')) {
851-
auto msg = "text block not terminated with |||";
852-
throw StaticError(filename, begin, msg);
853-
}
893+
// Skip the |||
854894
c += 3; // Leave after the last |
855895
data = block.str();
856896
kind = Token::STRING_BLOCK;

core/lexer_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ TEST(Lexer, TestBlockStringBadIndent)
341341
testLex("block string bad indent",
342342
str,
343343
{},
344-
"block string bad indent:1:1: text block not terminated with |||");
344+
"block string bad indent:1:1: text block indentation mismatch: expected at least 2 spaces, found 1 space");
345345
}
346346

347347
TEST(Lexer, TestBlockStringEof)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
STATIC ERROR: error.parse.text_block_bad_whitespace.jsonnet:17:1: text block not terminated with |||
1+
STATIC ERROR: error.parse.text_block_bad_whitespace.jsonnet:17:1: text block indentation mismatch: expected at least 4 spaces, found 1 tab
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
Copyright 2026 Google Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUTHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
|||
18+
foo
19+
bar
20+
|||
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
STATIC ERROR: error.parse.text_block_indent_spaces.jsonnet:17:1: text block indentation mismatch: expected at least 4 spaces, found 2 spaces

0 commit comments

Comments
 (0)