]>
Commit | Line | Data |
---|---|---|
48663c56 XL |
1 | //! Code related to parsing literals. |
2 | ||
9c376795 | 3 | use crate::ast::{self, LitKind, MetaItemLit, StrStyle}; |
60c5eb7d | 4 | use crate::token::{self, Token}; |
49aad941 | 5 | use rustc_lexer::unescape::{ |
c0240ec0 | 6 | byte_from_char, unescape_byte, unescape_char, unescape_mixed, unescape_unicode, MixedUnit, Mode, |
49aad941 | 7 | }; |
dfeec247 XL |
8 | use rustc_span::symbol::{kw, sym, Symbol}; |
9 | use rustc_span::Span; | |
9c376795 | 10 | use std::{ascii, fmt, str}; |
31ef2f64 | 11 | use tracing::debug; |
9c376795 FG |
12 | |
13 | // Escapes a string, represented as a symbol. Reuses the original symbol, | |
14 | // avoiding interning, if no changes are required. | |
15 | pub fn escape_string_symbol(symbol: Symbol) -> Symbol { | |
16 | let s = symbol.as_str(); | |
17 | let escaped = s.escape_default().to_string(); | |
18 | if s == escaped { symbol } else { Symbol::intern(&escaped) } | |
19 | } | |
20 | ||
21 | // Escapes a char. | |
22 | pub fn escape_char_symbol(ch: char) -> Symbol { | |
23 | let s: String = ch.escape_default().map(Into::<char>::into).collect(); | |
24 | Symbol::intern(&s) | |
25 | } | |
26 | ||
27 | // Escapes a byte string. | |
28 | pub fn escape_byte_str_symbol(bytes: &[u8]) -> Symbol { | |
29 | let s = bytes.escape_ascii().to_string(); | |
30 | Symbol::intern(&s) | |
31 | } | |
48663c56 | 32 | |
487cf647 | 33 | #[derive(Debug)] |
60c5eb7d | 34 | pub enum LitError { |
c620b35d FG |
35 | InvalidSuffix(Symbol), |
36 | InvalidIntSuffix(Symbol), | |
37 | InvalidFloatSuffix(Symbol), | |
38 | NonDecimalFloat(u32), // u32 is the base | |
39 | IntTooLarge(u32), // u32 is the base | |
dc9dc135 XL |
40 | } |
41 | ||
48663c56 | 42 | impl LitKind { |
dc9dc135 | 43 | /// Converts literal token into a semantic literal. |
f2b60f7d | 44 | pub fn from_token_lit(lit: token::Lit) -> Result<LitKind, LitError> { |
dc9dc135 | 45 | let token::Lit { kind, symbol, suffix } = lit; |
c620b35d FG |
46 | if let Some(suffix) = suffix |
47 | && !kind.may_have_suffix() | |
48 | { | |
49 | return Err(LitError::InvalidSuffix(suffix)); | |
48663c56 XL |
50 | } |
51 | ||
c0240ec0 FG |
52 | // For byte/char/string literals, chars and escapes have already been |
53 | // checked in the lexer (in `cook_lexer_literal`). So we can assume all | |
54 | // chars and escapes are valid here. | |
dc9dc135 XL |
55 | Ok(match kind { |
56 | token::Bool => { | |
e1599b0c | 57 | assert!(symbol.is_bool_lit()); |
dc9dc135 | 58 | LitKind::Bool(symbol == kw::True) |
48663c56 | 59 | } |
dfeec247 | 60 | token::Byte => { |
a2a8927a | 61 | return unescape_byte(symbol.as_str()) |
dfeec247 | 62 | .map(LitKind::Byte) |
c0240ec0 | 63 | .map_err(|_| panic!("failed to unescape byte literal")); |
dfeec247 XL |
64 | } |
65 | token::Char => { | |
a2a8927a | 66 | return unescape_char(symbol.as_str()) |
dfeec247 | 67 | .map(LitKind::Char) |
c0240ec0 | 68 | .map_err(|_| panic!("failed to unescape char literal")); |
dfeec247 | 69 | } |
48663c56 XL |
70 | |
71 | // There are some valid suffixes for integer and float literals, | |
72 | // so all the handling is done internally. | |
dc9dc135 XL |
73 | token::Integer => return integer_lit(symbol, suffix), |
74 | token::Float => return float_lit(symbol, suffix), | |
48663c56 | 75 | |
dc9dc135 | 76 | token::Str => { |
48663c56 | 77 | // If there are no characters requiring special treatment we can |
dc9dc135 | 78 | // reuse the symbol from the token. Otherwise, we must generate a |
48663c56 | 79 | // new symbol because the string in the LitKind is different to the |
dc9dc135 XL |
80 | // string in the token. |
81 | let s = symbol.as_str(); | |
4b012472 FG |
82 | // Vanilla strings are so common we optimize for the common case where no chars |
83 | // requiring special behaviour are present. | |
c0240ec0 | 84 | let symbol = if s.contains('\\') { |
5e7ed085 | 85 | let mut buf = String::with_capacity(s.len()); |
5e7ed085 | 86 | // Force-inlining here is aggressive but the closure is |
c0240ec0 FG |
87 | // called on every char in the string, so it can be hot in |
88 | // programs with many long strings containing escapes. | |
89 | unescape_unicode( | |
487cf647 | 90 | s, |
5e7ed085 FG |
91 | Mode::Str, |
92 | &mut #[inline(always)] | |
c0240ec0 | 93 | |_, c| match c { |
5e7ed085 FG |
94 | Ok(c) => buf.push(c), |
95 | Err(err) => { | |
c0240ec0 | 96 | assert!(!err.is_fatal(), "failed to unescape string literal") |
f9f354fc | 97 | } |
5e7ed085 FG |
98 | }, |
99 | ); | |
5e7ed085 FG |
100 | Symbol::intern(&buf) |
101 | } else { | |
102 | symbol | |
103 | }; | |
dc9dc135 | 104 | LitKind::Str(symbol, ast::StrStyle::Cooked) |
48663c56 | 105 | } |
dc9dc135 | 106 | token::StrRaw(n) => { |
c0240ec0 | 107 | // Raw strings have no escapes so no work is needed here. |
dc9dc135 | 108 | LitKind::Str(symbol, ast::StrStyle::Raw(n)) |
48663c56 | 109 | } |
dc9dc135 XL |
110 | token::ByteStr => { |
111 | let s = symbol.as_str(); | |
48663c56 | 112 | let mut buf = Vec::with_capacity(s.len()); |
c0240ec0 | 113 | unescape_unicode(s, Mode::ByteStr, &mut |_, c| match c { |
487cf647 FG |
114 | Ok(c) => buf.push(byte_from_char(c)), |
115 | Err(err) => { | |
c0240ec0 | 116 | assert!(!err.is_fatal(), "failed to unescape string literal") |
f9f354fc | 117 | } |
48663c56 | 118 | }); |
9c376795 | 119 | LitKind::ByteStr(buf.into(), StrStyle::Cooked) |
48663c56 | 120 | } |
9c376795 | 121 | token::ByteStrRaw(n) => { |
c0240ec0 FG |
122 | // Raw strings have no escapes so we can convert the symbol |
123 | // directly to a `Lrc<u8>`. | |
124 | let buf = symbol.as_str().to_owned().into_bytes(); | |
125 | LitKind::ByteStr(buf.into(), StrStyle::Raw(n)) | |
dfeec247 | 126 | } |
49aad941 FG |
127 | token::CStr => { |
128 | let s = symbol.as_str(); | |
129 | let mut buf = Vec::with_capacity(s.len()); | |
c0240ec0 FG |
130 | unescape_mixed(s, Mode::CStr, &mut |_span, c| match c { |
131 | Ok(MixedUnit::Char(c)) => { | |
49aad941 FG |
132 | buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes()) |
133 | } | |
c0240ec0 | 134 | Ok(MixedUnit::HighByte(b)) => buf.push(b), |
49aad941 | 135 | Err(err) => { |
c0240ec0 | 136 | assert!(!err.is_fatal(), "failed to unescape C string literal") |
49aad941 FG |
137 | } |
138 | }); | |
49aad941 FG |
139 | buf.push(0); |
140 | LitKind::CStr(buf.into(), StrStyle::Cooked) | |
141 | } | |
142 | token::CStrRaw(n) => { | |
c0240ec0 FG |
143 | // Raw strings have no escapes so we can convert the symbol |
144 | // directly to a `Lrc<u8>` after appending the terminating NUL | |
145 | // char. | |
146 | let mut buf = symbol.as_str().to_owned().into_bytes(); | |
49aad941 FG |
147 | buf.push(0); |
148 | LitKind::CStr(buf.into(), StrStyle::Raw(n)) | |
149 | } | |
c620b35d | 150 | token::Err(guar) => LitKind::Err(guar), |
48663c56 XL |
151 | }) |
152 | } | |
9c376795 | 153 | } |
48663c56 | 154 | |
9c376795 FG |
155 | impl fmt::Display for LitKind { |
156 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
157 | match *self { | |
158 | LitKind::Byte(b) => { | |
159 | let b: String = ascii::escape_default(b).map(Into::<char>::into).collect(); | |
160 | write!(f, "b'{b}'")?; | |
48663c56 | 161 | } |
9c376795 FG |
162 | LitKind::Char(ch) => write!(f, "'{}'", escape_char_symbol(ch))?, |
163 | LitKind::Str(sym, StrStyle::Cooked) => write!(f, "\"{}\"", escape_string_symbol(sym))?, | |
164 | LitKind::Str(sym, StrStyle::Raw(n)) => write!( | |
165 | f, | |
166 | "r{delim}\"{string}\"{delim}", | |
167 | delim = "#".repeat(n as usize), | |
168 | string = sym | |
169 | )?, | |
170 | LitKind::ByteStr(ref bytes, StrStyle::Cooked) => { | |
171 | write!(f, "b\"{}\"", escape_byte_str_symbol(bytes))? | |
48663c56 | 172 | } |
9c376795 FG |
173 | LitKind::ByteStr(ref bytes, StrStyle::Raw(n)) => { |
174 | // Unwrap because raw byte string literals can only contain ASCII. | |
175 | let symbol = str::from_utf8(bytes).unwrap(); | |
176 | write!( | |
177 | f, | |
178 | "br{delim}\"{string}\"{delim}", | |
179 | delim = "#".repeat(n as usize), | |
180 | string = symbol | |
181 | )?; | |
48663c56 | 182 | } |
49aad941 FG |
183 | LitKind::CStr(ref bytes, StrStyle::Cooked) => { |
184 | write!(f, "c\"{}\"", escape_byte_str_symbol(bytes))? | |
185 | } | |
186 | LitKind::CStr(ref bytes, StrStyle::Raw(n)) => { | |
187 | // This can only be valid UTF-8. | |
188 | let symbol = str::from_utf8(bytes).unwrap(); | |
189 | write!(f, "cr{delim}\"{symbol}\"{delim}", delim = "#".repeat(n as usize),)?; | |
190 | } | |
48663c56 | 191 | LitKind::Int(n, ty) => { |
9c376795 FG |
192 | write!(f, "{n}")?; |
193 | match ty { | |
194 | ast::LitIntType::Unsigned(ty) => write!(f, "{}", ty.name())?, | |
195 | ast::LitIntType::Signed(ty) => write!(f, "{}", ty.name())?, | |
196 | ast::LitIntType::Unsuffixed => {} | |
197 | } | |
48663c56 XL |
198 | } |
199 | LitKind::Float(symbol, ty) => { | |
9c376795 FG |
200 | write!(f, "{symbol}")?; |
201 | match ty { | |
202 | ast::LitFloatType::Suffixed(ty) => write!(f, "{}", ty.name())?, | |
203 | ast::LitFloatType::Unsuffixed => {} | |
204 | } | |
48663c56 | 205 | } |
9c376795 | 206 | LitKind::Bool(b) => write!(f, "{}", if b { "true" } else { "false" })?, |
c620b35d | 207 | LitKind::Err(_) => { |
9c376795 FG |
208 | // This only shows up in places like `-Zunpretty=hir` output, so we |
209 | // don't bother to produce something useful. | |
210 | write!(f, "<bad-literal>")?; | |
48663c56 | 211 | } |
9c376795 | 212 | } |
dc9dc135 | 213 | |
9c376795 | 214 | Ok(()) |
48663c56 XL |
215 | } |
216 | } | |
217 | ||
487cf647 | 218 | impl MetaItemLit { |
9c376795 | 219 | /// Converts a token literal into a meta item literal. |
487cf647 | 220 | pub fn from_token_lit(token_lit: token::Lit, span: Span) -> Result<MetaItemLit, LitError> { |
9c376795 FG |
221 | Ok(MetaItemLit { |
222 | symbol: token_lit.symbol, | |
223 | suffix: token_lit.suffix, | |
224 | kind: LitKind::from_token_lit(token_lit)?, | |
225 | span, | |
226 | }) | |
227 | } | |
228 | ||
229 | /// Cheaply converts a meta item literal into a token literal. | |
230 | pub fn as_token_lit(&self) -> token::Lit { | |
231 | let kind = match self.kind { | |
232 | LitKind::Bool(_) => token::Bool, | |
233 | LitKind::Str(_, ast::StrStyle::Cooked) => token::Str, | |
234 | LitKind::Str(_, ast::StrStyle::Raw(n)) => token::StrRaw(n), | |
235 | LitKind::ByteStr(_, ast::StrStyle::Cooked) => token::ByteStr, | |
236 | LitKind::ByteStr(_, ast::StrStyle::Raw(n)) => token::ByteStrRaw(n), | |
49aad941 FG |
237 | LitKind::CStr(_, ast::StrStyle::Cooked) => token::CStr, |
238 | LitKind::CStr(_, ast::StrStyle::Raw(n)) => token::CStrRaw(n), | |
9c376795 FG |
239 | LitKind::Byte(_) => token::Byte, |
240 | LitKind::Char(_) => token::Char, | |
241 | LitKind::Int(..) => token::Integer, | |
242 | LitKind::Float(..) => token::Float, | |
c620b35d | 243 | LitKind::Err(guar) => token::Err(guar), |
9c376795 FG |
244 | }; |
245 | ||
246 | token::Lit::new(kind, self.symbol, self.suffix) | |
48663c56 XL |
247 | } |
248 | ||
487cf647 FG |
249 | /// Converts an arbitrary token into meta item literal. |
250 | pub fn from_token(token: &Token) -> Option<MetaItemLit> { | |
251 | token::Lit::from_token(token) | |
252 | .and_then(|token_lit| MetaItemLit::from_token_lit(token_lit, token.span).ok()) | |
48663c56 | 253 | } |
48663c56 XL |
254 | } |
255 | ||
dc9dc135 XL |
256 | fn strip_underscores(symbol: Symbol) -> Symbol { |
257 | // Do not allocate a new string unless necessary. | |
258 | let s = symbol.as_str(); | |
259 | if s.contains('_') { | |
260 | let mut s = s.to_string(); | |
261 | s.retain(|c| c != '_'); | |
262 | return Symbol::intern(&s); | |
263 | } | |
264 | symbol | |
265 | } | |
48663c56 | 266 | |
dfeec247 XL |
267 | fn filtered_float_lit( |
268 | symbol: Symbol, | |
269 | suffix: Option<Symbol>, | |
270 | base: u32, | |
271 | ) -> Result<LitKind, LitError> { | |
dc9dc135 XL |
272 | debug!("filtered_float_lit: {:?}, {:?}, {:?}", symbol, suffix, base); |
273 | if base != 10 { | |
274 | return Err(LitError::NonDecimalFloat(base)); | |
275 | } | |
276 | Ok(match suffix { | |
c620b35d | 277 | Some(suffix) => LitKind::Float( |
dfeec247 | 278 | symbol, |
c620b35d FG |
279 | ast::LitFloatType::Suffixed(match suffix { |
280 | sym::f16 => ast::FloatTy::F16, | |
dfeec247 XL |
281 | sym::f32 => ast::FloatTy::F32, |
282 | sym::f64 => ast::FloatTy::F64, | |
c620b35d FG |
283 | sym::f128 => ast::FloatTy::F128, |
284 | _ => return Err(LitError::InvalidFloatSuffix(suffix)), | |
dfeec247 XL |
285 | }), |
286 | ), | |
287 | None => LitKind::Float(symbol, ast::LitFloatType::Unsuffixed), | |
48663c56 XL |
288 | }) |
289 | } | |
48663c56 | 290 | |
dc9dc135 XL |
291 | fn float_lit(symbol: Symbol, suffix: Option<Symbol>) -> Result<LitKind, LitError> { |
292 | debug!("float_lit: {:?}, {:?}", symbol, suffix); | |
293 | filtered_float_lit(strip_underscores(symbol), suffix, 10) | |
48663c56 XL |
294 | } |
295 | ||
dc9dc135 XL |
296 | fn integer_lit(symbol: Symbol, suffix: Option<Symbol>) -> Result<LitKind, LitError> { |
297 | debug!("integer_lit: {:?}, {:?}", symbol, suffix); | |
298 | let symbol = strip_underscores(symbol); | |
299 | let s = symbol.as_str(); | |
48663c56 | 300 | |
e74abb32 XL |
301 | let base = match s.as_bytes() { |
302 | [b'0', b'x', ..] => 16, | |
303 | [b'0', b'o', ..] => 8, | |
304 | [b'0', b'b', ..] => 2, | |
305 | _ => 10, | |
306 | }; | |
48663c56 | 307 | |
dc9dc135 XL |
308 | let ty = match suffix { |
309 | Some(suf) => match suf { | |
310 | sym::isize => ast::LitIntType::Signed(ast::IntTy::Isize), | |
dfeec247 | 311 | sym::i8 => ast::LitIntType::Signed(ast::IntTy::I8), |
dc9dc135 XL |
312 | sym::i16 => ast::LitIntType::Signed(ast::IntTy::I16), |
313 | sym::i32 => ast::LitIntType::Signed(ast::IntTy::I32), | |
314 | sym::i64 => ast::LitIntType::Signed(ast::IntTy::I64), | |
315 | sym::i128 => ast::LitIntType::Signed(ast::IntTy::I128), | |
316 | sym::usize => ast::LitIntType::Unsigned(ast::UintTy::Usize), | |
dfeec247 | 317 | sym::u8 => ast::LitIntType::Unsigned(ast::UintTy::U8), |
dc9dc135 XL |
318 | sym::u16 => ast::LitIntType::Unsigned(ast::UintTy::U16), |
319 | sym::u32 => ast::LitIntType::Unsigned(ast::UintTy::U32), | |
320 | sym::u64 => ast::LitIntType::Unsigned(ast::UintTy::U64), | |
321 | sym::u128 => ast::LitIntType::Unsigned(ast::UintTy::U128), | |
322 | // `1f64` and `2f32` etc. are valid float literals, and | |
323 | // `fxxx` looks more like an invalid float literal than invalid integer literal. | |
324 | _ if suf.as_str().starts_with('f') => return filtered_float_lit(symbol, suffix, base), | |
c620b35d | 325 | _ => return Err(LitError::InvalidIntSuffix(suf)), |
dfeec247 XL |
326 | }, |
327 | _ => ast::LitIntType::Unsuffixed, | |
dc9dc135 | 328 | }; |
48663c56 | 329 | |
dfeec247 | 330 | let s = &s[if base != 10 { 2 } else { 0 }..]; |
c620b35d FG |
331 | u128::from_str_radix(s, base) |
332 | .map(|i| LitKind::Int(i.into(), ty)) | |
333 | .map_err(|_| LitError::IntTooLarge(base)) | |
48663c56 | 334 | } |