From 4c43819d329d35b620deb6a0276912a0a631a620 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Sat, 28 Sep 2024 13:27:10 +0200 Subject: [PATCH] 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. --- src/parse_util.rs | 58 +++++++++++++++++++---------------------- src/tests/parse_util.rs | 4 +++ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/parse_util.rs b/src/parse_util.rs index 1369290ff..c898db131 100644 --- a/src/parse_util.rs +++ b/src/parse_util.rs @@ -941,42 +941,38 @@ fn indent_leaf(&mut self, range: SourceRange) { } fn indent_string_part(&mut self, range: Range, is_double_quoted: bool) { - let mut done = range.start; + let mut start = range.start; let mut quoted = false; - { - if is_double_quoted { - match quote_end(self.src, range.start, '"') { - Some(q_end) => { - // We may be (in) a multi-line string, so don't indent. - done = q_end + 1; - } - None => quoted = true, + if is_double_quoted { + match quote_end(self.src, range.start, '"') { + Some(q_end) => { + // We may be (in) a multi-line string, so don't indent. + start = q_end + 1; } + None => quoted = true, } + } + let mut done = start; + if !quoted { let part = &self.src[done..range.end]; - if !quoted { - let mut callback = |offset| { - if !quoted { - // Quote open event. Indent unquoted part, including the opening quote. - self.indents[done..range.start + offset + 1].fill(self.indent); - done = range.start + offset + 1; - } else { - // 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. - let first_line_length = self.src[range.start..range.start + offset] - .chars() - .take_while(|&c| c != '\n') - .count(); - self.indents[range.start..range.start + first_line_length] - .fill(self.indent); - done = range.start + offset; - } - quoted = !quoted; - }; - for _token in - Tokenizer::with_quote_events(part, TOK_ACCEPT_UNFINISHED, &mut callback) - { + let mut callback = |offset| { + if !quoted { + // Quote open event. Indent unquoted part, including the opening quote. + self.indents[done..start + offset + 1].fill(self.indent); + done = start + offset + 1; + } else { + // 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. + let first_line_length = self.src[start..start + offset] + .chars() + .take_while(|&c| c != '\n') + .count(); + self.indents[start..start + first_line_length].fill(self.indent); + done = start + offset; } + quoted = !quoted; + }; + for _token in Tokenizer::with_quote_events(part, TOK_ACCEPT_UNFINISHED, &mut callback) { } } if !quoted { diff --git a/src/tests/parse_util.rs b/src/tests/parse_util.rs index d91a10b8c..a1d67ad1a 100644 --- a/src/tests/parse_util.rs +++ b/src/tests/parse_util.rs @@ -435,5 +435,9 @@ macro_rules! validate { 0, "\n) line4", 0, "\nline5\"", ); + validate!( + 0, r#"echo "$()"'"#, + 0, "\n" + ); })(); }