Skip to content

Commit eb090d8

Browse files
Fix heredocs inside %W and %w lists
The problem was that we were treating heredoc bodies as part of the %W list because we didn't push the scanning cursor past the heredoc after lexing out the here doc. To fix this, we changed the whitespace scanning function to quit scanning when it reaches a newline but only in the case that a heredoc is present. Additionally, we need to prevent double counting newlines in the case of a heredoc. For example: ```ruby %W(<<foo 123) foo ``` The newline after the `)` is counted as part of scanning the heredoc, so we added logic to prevent double counting the newline when scanning the rest of the %W list. Co-authored-by: Jemma Issroff <jemmaissroff@gmail.com>
1 parent a5fa477 commit eb090d8

File tree

6 files changed

+53
-7
lines changed

6 files changed

+53
-7
lines changed

include/yarp/util/yp_char.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ size_t yp_strspn_whitespace(const char *string, ptrdiff_t length);
1515
// whitespace while also tracking the location of each newline. Disallows
1616
// searching past the given maximum number of characters.
1717
size_t
18-
yp_strspn_whitespace_newlines(const char *string, long length, yp_newline_list_t *newline_list);
18+
yp_strspn_whitespace_newlines(const char *string, long length, yp_newline_list_t *newline_list, bool);
1919

2020
// Returns the number of characters at the start of the string that are inline
2121
// whitespace. Disallows searching past the given maximum number of characters.

src/util/yp_char.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,20 @@ yp_strspn_whitespace(const char *string, ptrdiff_t length) {
7575
// whitespace while also tracking the location of each newline. Disallows
7676
// searching past the given maximum number of characters.
7777
size_t
78-
yp_strspn_whitespace_newlines(const char *string, long length, yp_newline_list_t *newline_list) {
78+
yp_strspn_whitespace_newlines(const char *string, long length, yp_newline_list_t *newline_list, bool stop_at_newline) {
7979
if (length <= 0) return 0;
8080

8181
size_t size = 0;
8282
size_t maximum = (size_t) length;
8383

8484
while (size < maximum && (yp_char_table[(unsigned char) string[size]] & YP_CHAR_BIT_WHITESPACE)) {
8585
if (string[size] == '\n') {
86-
yp_newline_list_append(newline_list, string + size);
86+
if (stop_at_newline) {
87+
return size + 1;
88+
}
89+
else {
90+
yp_newline_list_append(newline_list, string + size);
91+
}
8792
}
8893

8994
size++;

src/util/yp_newline_list.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ yp_newline_list_init(yp_newline_list_t *list, const char *start, size_t capacity
2525
bool
2626
yp_newline_list_append(yp_newline_list_t *list, const char *cursor) {
2727
if (list->size == list->capacity) {
28-
list->capacity = list->capacity * 3 / 2;
28+
list->capacity = (list->capacity * 3) / 2;
2929
list->offsets = (size_t *) realloc(list->offsets, list->capacity * sizeof(size_t));
3030
if (list->offsets == NULL) return false;
3131
}
3232

3333
assert(cursor >= list->start);
34-
list->offsets[list->size++] = (size_t) (cursor - list->start + 1);
34+
size_t newline_offset = (size_t) (cursor - list->start + 1);
35+
assert(list->size == 0 || newline_offset > list->offsets[list->size - 1]);
36+
list->offsets[list->size++] = newline_offset;
3537

3638
return true;
3739
}

src/yarp.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6505,14 +6505,26 @@ parser_lex(yp_parser_t *parser) {
65056505
}
65066506
}
65076507
case YP_LEX_LIST:
6508+
if (parser->next_start != NULL) {
6509+
parser->current.end = parser->next_start;
6510+
parser->next_start = NULL;
6511+
}
6512+
65086513
// First we'll set the beginning of the token.
65096514
parser->current.start = parser->current.end;
65106515

65116516
// If there's any whitespace at the start of the list, then we're
65126517
// going to trim it off the beginning and create a new token.
65136518
size_t whitespace;
6514-
if ((whitespace = yp_strspn_whitespace_newlines(parser->current.end, parser->end - parser->current.end, &parser->newline_list)) > 0) {
6519+
6520+
bool should_stop = parser->heredoc_end;
6521+
6522+
if ((whitespace = yp_strspn_whitespace_newlines(parser->current.end, parser->end - parser->current.end, &parser->newline_list, should_stop)) > 0) {
65156523
parser->current.end += whitespace;
6524+
if (parser->current.end[-1] == '\n') {
6525+
// mutates next_start
6526+
parser_flush_heredoc_end(parser);
6527+
}
65166528
LEX(YP_TOKEN_WORDS_SEP);
65176529
}
65186530

test/parse_test.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ def test_empty_string
2828

2929
known_failures = %w[
3030
seattlerb/heredoc_nested.txt
31-
seattlerb/pct_w_heredoc_interp_nested.txt
3231
]
3332

3433
def find_source_file_node(node)

test/snapshots/seattlerb/pct_w_heredoc_interp_nested.txt

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)