]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/parse/unescape_error_reporting.rs
Auto merge of #61492 - RalfJung:const-qualif-comments, r=eddyb
[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::BareCarriageReturnInRawString => {
84             assert!(mode.in_double_quotes());
85             let msg = "bare CR not allowed in raw string";
86             handler.span_err(span, msg);
87         }
88         EscapeError::InvalidEscape => {
89             let (c, span) = last_char();
90
91             let label = if mode.is_bytes() {
92                 "unknown byte escape"
93             } else {
94                 "unknown character escape"
95             };
96             let mut msg = label.to_string();
97             msg.push_str(": ");
98             push_escaped_char(&mut msg, c);
99
100             let mut diag = handler.struct_span_err(span, msg.as_str());
101             diag.span_label(span, label);
102             if c == '{' || c == '}' && !mode.is_bytes() {
103                 diag.help("if used in a formatting string, \
104                            curly braces are escaped with `{{` and `}}`");
105             } else if c == '\r' {
106                 diag.help("this is an isolated carriage return; \
107                            consider checking your editor and version control settings");
108             }
109             diag.emit();
110         }
111         EscapeError::TooShortHexEscape => {
112             handler.span_err(span, "numeric character escape is too short")
113         }
114         EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => {
115             let (c, span) = last_char();
116
117             let mut msg = if error == EscapeError::InvalidCharInHexEscape {
118                 "invalid character in numeric character escape: "
119             } else {
120                 "invalid character in unicode escape: "
121             }.to_string();
122             push_escaped_char(&mut msg, c);
123
124             handler.span_err(span, msg.as_str())
125         }
126         EscapeError::NonAsciiCharInByte => {
127             assert!(mode.is_bytes());
128             let (_c, span) = last_char();
129             handler.span_err(span, "byte constant must be ASCII. \
130                                     Use a \\xHH escape for a non-ASCII byte")
131         }
132         EscapeError::NonAsciiCharInByteString => {
133             assert!(mode.is_bytes());
134             let (_c, span) = last_char();
135             handler.span_err(span, "raw byte string must be ASCII")
136         }
137         EscapeError::OutOfRangeHexEscape => {
138             handler.span_err(span, "this form of character escape may only be used \
139                                     with characters in the range [\\x00-\\x7f]")
140         }
141         EscapeError::LeadingUnderscoreUnicodeEscape => {
142             let (_c, span) = last_char();
143             handler.span_err(span, "invalid start of unicode escape")
144         }
145         EscapeError::OverlongUnicodeEscape => {
146             handler.span_err(span, "overlong unicode escape (must have at most 6 hex digits)")
147         }
148         EscapeError::UnclosedUnicodeEscape => {
149             handler.span_err(span, "unterminated unicode escape (needed a `}`)")
150         }
151         EscapeError::NoBraceInUnicodeEscape => {
152             let msg = "incorrect unicode escape sequence";
153             let mut diag = handler.struct_span_err(span, msg);
154
155             let mut suggestion = "\\u{".to_owned();
156             let mut suggestion_len = 0;
157             let (c, char_span) = last_char();
158             let chars = once(c).chain(lit[range.end..].chars());
159             for c in chars.take(6).take_while(|c| c.is_digit(16)) {
160                 suggestion.push(c);
161                 suggestion_len += c.len_utf8();
162             }
163
164             if suggestion_len > 0 {
165                 suggestion.push('}');
166                 let lo = char_span.lo();
167                 let hi = lo + BytePos(suggestion_len as u32);
168                 diag.span_suggestion(
169                     span.with_lo(lo).with_hi(hi),
170                     "format of unicode escape sequences uses braces",
171                     suggestion,
172                     Applicability::MaybeIncorrect,
173                 );
174             } else {
175                 diag.span_label(span, msg);
176                 diag.help(
177                     "format of unicode escape sequences is `\\u{...}`",
178                 );
179             }
180
181             diag.emit();
182         }
183         EscapeError::UnicodeEscapeInByte => {
184             handler.span_err(span, "unicode escape sequences cannot be used \
185                                     as a byte or in a byte string")
186         }
187         EscapeError::EmptyUnicodeEscape => {
188             handler.span_err(span, "empty unicode escape (must have at least 1 hex digit)")
189         }
190         EscapeError::ZeroChars => {
191             handler.span_err(span, "empty character literal")
192         }
193         EscapeError::LoneSlash => {
194             panic!("lexer accepted unterminated literal with trailing slash")
195         }
196     }
197 }
198
199 /// Pushes a character to a message string for error reporting
200 pub(crate) fn push_escaped_char(msg: &mut String, c: char) {
201     match c {
202         '\u{20}'..='\u{7e}' => {
203             // Don't escape \, ' or " for user-facing messages
204             msg.push(c);
205         }
206         _ => {
207             msg.extend(c.escape_default());
208         }
209     }
210 }