/// A unary operation (e.g., `!x`, `*x`).
Unary(UnOp, P<Expr>),
/// A literal (e.g., `1`, `"foo"`).
- Lit(Lit),
+ Lit(token::Lit),
/// A cast (e.g., `foo as f64`).
Cast(P<Expr>, P<Ty>),
/// A type ascription (e.g., `42: usize`).
}
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)
}
}
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
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,
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,
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,
}
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())),
{
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));
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,
pub suffix: Option<Symbol>,
}
+impl Lit {
+ pub fn new(kind: LitKind, symbol: Symbol, suffix: Option<Symbol>) -> 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<Lit> {
+ 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;
}
}
-impl Lit {
- pub fn new(kind: LitKind, symbol: Symbol, suffix: Option<Symbol>) -> 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);
use rustc_span::Span;
use std::ascii;
+#[derive(Debug)]
pub enum LitError {
- NotLiteral,
LexerError,
InvalidSuffix,
InvalidIntSuffix,
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<Lit, LitError> {
- 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<Lit> {
+ 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.
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;
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),
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),
}
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) {
}
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();
}
}
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);
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();
// 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 {
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;
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;
+ }
+ Ok(ast::LitKind::Err) => {
+ has_errors = true;
}
- ast::LitKind::Err => {
+ Err(err) => {
+ report_lit_error(&cx.sess.parse_sess, err, token_lit, e.span);
has_errors = true;
}
},
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<rustc_ast::Expr>, 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,
.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,
}
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,
}
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!(),
}
*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")
}
_ => {
if !*has_errors {
- invalid_type_err(cx, expr, true);
+ invalid_type_err(cx, token_lit, expr.span, true);
}
*has_errors = true;
None
}
}
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)
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;
}
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)),
);
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)),
}
fn expr_lit(&self, span: Span, lit_kind: ast::LitKind) -> P<ast::Expr> {
- 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<ast::Expr> {
// 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);
))
}
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(()),
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 },
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(
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");
}
}
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 {
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 {
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);
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,
);
}
});
- (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))
+ }
}
}
}
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 });
};
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};
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};
fn parse_lit_expr(&mut self) -> PResult<'a, P<Expr>> {
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(),
})
});
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))
{
/// 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)
)
.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,
}
}
/// 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<ast::StrLit, Option<Lit>> {
- 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,
}
}
- 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);
- }
- }
- }
- 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)
+ 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,
};
- // 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))
+ 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_: &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<Lit> {
+ 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<Token> {
let mut recovered = None;
if self.token == token::Dot {
// Attempt to recover `.4` as `0.4`. We don't currently have any syntax where
}
}
- 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())
- }
-
- // Try to lowercase the prefix if it's a valid base prefix.
- fn fix_base_capitalisation(s: &str) -> Option<String> {
- 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
- }
- }
+ /// 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)
+ })
+ }
- 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,
- });
- }
- }
- 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() });
+ /// 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<Lit> {
+ 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::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,
}
}
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)))
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() {
/// 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 }));
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(
);
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
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};
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<String> {
+ 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 });
+ }
+ }
+}
+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
|
|
= 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`.
+// 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
}
-error: suffixes on string literals are invalid
- --> $DIR/error-stage.rs:17:5
- |
-LL | "string"any_suffix;
- | ^^^^^^^^^^^^^^^^^^ invalid suffix `any_suffix`
-
-error: invalid width `123` for integer literal
- --> $DIR/error-stage.rs:18:5
+error: binary float literal is not supported
+ --> $DIR/error-stage.rs:56:5
|
-LL | 10u123;
+LL | 0b10.0f32;
| ^^^^^^
- |
- = help: valid widths are 8, 16, 32, 64 and 128
-
-error: invalid width `123` for float literal
- --> $DIR/error-stage.rs:19:5
- |
-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:68:5
|
-LL | 0b10f32;
- | ^^^^^^^ not supported
+LL | 0b10.0f32;
+ | ^^^^^^
-error: integer literal is too large
- --> $DIR/error-stage.rs:21:5
+error: binary float literal is not supported
+ --> $DIR/error-stage.rs:78:5
|
-LL | 999340282366920938463463374607431768211455999;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | 0b10.0f32;
+ | ^^^^^^
error: suffixes on string literals are invalid
- --> $DIR/error-stage.rs:26: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:27:5
+ --> $DIR/error-stage.rs:75: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
+ --> $DIR/error-stage.rs:76:5
|
LL | 10.0f123;
| ^^^^^^^^
= help: valid widths are 32 and 64
error: binary float literal is not supported
- --> $DIR/error-stage.rs:29:5
+ --> $DIR/error-stage.rs:77:5
|
LL | 0b10f32;
| ^^^^^^^ not supported
error: integer literal is too large
- --> $DIR/error-stage.rs:30:5
+ --> $DIR/error-stage.rs:79:5
|
LL | 999340282366920938463463374607431768211455999;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 10 previous errors
+error: aborting due to 8 previous errors
}
#[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)]
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
|
|
= 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
}
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)
{
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};
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
(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,
{
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,
{
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,
(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,
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! {
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);
}
}
}
}
}
- 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;
}
| 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);
}
}
}
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);
}
}
}
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);
});
}
}
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.
};
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]),
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]),
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.
};
span_lint(
cx,
MIXED_CASE_HEX_LITERALS,
- lit.span,
+ lit_span,
"inconsistent casing in hexadecimal literal",
);
break;
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};
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 <https://github.com/rust-lang/rust-clippy/issues/4507> 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");
}
}
}
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,
// 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,
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);
}
}
}
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};
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(
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};
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::<f64>().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::<f64>().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 {
},
(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),
-use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
+use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use std::iter;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
}
impl<'a> NumericLiteral<'a> {
- pub fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
- NumericLiteral::from_lit_kind(src, &lit.kind)
- }
-
pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
let unsigned_src = src.strip_prefix('-').map_or(src, |s| s);
if lit_kind.is_numeric()
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
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)
+ }
}
}
}
// 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)
}
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;
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
}
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,
},
pub(crate) fn rewrite_literal(
context: &RewriteContext<'_>,
- l: &ast::Lit,
+ token_lit: token::Lit,
+ span: Span,
shape: Shape,
) -> Option<String> {
- 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,
),
)
}
-fn rewrite_int_lit(context: &RewriteContext<'_>, lit: &ast::Lit, shape: Shape) -> Option<String> {
- 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<String> {
+ 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() {
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,