diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:20:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:20:39 +0000 |
commit | 1376c5a617be5c25655d0d7cb63e3beaa5a6e026 (patch) | |
tree | 3bb8d61aee02bc7a15eab3f36e3b921afc2075d0 /compiler/rustc_parse_format/src | |
parent | Releasing progress-linux version 1.69.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-1376c5a617be5c25655d0d7cb63e3beaa5a6e026.tar.xz rustc-1376c5a617be5c25655d0d7cb63e3beaa5a6e026.zip |
Merging upstream version 1.70.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_parse_format/src')
-rw-r--r-- | compiler/rustc_parse_format/src/lib.rs | 54 |
1 files changed, 45 insertions, 9 deletions
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 8a3cedfee..7de84db21 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -14,6 +14,7 @@ // We want to be able to build this crate with a stable compiler, so no // `#![feature]` attributes should be added. +use rustc_lexer::unescape; pub use Alignment::*; pub use Count::*; pub use Piece::*; @@ -234,8 +235,10 @@ pub struct Parser<'a> { last_opening_brace: Option<InnerSpan>, /// Whether the source string is comes from `println!` as opposed to `format!` or `print!` append_newline: bool, - /// Whether this formatting string is a literal or it comes from a macro. - pub is_literal: bool, + /// Whether this formatting string was written directly in the source. This controls whether we + /// can use spans to refer into it and give better error messages. + /// N.B: This does _not_ control whether implicit argument captures can be used. + pub is_source_literal: bool, /// Start position of the current line. cur_line_start: usize, /// Start and end byte offset of every line of the format string. Excludes @@ -262,7 +265,7 @@ impl<'a> Iterator for Parser<'a> { } else { let arg = self.argument(lbrace_end); if let Some(rbrace_pos) = self.must_consume('}') { - if self.is_literal { + if self.is_source_literal { let lbrace_byte_pos = self.to_span_index(pos); let rbrace_byte_pos = self.to_span_index(rbrace_pos); @@ -302,7 +305,7 @@ impl<'a> Iterator for Parser<'a> { _ => Some(String(self.string(pos))), } } else { - if self.is_literal { + if self.is_source_literal { let span = self.span(self.cur_line_start, self.input.len()); if self.line_spans.last() != Some(&span) { self.line_spans.push(span); @@ -322,8 +325,8 @@ impl<'a> Parser<'a> { append_newline: bool, mode: ParseMode, ) -> Parser<'a> { - let input_string_kind = find_width_map_from_snippet(snippet, style); - let (width_map, is_literal) = match input_string_kind { + let input_string_kind = find_width_map_from_snippet(s, snippet, style); + let (width_map, is_source_literal) = match input_string_kind { InputStringKind::Literal { width_mappings } => (width_mappings, true), InputStringKind::NotALiteral => (Vec::new(), false), }; @@ -339,7 +342,7 @@ impl<'a> Parser<'a> { width_map, last_opening_brace: None, append_newline, - is_literal, + is_source_literal, cur_line_start: 0, line_spans: vec![], } @@ -532,13 +535,13 @@ impl<'a> Parser<'a> { '{' | '}' => { return &self.input[start..pos]; } - '\n' if self.is_literal => { + '\n' if self.is_source_literal => { self.line_spans.push(self.span(self.cur_line_start, pos)); self.cur_line_start = pos + 1; self.cur.next(); } _ => { - if self.is_literal && pos == self.cur_line_start && c.is_whitespace() { + if self.is_source_literal && pos == self.cur_line_start && c.is_whitespace() { self.cur_line_start = pos + c.len_utf8(); } self.cur.next(); @@ -890,6 +893,7 @@ impl<'a> Parser<'a> { /// written code (code snippet) and the `InternedString` that gets processed in the `Parser` /// in order to properly synthesise the intra-string `Span`s for error diagnostics. fn find_width_map_from_snippet( + input: &str, snippet: Option<string::String>, str_style: Option<usize>, ) -> InputStringKind { @@ -902,8 +906,27 @@ fn find_width_map_from_snippet( return InputStringKind::Literal { width_mappings: Vec::new() }; } + // Strip quotes. let snippet = &snippet[1..snippet.len() - 1]; + // Macros like `println` add a newline at the end. That technically doesn't make them "literals" anymore, but it's fine + // since we will never need to point our spans there, so we lie about it here by ignoring it. + // Since there might actually be newlines in the source code, we need to normalize away all trailing newlines. + // If we only trimmed it off the input, `format!("\n")` would cause a mismatch as here we they actually match up. + // Alternatively, we could just count the trailing newlines and only trim one from the input if they don't match up. + let input_no_nl = input.trim_end_matches('\n'); + let Some(unescaped) = unescape_string(snippet) else { + return InputStringKind::NotALiteral; + }; + + let unescaped_no_nl = unescaped.trim_end_matches('\n'); + + if unescaped_no_nl != input_no_nl { + // The source string that we're pointing at isn't our input, so spans pointing at it will be incorrect. + // This can for example happen with proc macros that respan generated literals. + return InputStringKind::NotALiteral; + } + let mut s = snippet.char_indices(); let mut width_mappings = vec![]; while let Some((pos, c)) = s.next() { @@ -986,6 +1009,19 @@ fn find_width_map_from_snippet( InputStringKind::Literal { width_mappings } } +fn unescape_string(string: &str) -> Option<string::String> { + let mut buf = string::String::new(); + let mut ok = true; + unescape::unescape_literal(string, unescape::Mode::Str, &mut |_, unescaped_char| { + match unescaped_char { + Ok(c) => buf.push(c), + Err(_) => ok = false, + } + }); + + ok.then_some(buf) +} + // Assert a reasonable size for `Piece` #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Piece<'_>, 16); |