Fix crash indenting quoted suffix after command substitution

Commit b00899179 (Don't indent multi-line quoted strings; do indent inside
(), 2024-04-28) made parse_util_compute_indents() crash on `echo "$()"'x`.
After recursively indenting the command substitution, we indent the "'x
suffix.  We skip the quoted part by setting "done=2".  Later we wrongly
index "self.indents[done..range.start+offset+1]" (= "self.indents[2..1]").

Fix this by making sure that "start >= done", thus not setting any indents
for the quoted suffix.  There is no need to do so; only the first character
in each line needs an indent.
This commit is contained in:
Johannes Altmanninger 2024-09-28 13:27:10 +02:00
parent 50314e309b
commit 4c43819d32
2 changed files with 31 additions and 31 deletions

View file

@ -941,42 +941,38 @@ impl<'a> IndentVisitor<'a> {
} }
fn indent_string_part(&mut self, range: Range<usize>, is_double_quoted: bool) { fn indent_string_part(&mut self, range: Range<usize>, is_double_quoted: bool) {
let mut done = range.start; let mut start = range.start;
let mut quoted = false; let mut quoted = false;
{
if is_double_quoted { if is_double_quoted {
match quote_end(self.src, range.start, '"') { match quote_end(self.src, range.start, '"') {
Some(q_end) => { Some(q_end) => {
// We may be (in) a multi-line string, so don't indent. // We may be (in) a multi-line string, so don't indent.
done = q_end + 1; start = q_end + 1;
} }
None => quoted = true, None => quoted = true,
} }
} }
let part = &self.src[done..range.end]; let mut done = start;
if !quoted { if !quoted {
let part = &self.src[done..range.end];
let mut callback = |offset| { let mut callback = |offset| {
if !quoted { if !quoted {
// Quote open event. Indent unquoted part, including the opening quote. // Quote open event. Indent unquoted part, including the opening quote.
self.indents[done..range.start + offset + 1].fill(self.indent); self.indents[done..start + offset + 1].fill(self.indent);
done = range.start + offset + 1; done = start + offset + 1;
} else { } else {
// Quote close. Don't indent, in case it's a multiline string. // Quote close. Don't indent, in case it's a multiline string.
// Mark the first line as indented but only to make tests look prettier. // Mark the first line as indented but only to make tests look prettier.
let first_line_length = self.src[range.start..range.start + offset] let first_line_length = self.src[start..start + offset]
.chars() .chars()
.take_while(|&c| c != '\n') .take_while(|&c| c != '\n')
.count(); .count();
self.indents[range.start..range.start + first_line_length] self.indents[start..start + first_line_length].fill(self.indent);
.fill(self.indent); done = start + offset;
done = range.start + offset;
} }
quoted = !quoted; quoted = !quoted;
}; };
for _token in for _token in Tokenizer::with_quote_events(part, TOK_ACCEPT_UNFINISHED, &mut callback) {
Tokenizer::with_quote_events(part, TOK_ACCEPT_UNFINISHED, &mut callback)
{
}
} }
} }
if !quoted { if !quoted {

View file

@ -435,5 +435,9 @@ fn test_indents() {
0, "\n) line4", 0, "\n) line4",
0, "\nline5\"", 0, "\nline5\"",
); );
validate!(
0, r#"echo "$()"'"#,
0, "\n"
);
})(); })();
} }