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