]> git.proxmox.com Git - rustc.git/blob - src/librustc_parse/lexer/unescape_error_reporting.rs
New upstream version 1.47.0~beta.2+dfsg1
[rustc.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 tracing::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 }