diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 2cbab90aa61..5d9d0a5feca 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1332,7 +1332,7 @@ pub enum ExprKind { /// A unary operation (e.g., `!x`, `*x`). Unary(UnOp, P), /// A literal (e.g., `1`, `"foo"`). - Lit(Lit), + Lit(token::Lit), /// A cast (e.g., `foo as f64`). Cast(P, P), /// A type ascription (e.g., `42: usize`). @@ -1698,16 +1698,12 @@ pub struct StrLit { } impl StrLit { - pub fn as_lit(&self) -> Lit { + pub fn as_token_lit(&self) -> token::Lit { let token_kind = match self.style { StrStyle::Cooked => token::Str, StrStyle::Raw(n) => token::StrRaw(n), }; - Lit { - token_lit: token::Lit::new(token_kind, self.symbol, self.suffix), - span: self.span, - kind: LitKind::Str(self.symbol_unescaped, self.style), - } + token::Lit::new(token_kind, self.symbol, self.suffix) } } @@ -1733,9 +1729,10 @@ pub enum LitFloatType { Unsuffixed, } -/// Literal kind. -/// -/// E.g., `"foo"`, `42`, `12.34`, or `bool`. +/// Note that the entire literal (including the suffix) is considered when +/// deciding the `LitKind`. This means that float literals like `1f32` are +/// classified by this type as `Float`. This is different to `token::LitKind` +/// which does *not* consider the suffix. #[derive(Clone, Encodable, Decodable, Debug, Hash, Eq, PartialEq, HashStable_Generic)] pub enum LitKind { /// A string literal (`"foo"`). The symbol is unescaped, and so may differ @@ -1749,10 +1746,11 @@ pub enum LitKind { Char(char), /// An integer literal (`1`). Int(u128, LitIntType), - /// A float literal (`1f64` or `1E10f64`). Stored as a symbol rather than - /// `f64` so that `LitKind` can impl `Eq` and `Hash`. + /// A float literal (`1.0`, `1f64` or `1E10f64`). The pre-suffix part is + /// stored as a symbol rather than `f64` so that `LitKind` can impl `Eq` + /// and `Hash`. Float(Symbol, LitFloatType), - /// A boolean literal. + /// A boolean literal (`true`, `false`). Bool(bool), /// Placeholder for a literal that wasn't well-formed in some way. Err, diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 990f4f8f132..07f982b7e86 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -533,7 +533,7 @@ impl MetaItemKind { MetaItemKind::NameValue(lit) => { let expr = P(ast::Expr { id: ast::DUMMY_NODE_ID, - kind: ast::ExprKind::Lit(lit.clone()), + kind: ast::ExprKind::Lit(lit.token_lit.clone()), span: lit.span, attrs: ast::AttrVec::new(), tokens: None, @@ -605,7 +605,7 @@ impl MetaItemKind { MetaItemKind::name_value_from_tokens(&mut inner_tokens.into_trees()) } Some(TokenTree::Token(token, _)) => { - Lit::from_token(&token).ok().map(MetaItemKind::NameValue) + Lit::from_token(&token).map(MetaItemKind::NameValue) } _ => None, } @@ -618,8 +618,10 @@ impl MetaItemKind { MetaItemKind::list_from_tokens(tokens.clone()) } MacArgs::Delimited(..) => None, - MacArgs::Eq(_, MacArgsEq::Ast(expr)) => match &expr.kind { - ast::ExprKind::Lit(lit) => Some(MetaItemKind::NameValue(lit.clone())), + MacArgs::Eq(_, MacArgsEq::Ast(expr)) => match expr.kind { + ast::ExprKind::Lit(token_lit) => Some(MetaItemKind::NameValue( + Lit::from_token_lit(token_lit, expr.span).expect("token_lit in from_mac_args"), + )), _ => None, }, MacArgs::Eq(_, MacArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())), @@ -668,7 +670,7 @@ impl NestedMetaItem { { match tokens.peek() { Some(TokenTree::Token(token, _)) - if let Ok(lit) = Lit::from_token(token) => + if let Some(lit) = Lit::from_token(token) => { tokens.next(); return Some(NestedMetaItem::Literal(lit)); diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index f6aac0b55f1..e0ff690e766 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -59,13 +59,17 @@ pub enum Delimiter { Invisible, } +// Note that the suffix is *not* considered when deciding the `LitKind` in this +// type. This means that float literals like `1f32` are classified by this type +// as `Int`. Only upon conversion to `ast::LitKind` will such a literal be +// given the `Float` kind. #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum LitKind { Bool, // AST only, must never appear in a `Token` Byte, Char, - Integer, - Float, + Integer, // e.g. `1`, `1u8`, `1f32` + Float, // e.g. `1.`, `1.0`, `1e3f32` Str, StrRaw(u8), // raw string delimited by `n` hash symbols ByteStr, @@ -81,6 +85,42 @@ pub struct Lit { pub suffix: Option, } +impl Lit { + pub fn new(kind: LitKind, symbol: Symbol, suffix: Option) -> Lit { + Lit { kind, symbol, suffix } + } + + /// Returns `true` if this is semantically a float literal. This includes + /// ones like `1f32` that have an `Integer` kind but a float suffix. + pub fn is_semantic_float(&self) -> bool { + match self.kind { + LitKind::Float => true, + LitKind::Integer => match self.suffix { + Some(sym) => sym == sym::f32 || sym == sym::f64, + None => false, + }, + _ => false, + } + } + + /// Keep this in sync with `Token::can_begin_literal_or_bool` excluding unary negation. + pub fn from_token(token: &Token) -> Option { + match token.uninterpolate().kind { + Ident(name, false) if name.is_bool_lit() => { + Some(Lit::new(Bool, name, None)) + } + Literal(token_lit) => Some(token_lit), + Interpolated(ref nt) + if let NtExpr(expr) | NtLiteral(expr) = &**nt + && let ast::ExprKind::Lit(token_lit) = expr.kind => + { + Some(token_lit.clone()) + } + _ => None, + } + } +} + impl fmt::Display for Lit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Lit { kind, symbol, suffix } = *self; @@ -139,12 +179,6 @@ impl LitKind { } } -impl Lit { - pub fn new(kind: LitKind, symbol: Symbol, suffix: Option) -> Lit { - Lit { kind, symbol, suffix } - } -} - pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool { let ident_token = Token::new(Ident(name, is_raw), span); diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index e267f8cd100..db2ac9626af 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -8,8 +8,8 @@ use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; use std::ascii; +#[derive(Debug)] pub enum LitError { - NotLiteral, LexerError, InvalidSuffix, InvalidIntSuffix, @@ -202,27 +202,10 @@ impl Lit { Ok(Lit { token_lit, kind: LitKind::from_token_lit(token_lit)?, span }) } - /// Converts arbitrary token into an AST literal. - /// - /// Keep this in sync with `Token::can_begin_literal_or_bool` excluding unary negation. - pub fn from_token(token: &Token) -> Result { - let lit = match token.uninterpolate().kind { - token::Ident(name, false) if name.is_bool_lit() => { - token::Lit::new(token::Bool, name, None) - } - token::Literal(lit) => lit, - token::Interpolated(ref nt) => { - if let token::NtExpr(expr) | token::NtLiteral(expr) = &**nt - && let ast::ExprKind::Lit(lit) = &expr.kind - { - return Ok(lit.clone()); - } - return Err(LitError::NotLiteral); - } - _ => return Err(LitError::NotLiteral), - }; - - Lit::from_token_lit(lit, token.span) + /// Converts an arbitrary token into an AST literal. + pub fn from_token(token: &Token) -> Option { + token::Lit::from_token(token) + .and_then(|token_lit| Lit::from_token_lit(token_lit, token.span).ok()) } /// Attempts to recover an AST literal from semantic literal. diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index a4ae493af86..b4a8283c4a0 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -14,6 +14,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::definitions::DefPathData; +use rustc_session::errors::report_lit_error; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; use rustc_span::symbol::{sym, Ident}; use rustc_span::DUMMY_SP; @@ -84,8 +85,15 @@ impl<'hir> LoweringContext<'_, 'hir> { let ohs = self.lower_expr(ohs); hir::ExprKind::Unary(op, ohs) } - ExprKind::Lit(ref l) => { - hir::ExprKind::Lit(respan(self.lower_span(l.span), l.kind.clone())) + ExprKind::Lit(token_lit) => { + let lit_kind = match LitKind::from_token_lit(token_lit) { + Ok(lit_kind) => lit_kind, + Err(err) => { + report_lit_error(&self.tcx.sess.parse_sess, err, token_lit, e.span); + LitKind::Err + } + }; + hir::ExprKind::Lit(respan(self.lower_span(e.span), lit_kind)) } ExprKind::IncludedBytes(ref bytes) => hir::ExprKind::Lit(respan( self.lower_span(e.span), diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index ff29d15f1b5..cc93774d846 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -959,8 +959,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { MacArgs::Eq(eq_span, MacArgsEq::Ast(ref expr)) => { // In valid code the value always ends up as a single literal. Otherwise, a dummy // literal suffices because the error is handled elsewhere. - let lit = if let ExprKind::Lit(lit) = &expr.kind { - lit.clone() + let lit = if let ExprKind::Lit(token_lit) = expr.kind { + match Lit::from_token_lit(token_lit, expr.span) { + Ok(lit) => lit, + Err(_err) => Lit { + token_lit: token::Lit::new(token::LitKind::Err, kw::Empty, None), + kind: LitKind::Err, + span: DUMMY_SP, + }, + } } else { Lit { token_lit: token::Lit::new(token::LitKind::Err, kw::Empty, None), diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index b87c6f78d72..5f01f555b30 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -373,8 +373,12 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } fn print_literal(&mut self, lit: &ast::Lit) { - self.maybe_print_comment(lit.span.lo()); - self.word(lit.token_lit.to_string()) + self.print_token_literal(lit.token_lit, lit.span) + } + + fn print_token_literal(&mut self, token_lit: token::Lit, span: Span) { + self.maybe_print_comment(span.lo()); + self.word(token_lit.to_string()) } fn print_string(&mut self, st: &str, style: ast::StrStyle) { @@ -1735,7 +1739,7 @@ impl<'a> State<'a> { } ast::Extern::Explicit(abi, _) => { self.word_nbsp("extern"); - self.print_literal(&abi.as_lit()); + self.print_token_literal(abi.as_token_lit(), abi.span); self.nbsp(); } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 930276242c3..86f1d6bfecd 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -319,8 +319,8 @@ impl<'a> State<'a> { ast::ExprKind::AddrOf(k, m, ref expr) => { self.print_expr_addr_of(k, m, expr); } - ast::ExprKind::Lit(ref lit) => { - self.print_literal(lit); + ast::ExprKind::Lit(token_lit) => { + self.print_token_literal(token_lit, expr.span); } ast::ExprKind::IncludedBytes(ref bytes) => { let lit = ast::Lit::from_included_bytes(bytes, expr.span); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 159853c9e24..9c4425701e0 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -207,7 +207,7 @@ impl<'a> State<'a> { s.word("extern"); })); if let Some(abi) = nmod.abi { - self.print_literal(&abi.as_lit()); + self.print_token_literal(abi.as_token_lit(), abi.span); self.nbsp(); } self.bopen(); diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index a1051d990b1..a34c17a4258 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -172,7 +172,11 @@ pub fn parse_asm_args<'a>( // If it can't possibly expand to a string, provide diagnostics here to include other // things it could have been. match template.kind { - ast::ExprKind::Lit(ast::Lit { kind: ast::LitKind::Str(..), .. }) => {} + ast::ExprKind::Lit(token_lit) + if matches!( + token_lit.kind, + token::LitKind::Str | token::LitKind::StrRaw(_) + ) => {} ast::ExprKind::MacCall(..) => {} _ => { let errstr = if is_global_asm { diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs index 01454d0e98e..d579616ad1b 100644 --- a/compiler/rustc_builtin_macros/src/concat.rs +++ b/compiler/rustc_builtin_macros/src/concat.rs @@ -1,6 +1,7 @@ use rustc_ast as ast; use rustc_ast::tokenstream::TokenStream; use rustc_expand::base::{self, DummyResult}; +use rustc_session::errors::report_lit_error; use rustc_span::symbol::Symbol; use std::string::String; @@ -18,28 +19,28 @@ pub fn expand_concat( let mut has_errors = false; for e in es { match e.kind { - ast::ExprKind::Lit(ref lit) => match lit.kind { - ast::LitKind::Str(ref s, _) | ast::LitKind::Float(ref s, _) => { + ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) { + Ok(ast::LitKind::Str(ref s, _) | ast::LitKind::Float(ref s, _)) => { accumulator.push_str(s.as_str()); } - ast::LitKind::Char(c) => { + Ok(ast::LitKind::Char(c)) => { accumulator.push(c); } - ast::LitKind::Int( - i, - ast::LitIntType::Unsigned(_) - | ast::LitIntType::Signed(_) - | ast::LitIntType::Unsuffixed, - ) => { + Ok(ast::LitKind::Int(i, _)) => { accumulator.push_str(&i.to_string()); } - ast::LitKind::Bool(b) => { + Ok(ast::LitKind::Bool(b)) => { accumulator.push_str(&b.to_string()); } - ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..) => { + Ok(ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..)) => { cx.span_err(e.span, "cannot concatenate a byte string literal"); + has_errors = true; } - ast::LitKind::Err => { + Ok(ast::LitKind::Err) => { + has_errors = true; + } + Err(err) => { + report_lit_error(&cx.sess.parse_sess, err, token_lit, e.span); has_errors = true; } }, diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs index 4886ca786a5..87658e60e9d 100644 --- a/compiler/rustc_builtin_macros/src/concat_bytes.rs +++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs @@ -2,18 +2,21 @@ use rustc_ast as ast; use rustc_ast::{ptr::P, tokenstream::TokenStream}; use rustc_errors::Applicability; use rustc_expand::base::{self, DummyResult}; +use rustc_span::Span; /// Emits errors for literal expressions that are invalid inside and outside of an array. -fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P, is_nested: bool) { - let ast::ExprKind::Lit(lit) = &expr.kind else { - unreachable!(); - }; - match lit.kind { - ast::LitKind::Char(_) => { - let mut err = cx.struct_span_err(expr.span, "cannot concatenate character literals"); - if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) { +fn invalid_type_err( + cx: &mut base::ExtCtxt<'_>, + token_lit: ast::token::Lit, + span: Span, + is_nested: bool, +) { + match ast::LitKind::from_token_lit(token_lit) { + Ok(ast::LitKind::Char(_)) => { + let mut err = cx.struct_span_err(span, "cannot concatenate character literals"); + if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) { err.span_suggestion( - expr.span, + span, "try using a byte character", format!("b{}", snippet), Applicability::MachineApplicable, @@ -21,13 +24,13 @@ fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P, is_ne .emit(); } } - ast::LitKind::Str(_, _) => { - let mut err = cx.struct_span_err(expr.span, "cannot concatenate string literals"); + Ok(ast::LitKind::Str(_, _)) => { + let mut err = cx.struct_span_err(span, "cannot concatenate string literals"); // suggestion would be invalid if we are nested if !is_nested { - if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) { + if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) { err.span_suggestion( - expr.span, + span, "try using a byte string", format!("b{}", snippet), Applicability::MachineApplicable, @@ -36,18 +39,18 @@ fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P, is_ne } err.emit(); } - ast::LitKind::Float(_, _) => { - cx.span_err(expr.span, "cannot concatenate float literals"); + Ok(ast::LitKind::Float(_, _)) => { + cx.span_err(span, "cannot concatenate float literals"); } - ast::LitKind::Bool(_) => { - cx.span_err(expr.span, "cannot concatenate boolean literals"); + Ok(ast::LitKind::Bool(_)) => { + cx.span_err(span, "cannot concatenate boolean literals"); } - ast::LitKind::Err => {} - ast::LitKind::Int(_, _) if !is_nested => { - let mut err = cx.struct_span_err(expr.span, "cannot concatenate numeric literals"); - if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) { + Ok(ast::LitKind::Err) => {} + Ok(ast::LitKind::Int(_, _)) if !is_nested => { + let mut err = cx.struct_span_err(span, "cannot concatenate numeric literals"); + if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) { err.span_suggestion( - expr.span, + span, "try wrapping the number in an array", format!("[{}]", snippet), Applicability::MachineApplicable, @@ -55,15 +58,15 @@ fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P, is_ne } err.emit(); } - ast::LitKind::Int( + Ok(ast::LitKind::Int( val, ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8), - ) => { + )) => { assert!(val > u8::MAX.into()); // must be an error - cx.span_err(expr.span, "numeric literal is out of bounds"); + cx.span_err(span, "numeric literal is out of bounds"); } - ast::LitKind::Int(_, _) => { - cx.span_err(expr.span, "numeric literal is not a `u8`"); + Ok(ast::LitKind::Int(_, _)) => { + cx.span_err(span, "numeric literal is not a `u8`"); } _ => unreachable!(), } @@ -83,14 +86,14 @@ fn handle_array_element( *has_errors = true; None } - ast::ExprKind::Lit(ref lit) => match lit.kind { - ast::LitKind::Int( + ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) { + Ok(ast::LitKind::Int( val, ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8), - ) if val <= u8::MAX.into() => Some(val as u8), + )) if val <= u8::MAX.into() => Some(val as u8), - ast::LitKind::Byte(val) => Some(val), - ast::LitKind::ByteStr(_) => { + Ok(ast::LitKind::Byte(val)) => Some(val), + Ok(ast::LitKind::ByteStr(_)) => { if !*has_errors { cx.struct_span_err(expr.span, "cannot concatenate doubly nested array") .note("byte strings are treated as arrays of bytes") @@ -102,7 +105,7 @@ fn handle_array_element( } _ => { if !*has_errors { - invalid_type_err(cx, expr, true); + invalid_type_err(cx, token_lit, expr.span, true); } *has_errors = true; None @@ -148,9 +151,9 @@ pub fn expand_concat_bytes( } } ast::ExprKind::Repeat(ref expr, ref count) => { - if let ast::ExprKind::Lit(ast::Lit { - kind: ast::LitKind::Int(count_val, _), .. - }) = count.value.kind + if let ast::ExprKind::Lit(token_lit) = count.value.kind + && let Ok(ast::LitKind::Int(count_val, _)) = + ast::LitKind::from_token_lit(token_lit) { if let Some(elem) = handle_array_element(cx, &mut has_errors, &mut missing_literals, expr) @@ -163,16 +166,16 @@ pub fn expand_concat_bytes( cx.span_err(count.value.span, "repeat count is not a positive number"); } } - ast::ExprKind::Lit(ref lit) => match lit.kind { - ast::LitKind::Byte(val) => { + ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) { + Ok(ast::LitKind::Byte(val)) => { accumulator.push(val); } - ast::LitKind::ByteStr(ref bytes) => { + Ok(ast::LitKind::ByteStr(ref bytes)) => { accumulator.extend_from_slice(&bytes); } _ => { if !has_errors { - invalid_type_err(cx, &e, false); + invalid_type_err(cx, token_lit, e.span, false); } has_errors = true; } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 1294f1e17d4..95fff929d46 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1226,10 +1226,10 @@ pub fn expr_to_spanned_string<'a>( let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr(); Err(match expr.kind { - ast::ExprKind::Lit(ref l) => match l.kind { - ast::LitKind::Str(s, style) => return Ok((s, style, expr.span)), - ast::LitKind::ByteStr(_) => { - let mut err = cx.struct_span_err(l.span, err_msg); + ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) { + Ok(ast::LitKind::Str(s, style)) => return Ok((s, style, expr.span)), + Ok(ast::LitKind::ByteStr(_)) => { + let mut err = cx.struct_span_err(expr.span, err_msg); let span = expr.span.shrink_to_lo(); err.span_suggestion( span.with_hi(span.lo() + BytePos(1)), @@ -1239,8 +1239,9 @@ pub fn expr_to_spanned_string<'a>( ); Some((err, true)) } - ast::LitKind::Err => None, - _ => Some((cx.struct_span_err(l.span, err_msg), false)), + Ok(ast::LitKind::Err) => None, + Err(_) => None, + _ => Some((cx.struct_span_err(expr.span, err_msg), false)), }, ast::ExprKind::Err => None, _ => Some((cx.struct_span_err(expr.span, err_msg), false)), diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 0952e65cfee..8aa72e142f8 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -334,8 +334,8 @@ impl<'a> ExtCtxt<'a> { } fn expr_lit(&self, span: Span, lit_kind: ast::LitKind) -> P { - let lit = ast::Lit::from_lit_kind(lit_kind, span); - self.expr(span, ast::ExprKind::Lit(lit)) + let token_lit = lit_kind.to_token_lit(); + self.expr(span, ast::ExprKind::Lit(token_lit)) } pub fn expr_usize(&self, span: Span, i: usize) -> P { diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index a929f6cb0a5..2e832deeecd 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -516,14 +516,14 @@ impl server::TokenStream for Rustc<'_, '_> { // We don't use `TokenStream::from_ast` as the tokenstream currently cannot // be recovered in the general case. match &expr.kind { - ast::ExprKind::Lit(l) if l.token_lit.kind == token::Bool => { + ast::ExprKind::Lit(token_lit) if token_lit.kind == token::Bool => { Ok(tokenstream::TokenStream::token_alone( - token::Ident(l.token_lit.symbol, false), - l.span, + token::Ident(token_lit.symbol, false), + expr.span, )) } - ast::ExprKind::Lit(l) => { - Ok(tokenstream::TokenStream::token_alone(token::Literal(l.token_lit), l.span)) + ast::ExprKind::Lit(token_lit) => { + Ok(tokenstream::TokenStream::token_alone(token::Literal(*token_lit), expr.span)) } ast::ExprKind::IncludedBytes(bytes) => { let lit = ast::Lit::from_included_bytes(bytes, expr.span); @@ -533,16 +533,13 @@ impl server::TokenStream for Rustc<'_, '_> { )) } ast::ExprKind::Unary(ast::UnOp::Neg, e) => match &e.kind { - ast::ExprKind::Lit(l) => match l.token_lit { + ast::ExprKind::Lit(token_lit) => match token_lit { token::Lit { kind: token::Integer | token::Float, .. } => { Ok(Self::TokenStream::from_iter([ // FIXME: The span of the `-` token is lost when // parsing, so we cannot faithfully recover it here. tokenstream::TokenTree::token_alone(token::BinOp(token::Minus), e.span), - tokenstream::TokenTree::token_alone( - token::Literal(l.token_lit), - l.span, - ), + tokenstream::TokenTree::token_alone(token::Literal(*token_lit), e.span), ])) } _ => Err(()), diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index d4140cb295f..3fbabbc6344 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -167,11 +167,15 @@ pub enum DocStyle { Inner, } +// Note that the suffix is *not* considered when deciding the `LiteralKind` in +// this type. This means that float literals like `1f32` are classified by this +// type as `Int`. (Compare against `rustc_ast::token::LitKind` and +// `rustc_ast::ast::LitKind.) #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum LiteralKind { - /// "12_u8", "0o100", "0b120i99" + /// "12_u8", "0o100", "0b120i99", "1f32". Int { base: Base, empty_int: bool }, - /// "12.34f32", "0b100.100" + /// "12.34f32", "1e3", but not "1f32`. Float { base: Base, empty_exponent: bool }, /// "'a'", "'\\'", "'''", "';" Char { terminated: bool }, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 27c04d82811..253ff1f793c 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -98,9 +98,10 @@ fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr { impl EarlyLintPass for WhileTrue { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { if let ast::ExprKind::While(cond, _, label) = &e.kind - && let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind - && let ast::LitKind::Bool(true) = lit.kind - && !lit.span.from_expansion() + && let cond = pierce_parens(cond) + && let ast::ExprKind::Lit(token_lit) = cond.kind + && let token::Lit { kind: token::Bool, symbol: kw::True, .. } = token_lit + && !cond.span.from_expansion() { let condition_span = e.span.with_hi(cond.span.hi()); cx.struct_span_lint( diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs index 7e884e990ce..7106e75dba2 100644 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs @@ -123,23 +123,22 @@ impl EarlyLintPass for HiddenUnicodeCodepoints { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { // byte strings are already handled well enough by `EscapeError::NonAsciiCharInByteString` - let (text, span, padding) = match &expr.kind { - ast::ExprKind::Lit(ast::Lit { token_lit, kind, span }) => { + match &expr.kind { + ast::ExprKind::Lit(token_lit) => { let text = token_lit.symbol; if !contains_text_flow_control_chars(text.as_str()) { return; } - let padding = match kind { + let padding = match token_lit.kind { // account for `"` or `'` - ast::LitKind::Str(_, ast::StrStyle::Cooked) | ast::LitKind::Char(_) => 1, + ast::token::LitKind::Str | ast::token::LitKind::Char => 1, // account for `r###"` - ast::LitKind::Str(_, ast::StrStyle::Raw(val)) => *val as u32 + 2, + ast::token::LitKind::StrRaw(n) => n as u32 + 2, _ => return, }; - (text, span, padding) + self.lint_text_direction_codepoint(cx, text, expr.span, padding, true, "literal"); } - _ => return, + _ => {} }; - self.lint_text_direction_codepoint(cx, text, *span, padding, true, "literal"); } } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 2b17cea9794..724d92254a4 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -304,61 +304,6 @@ pub(crate) struct FloatLiteralRequiresIntegerPart { pub correct: String, } -#[derive(Diagnostic)] -#[diag(parser_invalid_int_literal_width)] -#[help] -pub(crate) struct InvalidIntLiteralWidth { - #[primary_span] - pub span: Span, - pub width: String, -} - -#[derive(Diagnostic)] -#[diag(parser_invalid_num_literal_base_prefix)] -#[note] -pub(crate) struct InvalidNumLiteralBasePrefix { - #[primary_span] - #[suggestion(applicability = "maybe-incorrect", code = "{fixed}")] - pub span: Span, - pub fixed: String, -} - -#[derive(Diagnostic)] -#[diag(parser_invalid_num_literal_suffix)] -#[help] -pub(crate) struct InvalidNumLiteralSuffix { - #[primary_span] - #[label] - pub span: Span, - pub suffix: String, -} - -#[derive(Diagnostic)] -#[diag(parser_invalid_float_literal_width)] -#[help] -pub(crate) struct InvalidFloatLiteralWidth { - #[primary_span] - pub span: Span, - pub width: String, -} - -#[derive(Diagnostic)] -#[diag(parser_invalid_float_literal_suffix)] -#[help] -pub(crate) struct InvalidFloatLiteralSuffix { - #[primary_span] - #[label] - pub span: Span, - pub suffix: String, -} - -#[derive(Diagnostic)] -#[diag(parser_int_literal_too_large)] -pub(crate) struct IntLiteralTooLarge { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(parser_missing_semicolon_before_array)] pub(crate) struct MissingSemicolonBeforeArray { @@ -740,41 +685,6 @@ pub(crate) struct InvalidInterpolatedExpression { pub span: Span, } -#[derive(Diagnostic)] -#[diag(parser_hexadecimal_float_literal_not_supported)] -pub(crate) struct HexadecimalFloatLiteralNotSupported { - #[primary_span] - #[label(parser_not_supported)] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(parser_octal_float_literal_not_supported)] -pub(crate) struct OctalFloatLiteralNotSupported { - #[primary_span] - #[label(parser_not_supported)] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(parser_binary_float_literal_not_supported)] -pub(crate) struct BinaryFloatLiteralNotSupported { - #[primary_span] - #[label(parser_not_supported)] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(parser_invalid_literal_suffix)] -pub(crate) struct InvalidLiteralSuffix { - #[primary_span] - #[label] - pub span: Span, - // FIXME(#100717) - pub kind: String, - pub suffix: Symbol, -} - #[derive(Diagnostic)] #[diag(parser_invalid_literal_suffix_on_tuple_index)] pub(crate) struct InvalidLiteralSuffixOnTupleIndex { diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 645262bd2f1..f027843e6b4 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -661,6 +661,7 @@ impl<'a> StringReader<'a> { prefix_len: u32, postfix_len: u32, ) -> (token::LitKind, Symbol) { + let mut has_fatal_err = false; let content_start = start + BytePos(prefix_len); let content_end = end - BytePos(postfix_len); let lit_content = self.str_from_to(content_start, content_end); @@ -672,6 +673,9 @@ impl<'a> StringReader<'a> { let lo = content_start + BytePos(start); let hi = lo + BytePos(end - start); let span = self.mk_sp(lo, hi); + if err.is_fatal() { + has_fatal_err = true; + } emit_unescape_error( &self.sess.span_diagnostic, lit_content, @@ -683,7 +687,14 @@ impl<'a> StringReader<'a> { ); } }); - (kind, Symbol::intern(lit_content)) + + // We normally exclude the quotes for the symbol, but for errors we + // include it because it results in clearer error messages. + if !has_fatal_err { + (kind, Symbol::intern(lit_content)) + } else { + (token::Err, self.symbol_from_to(start, end)) + } } } diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 9e45656946b..612accf3e3b 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -316,8 +316,8 @@ impl<'a> Parser<'a> { } pub(crate) fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> { - let lit = self.parse_lit()?; - debug!("checking if {:?} is unusuffixed", lit); + let lit = self.parse_ast_lit()?; + debug!("checking if {:?} is unsuffixed", lit); if !lit.kind.is_unsuffixed() { self.sess.emit_err(SuffixedLiteralInAttribute { span: lit.span }); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index b072573af23..c9629ea49e0 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -7,35 +7,30 @@ use super::{ }; use crate::errors::{ ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect, - BinaryFloatLiteralNotSupported, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct, - ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg, - DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedEqForLetExpr, - ExpectedExpressionFoundLet, FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, - FoundExprWouldBeStmt, HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition, - IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, IntLiteralTooLarge, + BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct, ComparisonInterpretedAsGeneric, + ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, + ExpectedElseBlock, ExpectedEqForLetExpr, ExpectedExpressionFoundLet, + FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt, + IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub, - InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth, - InvalidInterpolatedExpression, InvalidLiteralSuffix, InvalidLiteralSuffixOnTupleIndex, - InvalidLogicalOperator, InvalidLogicalOperatorSub, InvalidNumLiteralBasePrefix, - InvalidNumLiteralSuffix, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator, + InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator, + InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel, MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm, MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub, - OctalFloatLiteralNotSupported, OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields, + OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields, RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere, StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses, }; use crate::maybe_recover_from_interpolated_ty_qpath; - use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::Spacing; use rustc_ast::util::case::Case; use rustc_ast::util::classify; -use rustc_ast::util::literal::LitError; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; use rustc_ast::visit::Visitor; use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID}; @@ -47,7 +42,7 @@ use rustc_errors::{ Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult, StashKey, }; -use rustc_session::errors::ExprParenthesesNeeded; +use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded}; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::source_map::{self, Span, Spanned}; @@ -1415,9 +1410,9 @@ impl<'a> Parser<'a> { fn parse_lit_expr(&mut self) -> PResult<'a, P> { let lo = self.token.span; - match self.parse_opt_lit() { - Some(literal) => { - let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(literal)); + match self.parse_opt_token_lit() { + Some((token_lit, _)) => { + let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(token_lit)); self.maybe_recover_from_bad_qpath(expr) } None => self.try_macro_suggestion(), @@ -1548,7 +1543,7 @@ impl<'a> Parser<'a> { }) }); consume_colon = false; - Ok(self.mk_expr(lo, ExprKind::Lit(lit))) + Ok(self.mk_expr(lo, ExprKind::Lit(lit.token_lit))) } else if !ate_colon && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt)) { @@ -1625,9 +1620,9 @@ impl<'a> Parser<'a> { /// Emit an error when a char is parsed as a lifetime because of a missing quote pub(super) fn recover_unclosed_char( - &mut self, + &self, lifetime: Ident, - err: impl FnOnce(&mut Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>, + err: impl FnOnce(&Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>, ) -> ast::Lit { if let Some(mut diag) = self.sess.span_diagnostic.steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar) @@ -1649,9 +1644,10 @@ impl<'a> Parser<'a> { ) .emit(); } + let name = lifetime.without_first_quote().name; ast::Lit { - token_lit: token::Lit::new(token::LitKind::Char, lifetime.name, None), - kind: ast::LitKind::Char(lifetime.name.as_str().chars().next().unwrap_or('_')), + token_lit: token::Lit::new(token::LitKind::Char, name, None), + kind: ast::LitKind::Char(name.as_str().chars().next().unwrap_or('_')), span: lifetime.span, } } @@ -1765,7 +1761,7 @@ impl<'a> Parser<'a> { /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind, /// and returns `None` if the next token is not literal at all. pub fn parse_str_lit(&mut self) -> Result> { - match self.parse_opt_lit() { + match self.parse_opt_ast_lit() { Some(lit) => match lit.kind { ast::LitKind::Str(symbol_unescaped, style) => Ok(ast::StrLit { style, @@ -1780,41 +1776,47 @@ impl<'a> Parser<'a> { } } - pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> { - self.parse_opt_lit().ok_or(()).or_else(|()| { - if let token::Interpolated(inner) = &self.token.kind { - let expr = match inner.as_ref() { - token::NtExpr(expr) => Some(expr), - token::NtLiteral(expr) => Some(expr), - _ => None, - }; - if let Some(expr) = expr { - if matches!(expr.kind, ExprKind::Err) { - let mut err = InvalidInterpolatedExpression { span: self.token.span } - .into_diagnostic(&self.sess.span_diagnostic); - err.downgrade_to_delayed_bug(); - return Err(err); - } + fn handle_missing_lit(&mut self) -> PResult<'a, Lit> { + if let token::Interpolated(inner) = &self.token.kind { + let expr = match inner.as_ref() { + token::NtExpr(expr) => Some(expr), + token::NtLiteral(expr) => Some(expr), + _ => None, + }; + if let Some(expr) = expr { + if matches!(expr.kind, ExprKind::Err) { + let mut err = InvalidInterpolatedExpression { span: self.token.span } + .into_diagnostic(&self.sess.span_diagnostic); + err.downgrade_to_delayed_bug(); + return Err(err); } } - let token = self.token.clone(); - let err = |self_: &mut Self| { - let msg = format!("unexpected token: {}", super::token_descr(&token)); - self_.struct_span_err(token.span, &msg) - }; - // On an error path, eagerly consider a lifetime to be an unclosed character lit - if self.token.is_lifetime() { - let lt = self.expect_lifetime(); - Ok(self.recover_unclosed_char(lt.ident, err)) - } else { - Err(err(self)) - } - }) + } + let token = self.token.clone(); + let err = |self_: &Self| { + let msg = format!("unexpected token: {}", super::token_descr(&token)); + self_.struct_span_err(token.span, &msg) + }; + // On an error path, eagerly consider a lifetime to be an unclosed character lit + if self.token.is_lifetime() { + let lt = self.expect_lifetime(); + Ok(self.recover_unclosed_char(lt.ident, err)) + } else { + Err(err(self)) + } } - /// Matches `lit = true | false | token_lit`. - /// Returns `None` if the next token is not a literal. - pub(super) fn parse_opt_lit(&mut self) -> Option { + pub(super) fn parse_token_lit(&mut self) -> PResult<'a, (token::Lit, Span)> { + self.parse_opt_token_lit() + .ok_or(()) + .or_else(|()| self.handle_missing_lit().map(|lit| (lit.token_lit, lit.span))) + } + + pub(super) fn parse_ast_lit(&mut self) -> PResult<'a, Lit> { + self.parse_opt_ast_lit().ok_or(()).or_else(|()| self.handle_missing_lit()) + } + + fn recover_after_dot(&mut self) -> Option { let mut recovered = None; if self.token == token::Dot { // Attempt to recover `.4` as `0.4`. We don't currently have any syntax where @@ -1840,100 +1842,50 @@ impl<'a> Parser<'a> { } } - let token = recovered.as_ref().unwrap_or(&self.token); - match Lit::from_token(token) { - Ok(lit) => { - self.bump(); - Some(lit) - } - Err(LitError::NotLiteral) => None, - Err(err) => { - let span = token.span; - let token::Literal(lit) = token.kind else { - unreachable!(); - }; - self.bump(); - self.report_lit_error(err, lit, span); - // Pack possible quotes and prefixes from the original literal into - // the error literal's symbol so they can be pretty-printed faithfully. - let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None); - let symbol = Symbol::intern(&suffixless_lit.to_string()); - let lit = token::Lit::new(token::Err, symbol, lit.suffix); - Some(Lit::from_token_lit(lit, span).unwrap_or_else(|_| unreachable!())) - } - } + recovered } - fn report_lit_error(&self, err: LitError, lit: token::Lit, span: Span) { - // Checks if `s` looks like i32 or u1234 etc. - fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { - s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit()) - } + /// Matches `lit = true | false | token_lit`. + /// Returns `None` if the next token is not a literal. + pub(super) fn parse_opt_token_lit(&mut self) -> Option<(token::Lit, Span)> { + let recovered = self.recover_after_dot(); + let token = recovered.as_ref().unwrap_or(&self.token); + let span = token.span; + token::Lit::from_token(token).map(|token_lit| { + self.bump(); + (token_lit, span) + }) + } - // Try to lowercase the prefix if it's a valid base prefix. - fn fix_base_capitalisation(s: &str) -> Option { - if let Some(stripped) = s.strip_prefix('B') { - Some(format!("0b{stripped}")) - } else if let Some(stripped) = s.strip_prefix('O') { - Some(format!("0o{stripped}")) - } else if let Some(stripped) = s.strip_prefix('X') { - Some(format!("0x{stripped}")) - } else { - None - } - } - - let token::Lit { kind, suffix, .. } = lit; - match err { - // `NotLiteral` is not an error by itself, so we don't report - // it and give the parser opportunity to try something else. - LitError::NotLiteral => {} - // `LexerError` *is* an error, but it was already reported - // by lexer, so here we don't report it the second time. - LitError::LexerError => {} - LitError::InvalidSuffix => { - if let Some(suffix) = suffix { - self.sess.emit_err(InvalidLiteralSuffix { - span, - kind: format!("{}", kind.descr()), - suffix, - }); + /// Matches `lit = true | false | token_lit`. + /// Returns `None` if the next token is not a literal. + pub(super) fn parse_opt_ast_lit(&mut self) -> Option { + let recovered = self.recover_after_dot(); + let token = recovered.as_ref().unwrap_or(&self.token); + match token::Lit::from_token(token) { + Some(token_lit) => { + match Lit::from_token_lit(token_lit, token.span) { + Ok(lit) => { + self.bump(); + Some(lit) + } + Err(err) => { + let span = token.span; + let token::Literal(lit) = token.kind else { + unreachable!(); + }; + self.bump(); + report_lit_error(&self.sess, err, lit, span); + // Pack possible quotes and prefixes from the original literal into + // the error literal's symbol so they can be pretty-printed faithfully. + let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None); + let symbol = Symbol::intern(&suffixless_lit.to_string()); + let lit = token::Lit::new(token::Err, symbol, lit.suffix); + Some(Lit::from_token_lit(lit, span).unwrap_or_else(|_| unreachable!())) + } } } - LitError::InvalidIntSuffix => { - let suf = suffix.expect("suffix error with no suffix"); - let suf = suf.as_str(); - if looks_like_width_suffix(&['i', 'u'], &suf) { - // If it looks like a width, try to be helpful. - self.sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() }); - } else if let Some(fixed) = fix_base_capitalisation(suf) { - self.sess.emit_err(InvalidNumLiteralBasePrefix { span, fixed }); - } else { - self.sess.emit_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() }); - } - } - LitError::InvalidFloatSuffix => { - let suf = suffix.expect("suffix error with no suffix"); - let suf = suf.as_str(); - if looks_like_width_suffix(&['f'], suf) { - // If it looks like a width, try to be helpful. - self.sess - .emit_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() }); - } else { - self.sess.emit_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() }); - } - } - LitError::NonDecimalFloat(base) => { - match base { - 16 => self.sess.emit_err(HexadecimalFloatLiteralNotSupported { span }), - 8 => self.sess.emit_err(OctalFloatLiteralNotSupported { span }), - 2 => self.sess.emit_err(BinaryFloatLiteralNotSupported { span }), - _ => unreachable!(), - }; - } - LitError::IntTooLarge => { - self.sess.emit_err(IntLiteralTooLarge { span }); - } + None => None, } } @@ -1958,8 +1910,8 @@ impl<'a> Parser<'a> { let lo = self.token.span; let minus_present = self.eat(&token::BinOp(token::Minus)); - let lit = self.parse_lit()?; - let expr = self.mk_expr(lit.span, ExprKind::Lit(lit)); + let (token_lit, span) = self.parse_token_lit()?; + let expr = self.mk_expr(span, ExprKind::Lit(token_lit)); if minus_present { Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_unary(UnOp::Neg, expr))) diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 52c11b4e35f..b3af37a5f70 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -420,7 +420,7 @@ impl<'a> Parser<'a> { err.span_label(self_.token.span, format!("expected {}", expected)); err }); - PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit))) + PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit.token_lit))) } else { // Try to parse everything else as literal with optional minus match self.parse_literal_maybe_minus() { diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 18b661034e0..7820bbc1789 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -359,7 +359,7 @@ impl<'a> Parser<'a> { /// report error for `let 1x = 123` pub fn report_invalid_identifier_error(&mut self) -> PResult<'a, ()> { if let token::Literal(lit) = self.token.uninterpolate().kind && - let Err(_) = rustc_ast::Lit::from_token(&self.token) && + rustc_ast::Lit::from_token(&self.token).is_none() && (lit.kind == token::LitKind::Integer || lit.kind == token::LitKind::Float) && self.look_ahead(1, |t| matches!(t.kind, token::Eq) || matches!(t.kind, token::Colon ) ) { return Err(self.sess.create_err(InvalidIdentiferStartsWithNumber { span: self.token.span })); diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 47477898b24..8e7f8bfe0f5 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -49,10 +49,12 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta MetaItemKind::List(nmis) } MacArgs::Eq(_, MacArgsEq::Ast(expr)) => { - if let ast::ExprKind::Lit(lit) = &expr.kind { - if !lit.kind.is_unsuffixed() { + if let ast::ExprKind::Lit(token_lit) = expr.kind + && let Ok(lit) = ast::Lit::from_token_lit(token_lit, expr.span) + { + if token_lit.suffix.is_some() { let mut err = sess.span_diagnostic.struct_span_err( - lit.span, + expr.span, "suffixed literals are not allowed in attributes", ); err.help( @@ -61,7 +63,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta ); return Err(err); } else { - MetaItemKind::NameValue(lit.clone()) + MetaItemKind::NameValue(lit) } } else { // The non-error case can happen with e.g. `#[foo = 1+1]`. The error case can diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index bf542faec41..4bfa583fc72 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -1,6 +1,9 @@ use std::num::NonZeroU32; use crate::cgu_reuse_tracker::CguReuse; +use crate::parse::ParseSess; +use rustc_ast::token; +use rustc_ast::util::literal::LitError; use rustc_errors::MultiSpan; use rustc_macros::Diagnostic; use rustc_span::{Span, Symbol}; @@ -191,3 +194,162 @@ pub enum UnleashedFeatureHelp { span: Span, }, } + +#[derive(Diagnostic)] +#[diag(parser_invalid_literal_suffix)] +pub(crate) struct InvalidLiteralSuffix { + #[primary_span] + #[label] + pub span: Span, + // FIXME(#100717) + pub kind: String, + pub suffix: Symbol, +} + +#[derive(Diagnostic)] +#[diag(parser_invalid_int_literal_width)] +#[help] +pub(crate) struct InvalidIntLiteralWidth { + #[primary_span] + pub span: Span, + pub width: String, +} + +#[derive(Diagnostic)] +#[diag(parser_invalid_num_literal_base_prefix)] +#[note] +pub(crate) struct InvalidNumLiteralBasePrefix { + #[primary_span] + #[suggestion(applicability = "maybe-incorrect", code = "{fixed}")] + pub span: Span, + pub fixed: String, +} + +#[derive(Diagnostic)] +#[diag(parser_invalid_num_literal_suffix)] +#[help] +pub(crate) struct InvalidNumLiteralSuffix { + #[primary_span] + #[label] + pub span: Span, + pub suffix: String, +} + +#[derive(Diagnostic)] +#[diag(parser_invalid_float_literal_width)] +#[help] +pub(crate) struct InvalidFloatLiteralWidth { + #[primary_span] + pub span: Span, + pub width: String, +} + +#[derive(Diagnostic)] +#[diag(parser_invalid_float_literal_suffix)] +#[help] +pub(crate) struct InvalidFloatLiteralSuffix { + #[primary_span] + #[label] + pub span: Span, + pub suffix: String, +} + +#[derive(Diagnostic)] +#[diag(parser_int_literal_too_large)] +pub(crate) struct IntLiteralTooLarge { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parser_hexadecimal_float_literal_not_supported)] +pub(crate) struct HexadecimalFloatLiteralNotSupported { + #[primary_span] + #[label(parser_not_supported)] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parser_octal_float_literal_not_supported)] +pub(crate) struct OctalFloatLiteralNotSupported { + #[primary_span] + #[label(parser_not_supported)] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parser_binary_float_literal_not_supported)] +pub(crate) struct BinaryFloatLiteralNotSupported { + #[primary_span] + #[label(parser_not_supported)] + pub span: Span, +} + +pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span: Span) { + // Checks if `s` looks like i32 or u1234 etc. + fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { + s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit()) + } + + // Try to lowercase the prefix if it's a valid base prefix. + fn fix_base_capitalisation(s: &str) -> Option { + if let Some(stripped) = s.strip_prefix('B') { + Some(format!("0b{stripped}")) + } else if let Some(stripped) = s.strip_prefix('O') { + Some(format!("0o{stripped}")) + } else if let Some(stripped) = s.strip_prefix('X') { + Some(format!("0x{stripped}")) + } else { + None + } + } + + let token::Lit { kind, suffix, .. } = lit; + match err { + // `LexerError` is an error, but it was already reported + // by lexer, so here we don't report it the second time. + LitError::LexerError => {} + LitError::InvalidSuffix => { + if let Some(suffix) = suffix { + sess.emit_err(InvalidLiteralSuffix { + span, + kind: format!("{}", kind.descr()), + suffix, + }); + } + } + LitError::InvalidIntSuffix => { + let suf = suffix.expect("suffix error with no suffix"); + let suf = suf.as_str(); + if looks_like_width_suffix(&['i', 'u'], &suf) { + // If it looks like a width, try to be helpful. + sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() }); + } else if let Some(fixed) = fix_base_capitalisation(suf) { + sess.emit_err(InvalidNumLiteralBasePrefix { span, fixed }); + } else { + sess.emit_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() }); + } + } + LitError::InvalidFloatSuffix => { + let suf = suffix.expect("suffix error with no suffix"); + let suf = suf.as_str(); + if looks_like_width_suffix(&['f'], suf) { + // If it looks like a width, try to be helpful. + sess.emit_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() }); + } else { + sess.emit_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() }); + } + } + LitError::NonDecimalFloat(base) => { + match base { + 16 => sess.emit_err(HexadecimalFloatLiteralNotSupported { span }), + 8 => sess.emit_err(OctalFloatLiteralNotSupported { span }), + 2 => sess.emit_err(BinaryFloatLiteralNotSupported { span }), + _ => unreachable!(), + }; + } + LitError::IntTooLarge => { + sess.emit_err(IntLiteralTooLarge { span }); + } + } +} diff --git a/src/test/ui/codemap_tests/unicode_2.stderr b/src/test/ui/codemap_tests/unicode_2.stderr index a776a4a1e7e..19aae1d3c95 100644 --- a/src/test/ui/codemap_tests/unicode_2.stderr +++ b/src/test/ui/codemap_tests/unicode_2.stderr @@ -1,3 +1,9 @@ +error[E0425]: cannot find value `a̐é` in this scope + --> $DIR/unicode_2.rs:4:13 + | +LL | let _ = a̐é; + | ^^ not found in this scope + error: invalid width `7` for integer literal --> $DIR/unicode_2.rs:2:25 | @@ -14,12 +20,6 @@ LL | let _ = ("아あ", 1i42); | = help: valid widths are 8, 16, 32, 64 and 128 -error[E0425]: cannot find value `a̐é` in this scope - --> $DIR/unicode_2.rs:4:13 - | -LL | let _ = a̐é; - | ^^ not found in this scope - error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/lexer/error-stage.rs b/src/test/ui/lexer/error-stage.rs index 05ae0e82bfb..c8d88f745a1 100644 --- a/src/test/ui/lexer/error-stage.rs +++ b/src/test/ui/lexer/error-stage.rs @@ -1,31 +1,80 @@ +// This test is about the treatment of invalid literals. In particular, some +// literals are only considered invalid if they survive to HIR lowering. +// +// Literals with bad suffixes +// -------------------------- +// Literals consist of a primary part and an optional suffix. +// https://doc.rust-lang.org/reference/tokens.html#suffixes says: +// +// Any kind of literal (string, integer, etc) with any suffix is valid as a +// token, and can be passed to a macro without producing an error. The macro +// itself will decide how to interpret such a token and whether to produce an +// error or not. +// +// ``` +// macro_rules! blackhole { ($tt:tt) => () } +// blackhole!("string"suffix); // OK +// ``` +// +// However, suffixes on literal tokens parsed as Rust code are restricted. +// Any suffixes are rejected on non-numeric literal tokens, and numeric +// literal tokens are accepted only with suffixes from the list below. +// +// Integer: u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize +// Floating-point: f32, f64 +// +// This means that something like `"string"any_suffix` is a token accepted by +// the lexer, but rejected later for being an invalid combination of primary +// part and suffix. +// +// `0b10f32` is a similar case. `0b10` is a valid primary part that is a valid +// *integer* literal when no suffix is present. It only causes an error later +// when combined with the `f32` float suffix. +// +// However, `0b10.0f32` is different. It is rejected by the lexer because +// `0b10.0` is not a valid token even on its own. +// +// This difference is unfortunate, but it's baked into the language now. +// +// Too-large integer literals +// -------------------------- +// https://doc.rust-lang.org/reference/tokens.html#integer-literals says that +// literals like `128_i8` and `256_u8` "are too big for their type, but are +// still valid tokens". + macro_rules! sink { ($($x:tt;)*) => {()} } -// The invalid literals are ignored because the macro consumes them. +// The invalid literals are ignored because the macro consumes them. Except for +// `0b10.0f32` because it's a lexer error. const _: () = sink! { "string"any_suffix; // OK 10u123; // OK 10.0f123; // OK 0b10f32; // OK + 0b10.0f32; //~ ERROR binary float literal is not supported 999340282366920938463463374607431768211455999; // OK }; -// The invalid literals cause errors. +// The invalid literals used to cause errors, but this was changed by #102944. +// Except for `0b010.0f32`, because it's a lexer error. #[cfg(FALSE)] fn configured_out() { - "string"any_suffix; //~ ERROR suffixes on string literals are invalid - 10u123; //~ ERROR invalid width `123` for integer literal - 10.0f123; //~ ERROR invalid width `123` for float literal - 0b10f32; //~ ERROR binary float literal is not supported - 999340282366920938463463374607431768211455999; //~ ERROR integer literal is too large + "string"any_suffix; // OK + 10u123; // OK + 10.0f123; // OK + 0b10f32; // OK + 0b10.0f32; //~ ERROR binary float literal is not supported + 999340282366920938463463374607431768211455999; // OK } -// The invalid literals cause errors. +// All the invalid literals cause errors. fn main() { "string"any_suffix; //~ ERROR suffixes on string literals are invalid 10u123; //~ ERROR invalid width `123` for integer literal 10.0f123; //~ ERROR invalid width `123` for float literal 0b10f32; //~ ERROR binary float literal is not supported + 0b10.0f32; //~ ERROR binary float literal is not supported 999340282366920938463463374607431768211455999; //~ ERROR integer literal is too large } diff --git a/src/test/ui/lexer/error-stage.stderr b/src/test/ui/lexer/error-stage.stderr index 024b7d94037..697a7c28da1 100644 --- a/src/test/ui/lexer/error-stage.stderr +++ b/src/test/ui/lexer/error-stage.stderr @@ -1,11 +1,29 @@ +error: binary float literal is not supported + --> $DIR/error-stage.rs:56:5 + | +LL | 0b10.0f32; + | ^^^^^^ + +error: binary float literal is not supported + --> $DIR/error-stage.rs:68:5 + | +LL | 0b10.0f32; + | ^^^^^^ + +error: binary float literal is not supported + --> $DIR/error-stage.rs:78:5 + | +LL | 0b10.0f32; + | ^^^^^^ + error: suffixes on string literals are invalid - --> $DIR/error-stage.rs:17:5 + --> $DIR/error-stage.rs:74:5 | LL | "string"any_suffix; | ^^^^^^^^^^^^^^^^^^ invalid suffix `any_suffix` error: invalid width `123` for integer literal - --> $DIR/error-stage.rs:18:5 + --> $DIR/error-stage.rs:75:5 | LL | 10u123; | ^^^^^^ @@ -13,7 +31,7 @@ LL | 10u123; = help: valid widths are 8, 16, 32, 64 and 128 error: invalid width `123` for float literal - --> $DIR/error-stage.rs:19:5 + --> $DIR/error-stage.rs:76:5 | LL | 10.0f123; | ^^^^^^^^ @@ -21,50 +39,16 @@ LL | 10.0f123; = help: valid widths are 32 and 64 error: binary float literal is not supported - --> $DIR/error-stage.rs:20:5 + --> $DIR/error-stage.rs:77:5 | LL | 0b10f32; | ^^^^^^^ not supported error: integer literal is too large - --> $DIR/error-stage.rs:21:5 + --> $DIR/error-stage.rs:79:5 | LL | 999340282366920938463463374607431768211455999; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: suffixes on string literals are invalid - --> $DIR/error-stage.rs:26:5 - | -LL | "string"any_suffix; - | ^^^^^^^^^^^^^^^^^^ invalid suffix `any_suffix` - -error: invalid width `123` for integer literal - --> $DIR/error-stage.rs:27:5 - | -LL | 10u123; - | ^^^^^^ - | - = help: valid widths are 8, 16, 32, 64 and 128 - -error: invalid width `123` for float literal - --> $DIR/error-stage.rs:28:5 - | -LL | 10.0f123; - | ^^^^^^^^ - | - = help: valid widths are 32 and 64 - -error: binary float literal is not supported - --> $DIR/error-stage.rs:29:5 - | -LL | 0b10f32; - | ^^^^^^^ not supported - -error: integer literal is too large - --> $DIR/error-stage.rs:30:5 - | -LL | 999340282366920938463463374607431768211455999; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 10 previous errors +error: aborting due to 8 previous errors diff --git a/src/test/ui/parser/bad-lit-suffixes.rs b/src/test/ui/parser/bad-lit-suffixes.rs index 58b30872677..8cb9ef7e0c9 100644 --- a/src/test/ui/parser/bad-lit-suffixes.rs +++ b/src/test/ui/parser/bad-lit-suffixes.rs @@ -28,12 +28,11 @@ fn main() { } #[rustc_dummy = "string"suffix] -//~^ ERROR suffixes on string literals are invalid +//~^ ERROR unexpected expression: `"string"suffix` fn f() {} #[must_use = "string"suffix] -//~^ ERROR suffixes on string literals are invalid -//~^^ ERROR malformed `must_use` attribute input +//~^ ERROR unexpected expression: `"string"suffix` fn g() {} #[link(name = "string"suffix)] diff --git a/src/test/ui/parser/bad-lit-suffixes.stderr b/src/test/ui/parser/bad-lit-suffixes.stderr index 14c0eda81be..756f99ab12c 100644 --- a/src/test/ui/parser/bad-lit-suffixes.stderr +++ b/src/test/ui/parser/bad-lit-suffixes.stderr @@ -10,6 +10,32 @@ error: suffixes on string literals are invalid LL | "C"suffix | ^^^^^^^^^ invalid suffix `suffix` +error: unexpected expression: `"string"suffix` + --> $DIR/bad-lit-suffixes.rs:30:17 + | +LL | #[rustc_dummy = "string"suffix] + | ^^^^^^^^^^^^^^ + +error: unexpected expression: `"string"suffix` + --> $DIR/bad-lit-suffixes.rs:34:14 + | +LL | #[must_use = "string"suffix] + | ^^^^^^^^^^^^^^ + +error: suffixes on string literals are invalid + --> $DIR/bad-lit-suffixes.rs:38:15 + | +LL | #[link(name = "string"suffix)] + | ^^^^^^^^^^^^^^ invalid suffix `suffix` + +error: invalid suffix `suffix` for number literal + --> $DIR/bad-lit-suffixes.rs:42:41 + | +LL | #[rustc_layout_scalar_valid_range_start(0suffix)] + | ^^^^^^^ invalid suffix `suffix` + | + = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) + error: suffixes on string literals are invalid --> $DIR/bad-lit-suffixes.rs:12:5 | @@ -110,44 +136,5 @@ LL | 1.0e10suffix; | = help: valid suffixes are `f32` and `f64` -error: suffixes on string literals are invalid - --> $DIR/bad-lit-suffixes.rs:30:17 - | -LL | #[rustc_dummy = "string"suffix] - | ^^^^^^^^^^^^^^ invalid suffix `suffix` - -error: suffixes on string literals are invalid - --> $DIR/bad-lit-suffixes.rs:34:14 - | -LL | #[must_use = "string"suffix] - | ^^^^^^^^^^^^^^ invalid suffix `suffix` - -error: malformed `must_use` attribute input - --> $DIR/bad-lit-suffixes.rs:34:1 - | -LL | #[must_use = "string"suffix] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the following are the possible correct uses - | -LL | #[must_use = "reason"] - | -LL | #[must_use] - | - -error: suffixes on string literals are invalid - --> $DIR/bad-lit-suffixes.rs:39:15 - | -LL | #[link(name = "string"suffix)] - | ^^^^^^^^^^^^^^ invalid suffix `suffix` - -error: invalid suffix `suffix` for number literal - --> $DIR/bad-lit-suffixes.rs:43:41 - | -LL | #[rustc_layout_scalar_valid_range_start(0suffix)] - | ^^^^^^^ invalid suffix `suffix` - | - = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) - -error: aborting due to 21 previous errors +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs index 073e4af1318..df92579a85d 100644 --- a/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs +++ b/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs @@ -73,12 +73,21 @@ impl EarlyLintPass for AlmostCompleteLetterRange { } fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) { - if let ExprKind::Lit(start_lit) = &start.peel_parens().kind - && let ExprKind::Lit(end_lit) = &end.peel_parens().kind + if let ExprKind::Lit(start_token_lit) = start.peel_parens().kind + && let ExprKind::Lit(end_token_lit) = end.peel_parens().kind && matches!( - (&start_lit.kind, &end_lit.kind), - (LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z')) - | (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z')) + ( + LitKind::from_token_lit(start_token_lit), + LitKind::from_token_lit(end_token_lit), + ), + ( + Ok(LitKind::Byte(b'a') | LitKind::Char('a')), + Ok(LitKind::Byte(b'z') | LitKind::Char('z')) + ) + | ( + Ok(LitKind::Byte(b'A') | LitKind::Char('A')), + Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')), + ) ) && !in_external_macro(cx.sess(), span) { diff --git a/src/tools/clippy/clippy_lints/src/int_plus_one.rs b/src/tools/clippy/clippy_lints/src/int_plus_one.rs index 33491da3fc5..f793abdfda3 100644 --- a/src/tools/clippy/clippy_lints/src/int_plus_one.rs +++ b/src/tools/clippy/clippy_lints/src/int_plus_one.rs @@ -2,7 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind}; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind}; +use rustc_ast::token; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -52,8 +53,8 @@ enum Side { impl IntPlusOne { #[expect(clippy::cast_sign_loss)] - fn check_lit(lit: &Lit, target_value: i128) -> bool { - if let LitKind::Int(value, ..) = lit.kind { + fn check_lit(token_lit: token::Lit, target_value: i128) -> bool { + if let Ok(LitKind::Int(value, ..)) = LitKind::from_token_lit(token_lit) { return value == (target_value as u128); } false @@ -65,11 +66,11 @@ impl IntPlusOne { (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => { match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { // `-1 + x` - (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, // `x - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, @@ -81,10 +82,10 @@ impl IntPlusOne { { match (&rhslhs.kind, &rhsrhs.kind) { // `y + 1` and `1 + y` - (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { + (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, - (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, @@ -96,10 +97,10 @@ impl IntPlusOne { { match (&lhslhs.kind, &lhsrhs.kind) { // `1 + x` and `x + 1` - (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { + (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, - (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, @@ -109,11 +110,11 @@ impl IntPlusOne { (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => { match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { // `-1 + y` - (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, // `y - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 25f19b9c6e6..3a7b7835c99 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -5,11 +5,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; use clippy_utils::source::snippet_opt; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; +use rustc_ast::ast::{Expr, ExprKind, LitKind}; +use rustc_ast::token; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; use std::iter; declare_clippy_lint! { @@ -236,8 +238,8 @@ impl EarlyLintPass for LiteralDigitGrouping { return; } - if let ExprKind::Lit(ref lit) = expr.kind { - self.check_lit(cx, lit); + if let ExprKind::Lit(lit) = expr.kind { + self.check_lit(cx, lit, expr.span); } } } @@ -252,12 +254,13 @@ impl LiteralDigitGrouping { } } - fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { if_chain! { - if let Some(src) = snippet_opt(cx, lit.span); - if let Some(mut num_lit) = NumericLiteral::from_lit(&src, lit); + if let Some(src) = snippet_opt(cx, span); + if let Ok(lit_kind) = LitKind::from_token_lit(lit); + if let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); then { - if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) { + if !Self::check_for_mistyped_suffix(cx, span, &mut num_lit) { return; } @@ -293,14 +296,14 @@ impl LiteralDigitGrouping { | WarningType::InconsistentDigitGrouping | WarningType::UnusualByteGroupings | WarningType::LargeDigitGroups => { - !lit.span.from_expansion() + !span.from_expansion() } WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => { true } }; if should_warn { - warning_type.display(num_lit.format(), cx, lit.span); + warning_type.display(num_lit.format(), cx, span); } } } @@ -458,8 +461,8 @@ impl EarlyLintPass for DecimalLiteralRepresentation { return; } - if let ExprKind::Lit(ref lit) = expr.kind { - self.check_lit(cx, lit); + if let ExprKind::Lit(lit) = expr.kind { + self.check_lit(cx, lit, expr.span); } } } @@ -469,19 +472,20 @@ impl DecimalLiteralRepresentation { pub fn new(threshold: u64) -> Self { Self { threshold } } - fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // Lint integral literals. if_chain! { - if let LitKind::Int(val, _) = lit.kind; - if let Some(src) = snippet_opt(cx, lit.span); - if let Some(num_lit) = NumericLiteral::from_lit(&src, lit); + if let Ok(lit_kind) = LitKind::from_token_lit(lit); + if let LitKind::Int(val, _) = lit_kind; + if let Some(src) = snippet_opt(cx, span); + if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); if num_lit.radix == Radix::Decimal; if val >= u128::from(self.threshold); then { let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| { - warning_type.display(num_lit.format(), cx, lit.span); + warning_type.display(num_lit.format(), cx, span); }); } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs b/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs index 27e7f8505eb..eda4376f200 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::Lit; use rustc_errors::Applicability; use rustc_lint::EarlyContext; +use rustc_span::Span; use super::{SEPARATED_LITERAL_SUFFIX, UNSEPARATED_LITERAL_SUFFIX}; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str, suffix: &str, sugg_type: &str) { let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; @@ -15,7 +15,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s span_lint_and_sugg( cx, SEPARATED_LITERAL_SUFFIX, - lit.span, + lit_span, &format!("{sugg_type} type suffix should not be separated by an underscore"), "remove the underscore", format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]), @@ -25,7 +25,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s span_lint_and_sugg( cx, UNSEPARATED_LITERAL_SUFFIX, - lit.span, + lit_span, &format!("{sugg_type} type suffix should be separated by an underscore"), "add an underscore", format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]), diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index 263ee1e945a..ddb8b9173a5 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast::Lit; use rustc_lint::EarlyContext; +use rustc_span::Span; use super::MIXED_CASE_HEX_LITERALS; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, suffix: &str, lit_snip: &str) { let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; @@ -23,7 +23,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &s span_lint( cx, MIXED_CASE_HEX_LITERALS, - lit.span, + lit_span, "inconsistent casing in hexadecimal literal", ); break; diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs index c8227ca4450..78be6b9e23f 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs @@ -9,7 +9,8 @@ mod zero_prefixed_literal; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; +use rustc_ast::ast::{Expr, ExprKind, Generics, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; +use rustc_ast::token; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; @@ -374,42 +375,43 @@ impl EarlyLintPass for MiscEarlyLints { return; } - if let ExprKind::Lit(ref lit) = expr.kind { - MiscEarlyLints::check_lit(cx, lit); + if let ExprKind::Lit(lit) = expr.kind { + MiscEarlyLints::check_lit(cx, lit, expr.span); } double_neg::check(cx, expr); } } impl MiscEarlyLints { - fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // We test if first character in snippet is a number, because the snippet could be an expansion // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`. // Note that this check also covers special case that `line!()` is eagerly expanded by compiler. // See for a regression. // FIXME: Find a better way to detect those cases. - let lit_snip = match snippet_opt(cx, lit.span) { + let lit_snip = match snippet_opt(cx, span) { Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip, _ => return, }; - if let LitKind::Int(value, lit_int_type) = lit.kind { + let lit_kind = LitKind::from_token_lit(lit); + if let Ok(LitKind::Int(value, lit_int_type)) = lit_kind { let suffix = match lit_int_type { LitIntType::Signed(ty) => ty.name_str(), LitIntType::Unsigned(ty) => ty.name_str(), LitIntType::Unsuffixed => "", }; - literal_suffix::check(cx, lit, &lit_snip, suffix, "integer"); + literal_suffix::check(cx, span, &lit_snip, suffix, "integer"); if lit_snip.starts_with("0x") { - mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip); + mixed_case_hex_literals::check(cx, span, suffix, &lit_snip); } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { // nothing to do } else if value != 0 && lit_snip.starts_with('0') { - zero_prefixed_literal::check(cx, lit, &lit_snip); + zero_prefixed_literal::check(cx, span, &lit_snip); } - } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { + } else if let Ok(LitKind::Float(_, LitFloatType::Suffixed(float_ty))) = lit_kind { let suffix = float_ty.name_str(); - literal_suffix::check(cx, lit, &lit_snip, suffix, "float"); + literal_suffix::check(cx, span, &lit_snip, suffix, "float"); } } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 9ead43ea4a4..4f9578d1b25 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -1,20 +1,20 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::ast::Lit; use rustc_errors::Applicability; use rustc_lint::EarlyContext; +use rustc_span::Span; use super::ZERO_PREFIXED_LITERAL; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) { let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0'); span_lint_and_then( cx, ZERO_PREFIXED_LITERAL, - lit.span, + lit_span, "this is a decimal constant", |diag| { diag.span_suggestion( - lit.span, + lit_span, "if you mean to use a decimal constant, remove the `0` to avoid confusion", trimmed_lit_snip.to_string(), Applicability::MaybeIncorrect, @@ -22,7 +22,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { // do not advise to use octal form if the literal cannot be expressed in base 8. if !lit_snip.contains(|c| c == '8' || c == '9') { diag.span_suggestion( - lit.span, + lit_span, "if you mean to use an octal constant, use `0o`", format!("0o{trimmed_lit_snip}"), Applicability::MaybeIncorrect, diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs index f380a506582..2a7159764e4 100644 --- a/src/tools/clippy/clippy_lints/src/octal_escapes.rs +++ b/src/tools/clippy/clippy_lints/src/octal_escapes.rs @@ -56,11 +56,11 @@ impl EarlyLintPass for OctalEscapes { return; } - if let ExprKind::Lit(lit) = &expr.kind { - if matches!(lit.token_lit.kind, LitKind::Str) { - check_lit(cx, &lit.token_lit, lit.span, true); - } else if matches!(lit.token_lit.kind, LitKind::ByteStr) { - check_lit(cx, &lit.token_lit, lit.span, false); + if let ExprKind::Lit(token_lit) = &expr.kind { + if matches!(token_lit.kind, LitKind::Str) { + check_lit(cx, &token_lit, expr.span, true); + } else if matches!(token_lit.kind, LitKind::ByteStr) { + check_lit(cx, &token_lit, expr.span, false); } } } diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs index e6e3ad05ad7..bee4a33fb4a 100644 --- a/src/tools/clippy/clippy_lints/src/precedence.rs +++ b/src/tools/clippy/clippy_lints/src/precedence.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; -use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_ast::token; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -120,7 +121,7 @@ impl EarlyLintPass for Precedence { if_chain! { if !all_odd; if let ExprKind::Lit(lit) = &arg.kind; - if let LitKind::Int(..) | LitKind::Float(..) = &lit.kind; + if let token::LitKind::Integer | token::LitKind::Float = &lit.kind; then { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/unused_rounding.rs b/src/tools/clippy/clippy_lints/src/unused_rounding.rs index 3164937293b..3c1998d0237 100644 --- a/src/tools/clippy/clippy_lints/src/unused_rounding.rs +++ b/src/tools/clippy/clippy_lints/src/unused_rounding.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind}; +use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -33,14 +33,14 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { if let ExprKind::MethodCall(name_ident, receiver, _, _) = &expr.kind && let method_name = name_ident.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") - && let ExprKind::Lit(spanned) = &receiver.kind - && let LitKind::Float(symbol, ty) = spanned.kind { - let f = symbol.as_str().parse::().unwrap(); - let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { - ty.name_str() - } else { - "" - }; + && let ExprKind::Lit(token_lit) = &receiver.kind + && token_lit.is_semantic_float() { + let f = token_lit.symbol.as_str().parse::().unwrap(); + let mut f_str = token_lit.symbol.to_string(); + match token_lit.suffix { + Some(suffix) => f_str.push_str(suffix.as_str()), + None => {} + } if f.fract() == 0.0 { Some((method_name, f_str)) } else { diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 0133997560e..73d1ba727c8 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -152,7 +152,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { }, (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr), (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), - (Lit(l), Lit(r)) => l.kind == r.kind, + (Lit(l), Lit(r)) => l == r, (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re), (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs index c5dcd7b31f5..42bdfd4827f 100644 --- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs +++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs @@ -1,4 +1,4 @@ -use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; +use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use std::iter; #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -46,10 +46,6 @@ pub struct NumericLiteral<'a> { } impl<'a> NumericLiteral<'a> { - pub fn from_lit(src: &'a str, lit: &Lit) -> Option> { - NumericLiteral::from_lit_kind(src, &lit.kind) - } - pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option> { let unsigned_src = src.strip_prefix('-').map_or(src, |s| s); if lit_kind.is_numeric() diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs index f5c1ee5fdd1..ccc2fd0d5f5 100644 --- a/src/tools/rustfmt/src/attr.rs +++ b/src/tools/rustfmt/src/attr.rs @@ -260,7 +260,9 @@ impl Rewrite for ast::NestedMetaItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { match self { ast::NestedMetaItem::MetaItem(ref meta_item) => meta_item.rewrite(context, shape), - ast::NestedMetaItem::Literal(ref l) => rewrite_literal(context, l, shape), + ast::NestedMetaItem::Literal(ref l) => { + rewrite_literal(context, l.token_lit, l.span, shape) + } } } } @@ -318,7 +320,7 @@ impl Rewrite for ast::MetaItem { // we might be better off ignoring the fact that the attribute // is longer than the max width and continue on formatting. // See #2479 for example. - let value = rewrite_literal(context, literal, lit_shape) + let value = rewrite_literal(context, literal.token_lit, literal.span, lit_shape) .unwrap_or_else(|| context.snippet(literal.span).to_owned()); format!("{} = {}", path, value) } diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 7750df0fff3..b4f1a178dbf 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -3,7 +3,7 @@ use std::cmp::min; use itertools::Itertools; use rustc_ast::token::{Delimiter, LitKind}; -use rustc_ast::{ast, ptr}; +use rustc_ast::{ast, ptr, token}; use rustc_span::{BytePos, Span}; use crate::chains::rewrite_chain; @@ -75,12 +75,12 @@ pub(crate) fn format_expr( choose_separator_tactic(context, expr.span), None, ), - ast::ExprKind::Lit(ref l) => { - if let Some(expr_rw) = rewrite_literal(context, l, shape) { + ast::ExprKind::Lit(token_lit) => { + if let Some(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) { Some(expr_rw) } else { - if let LitKind::StrRaw(_) = l.token_lit.kind { - Some(context.snippet(l.span).trim().into()) + if let LitKind::StrRaw(_) = token_lit.kind { + Some(context.snippet(expr.span).trim().into()) } else { None } @@ -274,9 +274,9 @@ pub(crate) fn format_expr( fn needs_space_before_range(context: &RewriteContext<'_>, lhs: &ast::Expr) -> bool { match lhs.kind { - ast::ExprKind::Lit(ref lit) => match lit.kind { - ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { - context.snippet(lit.span).ends_with('.') + ast::ExprKind::Lit(token_lit) => match token_lit.kind { + token::LitKind::Float if token_lit.suffix.is_none() => { + context.snippet(lhs.span).ends_with('.') } _ => false, }, @@ -1185,14 +1185,15 @@ pub(crate) fn is_unsafe_block(block: &ast::Block) -> bool { pub(crate) fn rewrite_literal( context: &RewriteContext<'_>, - l: &ast::Lit, + token_lit: token::Lit, + span: Span, shape: Shape, ) -> Option { - match l.kind { - ast::LitKind::Str(_, ast::StrStyle::Cooked) => rewrite_string_lit(context, l.span, shape), - ast::LitKind::Int(..) => rewrite_int_lit(context, l, shape), + match token_lit.kind { + token::LitKind::Str => rewrite_string_lit(context, span, shape), + token::LitKind::Integer => rewrite_int_lit(context, token_lit, span, shape), _ => wrap_str( - context.snippet(l.span).to_owned(), + context.snippet(span).to_owned(), context.config.max_width(), shape, ), @@ -1225,9 +1226,13 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> ) } -fn rewrite_int_lit(context: &RewriteContext<'_>, lit: &ast::Lit, shape: Shape) -> Option { - let span = lit.span; - let symbol = lit.token_lit.symbol.as_str(); +fn rewrite_int_lit( + context: &RewriteContext<'_>, + token_lit: token::Lit, + span: Span, + shape: Shape, +) -> Option { + let symbol = token_lit.symbol.as_str(); if let Some(symbol_stripped) = symbol.strip_prefix("0x") { let hex_lit = match context.config.hex_literal_case() { @@ -1240,9 +1245,7 @@ fn rewrite_int_lit(context: &RewriteContext<'_>, lit: &ast::Lit, shape: Shape) - format!( "0x{}{}", hex_lit, - lit.token_lit - .suffix - .map_or(String::new(), |s| s.to_string()) + token_lit.suffix.map_or(String::new(), |s| s.to_string()) ), context.config.max_width(), shape,