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