summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_ast/src/util/literal.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_ast/src/util/literal.rs')
-rw-r--r--compiler/rustc_ast/src/util/literal.rs146
1 files changed, 96 insertions, 50 deletions
diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs
index f6f186b51..74b842ac9 100644
--- a/compiler/rustc_ast/src/util/literal.rs
+++ b/compiler/rustc_ast/src/util/literal.rs
@@ -1,11 +1,31 @@
//! Code related to parsing literals.
-use crate::ast::{self, LitKind, MetaItemLit};
+use crate::ast::{self, LitKind, MetaItemLit, StrStyle};
use crate::token::{self, Token};
use rustc_lexer::unescape::{byte_from_char, unescape_byte, unescape_char, unescape_literal, Mode};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
-use std::ascii;
+use std::{ascii, fmt, str};
+
+// Escapes a string, represented as a symbol. Reuses the original symbol,
+// avoiding interning, if no changes are required.
+pub fn escape_string_symbol(symbol: Symbol) -> Symbol {
+ let s = symbol.as_str();
+ let escaped = s.escape_default().to_string();
+ if s == escaped { symbol } else { Symbol::intern(&escaped) }
+}
+
+// Escapes a char.
+pub fn escape_char_symbol(ch: char) -> Symbol {
+ let s: String = ch.escape_default().map(Into::<char>::into).collect();
+ Symbol::intern(&s)
+}
+
+// Escapes a byte string.
+pub fn escape_byte_str_symbol(bytes: &[u8]) -> Symbol {
+ let s = bytes.escape_ascii().to_string();
+ Symbol::intern(&s)
+}
#[derive(Debug)]
pub enum LitError {
@@ -14,7 +34,7 @@ pub enum LitError {
InvalidIntSuffix,
InvalidFloatSuffix,
NonDecimalFloat(u32),
- IntTooLarge,
+ IntTooLarge(u32),
}
impl LitKind {
@@ -115,9 +135,9 @@ impl LitKind {
}
});
error?;
- LitKind::ByteStr(buf.into())
+ LitKind::ByteStr(buf.into(), StrStyle::Cooked)
}
- token::ByteStrRaw(_) => {
+ token::ByteStrRaw(n) => {
let s = symbol.as_str();
let bytes = if s.contains('\r') {
let mut buf = Vec::with_capacity(s.len());
@@ -136,69 +156,95 @@ impl LitKind {
symbol.to_string().into_bytes()
};
- LitKind::ByteStr(bytes.into())
+ LitKind::ByteStr(bytes.into(), StrStyle::Raw(n))
}
token::Err => LitKind::Err,
})
}
+}
- /// Attempts to recover a token from semantic literal.
- /// This function is used when the original token doesn't exist (e.g. the literal is created
- /// by an AST-based macro) or unavailable (e.g. from HIR pretty-printing).
- pub fn to_token_lit(&self) -> token::Lit {
- let (kind, symbol, suffix) = match *self {
- LitKind::Str(symbol, ast::StrStyle::Cooked) => {
- // Don't re-intern unless the escaped string is different.
- let s = symbol.as_str();
- let escaped = s.escape_default().to_string();
- let symbol = if s == escaped { symbol } else { Symbol::intern(&escaped) };
- (token::Str, symbol, None)
- }
- LitKind::Str(symbol, ast::StrStyle::Raw(n)) => (token::StrRaw(n), symbol, None),
- LitKind::ByteStr(ref bytes) => {
- let string = bytes.escape_ascii().to_string();
- (token::ByteStr, Symbol::intern(&string), None)
+impl fmt::Display for LitKind {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ LitKind::Byte(b) => {
+ let b: String = ascii::escape_default(b).map(Into::<char>::into).collect();
+ write!(f, "b'{b}'")?;
}
- LitKind::Byte(byte) => {
- let string: String = ascii::escape_default(byte).map(Into::<char>::into).collect();
- (token::Byte, Symbol::intern(&string), None)
+ LitKind::Char(ch) => write!(f, "'{}'", escape_char_symbol(ch))?,
+ LitKind::Str(sym, StrStyle::Cooked) => write!(f, "\"{}\"", escape_string_symbol(sym))?,
+ LitKind::Str(sym, StrStyle::Raw(n)) => write!(
+ f,
+ "r{delim}\"{string}\"{delim}",
+ delim = "#".repeat(n as usize),
+ string = sym
+ )?,
+ LitKind::ByteStr(ref bytes, StrStyle::Cooked) => {
+ write!(f, "b\"{}\"", escape_byte_str_symbol(bytes))?
}
- LitKind::Char(ch) => {
- let string: String = ch.escape_default().map(Into::<char>::into).collect();
- (token::Char, Symbol::intern(&string), None)
+ LitKind::ByteStr(ref bytes, StrStyle::Raw(n)) => {
+ // Unwrap because raw byte string literals can only contain ASCII.
+ let symbol = str::from_utf8(bytes).unwrap();
+ write!(
+ f,
+ "br{delim}\"{string}\"{delim}",
+ delim = "#".repeat(n as usize),
+ string = symbol
+ )?;
}
LitKind::Int(n, ty) => {
- let suffix = match ty {
- ast::LitIntType::Unsigned(ty) => Some(ty.name()),
- ast::LitIntType::Signed(ty) => Some(ty.name()),
- ast::LitIntType::Unsuffixed => None,
- };
- (token::Integer, sym::integer(n), suffix)
+ write!(f, "{n}")?;
+ match ty {
+ ast::LitIntType::Unsigned(ty) => write!(f, "{}", ty.name())?,
+ ast::LitIntType::Signed(ty) => write!(f, "{}", ty.name())?,
+ ast::LitIntType::Unsuffixed => {}
+ }
}
LitKind::Float(symbol, ty) => {
- let suffix = match ty {
- ast::LitFloatType::Suffixed(ty) => Some(ty.name()),
- ast::LitFloatType::Unsuffixed => None,
- };
- (token::Float, symbol, suffix)
+ write!(f, "{symbol}")?;
+ match ty {
+ ast::LitFloatType::Suffixed(ty) => write!(f, "{}", ty.name())?,
+ ast::LitFloatType::Unsuffixed => {}
+ }
}
- LitKind::Bool(value) => {
- let symbol = if value { kw::True } else { kw::False };
- (token::Bool, symbol, None)
+ LitKind::Bool(b) => write!(f, "{}", if b { "true" } else { "false" })?,
+ LitKind::Err => {
+ // This only shows up in places like `-Zunpretty=hir` output, so we
+ // don't bother to produce something useful.
+ write!(f, "<bad-literal>")?;
}
- // This only shows up in places like `-Zunpretty=hir` output, so we
- // don't bother to produce something useful.
- LitKind::Err => (token::Err, Symbol::intern("<bad-literal>"), None),
- };
+ }
- token::Lit::new(kind, symbol, suffix)
+ Ok(())
}
}
impl MetaItemLit {
- /// Converts token literal into a meta item literal.
+ /// Converts a token literal into a meta item literal.
pub fn from_token_lit(token_lit: token::Lit, span: Span) -> Result<MetaItemLit, LitError> {
- Ok(MetaItemLit { token_lit, kind: LitKind::from_token_lit(token_lit)?, span })
+ Ok(MetaItemLit {
+ symbol: token_lit.symbol,
+ suffix: token_lit.suffix,
+ kind: LitKind::from_token_lit(token_lit)?,
+ span,
+ })
+ }
+
+ /// Cheaply converts a meta item literal into a token literal.
+ pub fn as_token_lit(&self) -> token::Lit {
+ let kind = match self.kind {
+ LitKind::Bool(_) => token::Bool,
+ LitKind::Str(_, ast::StrStyle::Cooked) => token::Str,
+ LitKind::Str(_, ast::StrStyle::Raw(n)) => token::StrRaw(n),
+ LitKind::ByteStr(_, ast::StrStyle::Cooked) => token::ByteStr,
+ LitKind::ByteStr(_, ast::StrStyle::Raw(n)) => token::ByteStrRaw(n),
+ LitKind::Byte(_) => token::Byte,
+ LitKind::Char(_) => token::Char,
+ LitKind::Int(..) => token::Integer,
+ LitKind::Float(..) => token::Float,
+ LitKind::Err => token::Err,
+ };
+
+ token::Lit::new(kind, self.symbol, self.suffix)
}
/// Converts an arbitrary token into meta item literal.
@@ -287,6 +333,6 @@ fn integer_lit(symbol: Symbol, suffix: Option<Symbol>) -> Result<LitKind, LitErr
// but these kinds of errors are already reported by the lexer.
let from_lexer =
base < 10 && s.chars().any(|c| c.to_digit(10).map_or(false, |d| d >= base));
- if from_lexer { LitError::LexerError } else { LitError::IntTooLarge }
+ if from_lexer { LitError::LexerError } else { LitError::IntTooLarge(base) }
})
}