From 2ae77c199882e0ec21329062876372725296ade1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=C3=A4schle?= Date: Tue, 10 Aug 2021 16:04:42 +0200 Subject: [PATCH] v.scanner: fix escaped backslash after string interpolation (#11118) --- vlib/v/scanner/scanner.v | 27 +++++++++++++-------- vlib/v/tests/string_escape_backslash_test.v | 5 ++++ 2 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 vlib/v/tests/string_escape_backslash_test.v diff --git a/vlib/v/scanner/scanner.v b/vlib/v/scanner/scanner.v index 5a69281932..0e775244e9 100644 --- a/vlib/v/scanner/scanner.v +++ b/vlib/v/scanner/scanner.v @@ -19,6 +19,7 @@ const ( num_sep = `_` b_lf = 10 b_cr = 13 + backslash = `\\` ) pub struct Scanner { @@ -1111,7 +1112,7 @@ fn (mut s Scanner) ident_string() string { } s.is_inside_string = false mut u_escapes_pos := []int{} // pos list of \uXXXX - slash := `\\` + mut backslash_count := if start_char == scanner.backslash { 1 } else { 0 } for { s.pos++ if s.pos >= s.text.len { @@ -1120,10 +1121,12 @@ fn (mut s Scanner) ident_string() string { } c := s.text[s.pos] prevc := s.text[s.pos - 1] + if c == scanner.backslash { + backslash_count++ + } // end of string - if c == s.quote - && (is_raw || prevc != slash || (prevc == slash && s.text[s.pos - 2] == slash)) { - // handle '123\\' slash at the end + if c == s.quote && (is_raw || backslash_count % 2 == 0) { + // handle '123\\' backslash at the end break } if c == s.inter_quote && (s.is_inter_start || s.is_enclosed_inter) { @@ -1136,22 +1139,22 @@ fn (mut s Scanner) ident_string() string { s.inc_line_number() } // Don't allow \0 - if c == `0` && s.pos > 2 && prevc == slash { + if c == `0` && s.pos > 2 && prevc == scanner.backslash { if (s.pos < s.text.len - 1 && s.text[s.pos + 1].is_digit()) - || s.count_symbol_before(s.pos - 1, slash) % 2 == 0 { + || s.count_symbol_before(s.pos - 1, scanner.backslash) % 2 == 0 { } else if !is_cstr && !is_raw { s.error(r'cannot use `\0` (NULL character) in the string literal') } } // Don't allow \x00 if c == `0` && s.pos > 5 && s.expect('\\x0', s.pos - 3) { - if s.count_symbol_before(s.pos - 3, slash) % 2 == 0 { + if s.count_symbol_before(s.pos - 3, scanner.backslash) % 2 == 0 { } else if !is_cstr && !is_raw { s.error(r'cannot use `\x00` (NULL character) in the string literal') } } // Escape `\x` `\u` - if prevc == slash && !is_raw && !is_cstr && s.count_symbol_before(s.pos - 2, slash) % 2 == 0 { + if backslash_count % 2 == 1 && !is_raw && !is_cstr { // Escape `\x` if c == `x` && (s.text[s.pos + 1] == s.quote || !s.text[s.pos + 1].is_hex_digit()) { s.error(r'`\x` used with no following hex digits') @@ -1168,7 +1171,8 @@ fn (mut s Scanner) ident_string() string { } } // ${var} (ignore in vfmt mode) (skip \$) - if prevc == `$` && c == `{` && !is_raw && s.count_symbol_before(s.pos - 2, slash) % 2 == 0 { + if prevc == `$` && c == `{` && !is_raw + && s.count_symbol_before(s.pos - 2, scanner.backslash) % 2 == 0 { s.is_inside_string = true s.is_enclosed_inter = true // so that s.pos points to $ at the next step @@ -1177,12 +1181,15 @@ fn (mut s Scanner) ident_string() string { } // $var if prevc == `$` && util.is_name_char(c) && !is_raw - && s.count_symbol_before(s.pos - 2, slash) % 2 == 0 { + && s.count_symbol_before(s.pos - 2, scanner.backslash) % 2 == 0 { s.is_inside_string = true s.is_inter_start = true s.pos -= 2 break } + if c != scanner.backslash { + backslash_count = 0 + } } mut lit := '' mut end := s.pos diff --git a/vlib/v/tests/string_escape_backslash_test.v b/vlib/v/tests/string_escape_backslash_test.v new file mode 100644 index 0000000000..73e0fbeba4 --- /dev/null +++ b/vlib/v/tests/string_escape_backslash_test.v @@ -0,0 +1,5 @@ +fn test_escaped_backslash_after_string_interpolation() { + test := 'test' + a := "\\\"$test\\\"" + assert a == '\\"test\\"' +}