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