]> git.lizzy.rs Git - rust.git/blob - src/librustc_parse/lexer/unescape_error_reporting.rs
Rollup merge of #69581 - RalfJung:align_to_mut, r=Centril
[rust.git] / src / librustc_parse / lexer / unescape_error_reporting.rs
1 //! Utilities for rendering escape sequence errors as diagnostics.
2
3 use std::iter::once;
4 use std::ops::Range;
5
6 use rustc_errors::{Applicability, Handler};
7 use rustc_lexer::unescape::{EscapeError, Mode};
8 use rustc_span::{BytePos, Span};
9
10 pub(crate) fn emit_unescape_error(
11     handler: &Handler,
12     // interior part of the literal, without quotes
13     lit: &str,
14     // full span of the literal, including quotes
15     span_with_quotes: Span,
16     mode: Mode,
17     // range of the error inside `lit`
18     range: Range<usize>,
19     error: EscapeError,
20 ) {
21     log::debug!(
22         "emit_unescape_error: {:?}, {:?}, {:?}, {:?}, {:?}",
23         lit,
24         span_with_quotes,
25         mode,
26         range,
27         error
28     );
29     let span = {
30         let Range { start, end } = range;
31         let (start, end) = (start as u32, end as u32);
32         let lo = span_with_quotes.lo() + BytePos(start + 1);
33         let hi = lo + BytePos(end - start);
34         span_with_quotes.with_lo(lo).with_hi(hi)
35     };
36     let last_char = || {
37         let c = lit[range.clone()].chars().rev().next().unwrap();
38         let span = span.with_lo(span.hi() - BytePos(c.len_utf8() as u32));
39         (c, span)
40     };
41     match error {
42         EscapeError::LoneSurrogateUnicodeEscape => {
43             handler
44                 .struct_span_err(span, "invalid unicode character escape")
45                 .help("unicode escape must not be a surrogate")
46                 .emit();
47         }
48         EscapeError::OutOfRangeUnicodeEscape => {
49             handler
50                 .struct_span_err(span, "invalid unicode character escape")
51                 .help("unicode escape must be at most 10FFFF")
52                 .emit();
53         }
54         EscapeError::MoreThanOneChar => {
55             let msg = if mode.is_bytes() {
56                 "if you meant to write a byte string literal, use double quotes"
57             } else {
58                 "if you meant to write a `str` literal, use double quotes"
59             };
60
61             handler
62                 .struct_span_err(
63                     span_with_quotes,
64                     "character literal may only contain one codepoint",
65                 )
66                 .span_suggestion(
67                     span_with_quotes,
68                     msg,
69                     format!("\"{}\"", lit),
70                     Applicability::MachineApplicable,
71                 )
72                 .emit();
73         }
74         EscapeError::EscapeOnlyChar => {
75             let (c, _span) = last_char();
76
77             let mut msg = if mode.is_bytes() {
78                 "byte constant must be escaped: "
79             } else {
80                 "character constant must be escaped: "
81             }
82             .to_string();
83             push_escaped_char(&mut msg, c);
84
85             handler.span_err(span, msg.as_str())
86         }
87         EscapeError::BareCarriageReturn => {
88             let msg = if mode.in_double_quotes() {
89                 "bare CR not allowed in string, use \\r instead"
90             } else {
91                 "character constant must be escaped: \\r"
92             };
93             handler.span_err(span, msg);
94         }
95         EscapeError::BareCarriageReturnInRawString => {
96             assert!(mode.in_double_quotes());
97             let msg = "bare CR not allowed in raw string";
98             handler.span_err(span, msg);
99         }
100         EscapeError::InvalidEscape => {
101             let (c, span) = last_char();
102
103             let label =
104                 if mode.is_bytes() { "unknown byte escape" } else { "unknown character escape" };
105             let mut msg = label.to_string();
106             msg.push_str(": ");
107             push_escaped_char(&mut msg, c);
108
109             let mut diag = handler.struct_span_err(span, msg.as_str());
110             diag.span_label(span, label);
111             if c == '{' || c == '}' && !mode.is_bytes() {
112                 diag.help(
113                     "if used in a formatting string, \
114                            curly braces are escaped with `{{` and `}}`",
115                 );
116             } else if c == '\r' {
117                 diag.help(
118                     "this is an isolated carriage return; \
119                            consider checking your editor and version control settings",
120                 );
121             }
122             diag.emit();
123         }
124         EscapeError::TooShortHexEscape => {
125             handler.span_err(span, "numeric character escape is too short")
126         }
127         EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => {
128             let (c, span) = last_char();
129
130             let mut msg = if error == EscapeError::InvalidCharInHexEscape {
131                 "invalid character in numeric character escape: "
132             } else {
133                 "invalid character in unicode escape: "
134             }
135             .to_string();
136             push_escaped_char(&mut msg, c);
137
138             handler.span_err(span, msg.as_str())
139         }
140         EscapeError::NonAsciiCharInByte => {
141             assert!(mode.is_bytes());
142             let (_c, span) = last_char();
143             handler.span_err(
144                 span,
145                 "byte constant must be ASCII. \
146                                     Use a \\xHH escape for a non-ASCII byte",
147             )
148         }
149         EscapeError::NonAsciiCharInByteString => {
150             assert!(mode.is_bytes());
151             let (_c, span) = last_char();
152             handler.span_err(span, "raw byte string must be ASCII")
153         }
154         EscapeError::OutOfRangeHexEscape => handler.span_err(
155             span,
156             "this form of character escape may only be used \
157                                     with characters in the range [\\x00-\\x7f]",
158         ),
159         EscapeError::LeadingUnderscoreUnicodeEscape => {
160             let (_c, span) = last_char();
161             handler.span_err(span, "invalid start of unicode escape")
162         }
163         EscapeError::OverlongUnicodeEscape => {
164             handler.span_err(span, "overlong unicode escape (must have at most 6 hex digits)")
165         }
166         EscapeError::UnclosedUnicodeEscape => {
167             handler.span_err(span, "unterminated unicode escape (needed a `}`)")
168         }
169         EscapeError::NoBraceInUnicodeEscape => {
170             let msg = "incorrect unicode escape sequence";
171             let mut diag = handler.struct_span_err(span, msg);
172
173             let mut suggestion = "\\u{".to_owned();
174             let mut suggestion_len = 0;
175             let (c, char_span) = last_char();
176             let chars = once(c).chain(lit[range.end..].chars());
177             for c in chars.take(6).take_while(|c| c.is_digit(16)) {
178                 suggestion.push(c);
179                 suggestion_len += c.len_utf8();
180             }
181
182             if suggestion_len > 0 {
183                 suggestion.push('}');
184                 let lo = char_span.lo();
185                 let hi = lo + BytePos(suggestion_len as u32);
186                 diag.span_suggestion(
187                     span.with_lo(lo).with_hi(hi),
188                     "format of unicode escape sequences uses braces",
189                     suggestion,
190                     Applicability::MaybeIncorrect,
191                 );
192             } else {
193                 diag.span_label(span, msg);
194                 diag.help("format of unicode escape sequences is `\\u{...}`");
195             }
196
197             diag.emit();
198         }
199         EscapeError::UnicodeEscapeInByte => handler.span_err(
200             span,
201             "unicode escape sequences cannot be used \
202                                     as a byte or in a byte string",
203         ),
204         EscapeError::EmptyUnicodeEscape => {
205             handler.span_err(span, "empty unicode escape (must have at least 1 hex digit)")
206         }
207         EscapeError::ZeroChars => handler.span_err(span, "empty character literal"),
208         EscapeError::LoneSlash => handler.span_err(span, "invalid trailing slash in literal"),
209     }
210 }
211
212 /// Pushes a character to a message string for error reporting
213 pub(crate) fn push_escaped_char(msg: &mut String, c: char) {
214     match c {
215         '\u{20}'..='\u{7e}' => {
216             // Don't escape \, ' or " for user-facing messages
217             msg.push(c);
218         }
219         _ => {
220             msg.extend(c.escape_default());
221         }
222     }
223 }