Auto merge of #102944 - nnethercote:ast-Lit-third-time-lucky, r=petrochenkov
Use `token::Lit` in `ast::ExprKind::Lit`. Instead of `ast::Lit`. Literal lowering now happens at two different times. Expression literals are lowered when HIR is crated. Attribute literals are lowered during parsing. r? `@petrochenkov`
This commit is contained in:
commit
bebd57a960
45 changed files with 786 additions and 507 deletions
|
@ -1332,7 +1332,7 @@ pub enum ExprKind {
|
|||
/// 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`).
|
||||
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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<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;
|
||||
|
@ -139,12 +179,6 @@ impl LitKind {
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -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<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.
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -373,8 +373,12 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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<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,
|
||||
|
@ -21,13 +24,13 @@ fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P<rustc_ast::Expr>, 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<rustc_ast::Expr>, 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<rustc_ast::Expr>, 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;
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -334,8 +334,8 @@ impl<'a> ExtCtxt<'a> {
|
|||
}
|
||||
|
||||
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> {
|
||||
|
|
|
@ -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(()),
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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> {
|
|||
);
|
||||
}
|
||||
});
|
||||
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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<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(),
|
||||
|
@ -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<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,
|
||||
|
@ -1780,8 +1776,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> {
|
||||
self.parse_opt_lit().ok_or(()).or_else(|()| {
|
||||
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),
|
||||
|
@ -1798,7 +1793,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
let token = self.token.clone();
|
||||
let err = |self_: &mut Self| {
|
||||
let err = |self_: &Self| {
|
||||
let msg = format!("unexpected token: {}", super::token_descr(&token));
|
||||
self_.struct_span_err(token.span, &msg)
|
||||
};
|
||||
|
@ -1809,12 +1804,19 @@ impl<'a> Parser<'a> {
|
|||
} 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
|
||||
|
@ -1840,20 +1842,40 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
recovered
|
||||
}
|
||||
|
||||
/// 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);
|
||||
match Lit::from_token(token) {
|
||||
let span = token.span;
|
||||
token::Lit::from_token(token).map(|token_lit| {
|
||||
self.bump();
|
||||
(token_lit, span)
|
||||
})
|
||||
}
|
||||
|
||||
/// 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(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);
|
||||
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);
|
||||
|
@ -1863,77 +1885,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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() });
|
||||
}
|
||||
}
|
||||
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)))
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 }));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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`.
|
||||
|
|
80
src/test/ui/lexer/error-stage.rs
Normal file
80
src/test/ui/lexer/error-stage.rs
Normal file
|
@ -0,0 +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. 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 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; // OK
|
||||
10u123; // OK
|
||||
10.0f123; // OK
|
||||
0b10f32; // OK
|
||||
0b10.0f32; //~ ERROR binary float literal is not supported
|
||||
999340282366920938463463374607431768211455999; // OK
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
54
src/test/ui/lexer/error-stage.stderr
Normal file
54
src/test/ui/lexer/error-stage.stderr
Normal file
|
@ -0,0 +1,54 @@
|
|||
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:74:5
|
||||
|
|
||||
LL | "string"any_suffix;
|
||||
| ^^^^^^^^^^^^^^^^^^ invalid suffix `any_suffix`
|
||||
|
||||
error: invalid width `123` for integer literal
|
||||
--> $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:76:5
|
||||
|
|
||||
LL | 10.0f123;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: valid widths are 32 and 64
|
||||
|
||||
error: binary float literal is not supported
|
||||
--> $DIR/error-stage.rs:77:5
|
||||
|
|
||||
LL | 0b10f32;
|
||||
| ^^^^^^^ not supported
|
||||
|
||||
error: integer literal is too large
|
||||
--> $DIR/error-stage.rs:79:5
|
||||
|
|
||||
LL | 999340282366920938463463374607431768211455999;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
#![feature(rustc_attrs)]
|
||||
|
||||
extern
|
||||
"C"suffix //~ ERROR suffixes on string literals are invalid
|
||||
fn foo() {}
|
||||
|
@ -24,3 +26,19 @@ fn main() {
|
|||
1.0suffix; //~ ERROR invalid suffix `suffix` for float literal
|
||||
1.0e10suffix; //~ ERROR invalid suffix `suffix` for float literal
|
||||
}
|
||||
|
||||
#[rustc_dummy = "string"suffix]
|
||||
//~^ ERROR unexpected expression: `"string"suffix`
|
||||
fn f() {}
|
||||
|
||||
#[must_use = "string"suffix]
|
||||
//~^ ERROR unexpected expression: `"string"suffix`
|
||||
fn g() {}
|
||||
|
||||
#[link(name = "string"suffix)]
|
||||
//~^ ERROR suffixes on string literals are invalid
|
||||
extern "C" {}
|
||||
|
||||
#[rustc_layout_scalar_valid_range_start(0suffix)]
|
||||
//~^ ERROR invalid suffix `suffix` for number literal
|
||||
struct S;
|
||||
|
|
|
@ -1,53 +1,79 @@
|
|||
error: suffixes on string literals are invalid
|
||||
--> $DIR/bad-lit-suffixes.rs:2:5
|
||||
--> $DIR/bad-lit-suffixes.rs:4:5
|
||||
|
|
||||
LL | "C"suffix
|
||||
| ^^^^^^^^^ invalid suffix `suffix`
|
||||
|
||||
error: suffixes on string literals are invalid
|
||||
--> $DIR/bad-lit-suffixes.rs:6:5
|
||||
--> $DIR/bad-lit-suffixes.rs:8:5
|
||||
|
|
||||
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:10:5
|
||||
--> $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
|
||||
|
|
||||
LL | ""suffix;
|
||||
| ^^^^^^^^ invalid suffix `suffix`
|
||||
|
||||
error: suffixes on byte string literals are invalid
|
||||
--> $DIR/bad-lit-suffixes.rs:11:5
|
||||
--> $DIR/bad-lit-suffixes.rs:13:5
|
||||
|
|
||||
LL | b""suffix;
|
||||
| ^^^^^^^^^ invalid suffix `suffix`
|
||||
|
||||
error: suffixes on string literals are invalid
|
||||
--> $DIR/bad-lit-suffixes.rs:12:5
|
||||
--> $DIR/bad-lit-suffixes.rs:14:5
|
||||
|
|
||||
LL | r#""#suffix;
|
||||
| ^^^^^^^^^^^ invalid suffix `suffix`
|
||||
|
||||
error: suffixes on byte string literals are invalid
|
||||
--> $DIR/bad-lit-suffixes.rs:13:5
|
||||
--> $DIR/bad-lit-suffixes.rs:15:5
|
||||
|
|
||||
LL | br#""#suffix;
|
||||
| ^^^^^^^^^^^^ invalid suffix `suffix`
|
||||
|
||||
error: suffixes on char literals are invalid
|
||||
--> $DIR/bad-lit-suffixes.rs:14:5
|
||||
--> $DIR/bad-lit-suffixes.rs:16:5
|
||||
|
|
||||
LL | 'a'suffix;
|
||||
| ^^^^^^^^^ invalid suffix `suffix`
|
||||
|
||||
error: suffixes on byte literals are invalid
|
||||
--> $DIR/bad-lit-suffixes.rs:15:5
|
||||
--> $DIR/bad-lit-suffixes.rs:17:5
|
||||
|
|
||||
LL | b'a'suffix;
|
||||
| ^^^^^^^^^^ invalid suffix `suffix`
|
||||
|
||||
error: invalid width `1024` for integer literal
|
||||
--> $DIR/bad-lit-suffixes.rs:17:5
|
||||
--> $DIR/bad-lit-suffixes.rs:19:5
|
||||
|
|
||||
LL | 1234u1024;
|
||||
| ^^^^^^^^^
|
||||
|
@ -55,7 +81,7 @@ LL | 1234u1024;
|
|||
= help: valid widths are 8, 16, 32, 64 and 128
|
||||
|
||||
error: invalid width `1024` for integer literal
|
||||
--> $DIR/bad-lit-suffixes.rs:18:5
|
||||
--> $DIR/bad-lit-suffixes.rs:20:5
|
||||
|
|
||||
LL | 1234i1024;
|
||||
| ^^^^^^^^^
|
||||
|
@ -63,7 +89,7 @@ LL | 1234i1024;
|
|||
= help: valid widths are 8, 16, 32, 64 and 128
|
||||
|
||||
error: invalid width `1024` for float literal
|
||||
--> $DIR/bad-lit-suffixes.rs:19:5
|
||||
--> $DIR/bad-lit-suffixes.rs:21:5
|
||||
|
|
||||
LL | 1234f1024;
|
||||
| ^^^^^^^^^
|
||||
|
@ -71,7 +97,7 @@ LL | 1234f1024;
|
|||
= help: valid widths are 32 and 64
|
||||
|
||||
error: invalid width `1024` for float literal
|
||||
--> $DIR/bad-lit-suffixes.rs:20:5
|
||||
--> $DIR/bad-lit-suffixes.rs:22:5
|
||||
|
|
||||
LL | 1234.5f1024;
|
||||
| ^^^^^^^^^^^
|
||||
|
@ -79,7 +105,7 @@ LL | 1234.5f1024;
|
|||
= help: valid widths are 32 and 64
|
||||
|
||||
error: invalid suffix `suffix` for number literal
|
||||
--> $DIR/bad-lit-suffixes.rs:22:5
|
||||
--> $DIR/bad-lit-suffixes.rs:24:5
|
||||
|
|
||||
LL | 1234suffix;
|
||||
| ^^^^^^^^^^ invalid suffix `suffix`
|
||||
|
@ -87,7 +113,7 @@ LL | 1234suffix;
|
|||
= help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
|
||||
|
||||
error: invalid suffix `suffix` for number literal
|
||||
--> $DIR/bad-lit-suffixes.rs:23:5
|
||||
--> $DIR/bad-lit-suffixes.rs:25:5
|
||||
|
|
||||
LL | 0b101suffix;
|
||||
| ^^^^^^^^^^^ invalid suffix `suffix`
|
||||
|
@ -95,7 +121,7 @@ LL | 0b101suffix;
|
|||
= help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
|
||||
|
||||
error: invalid suffix `suffix` for float literal
|
||||
--> $DIR/bad-lit-suffixes.rs:24:5
|
||||
--> $DIR/bad-lit-suffixes.rs:26:5
|
||||
|
|
||||
LL | 1.0suffix;
|
||||
| ^^^^^^^^^ invalid suffix `suffix`
|
||||
|
@ -103,12 +129,12 @@ LL | 1.0suffix;
|
|||
= help: valid suffixes are `f32` and `f64`
|
||||
|
||||
error: invalid suffix `suffix` for float literal
|
||||
--> $DIR/bad-lit-suffixes.rs:25:5
|
||||
--> $DIR/bad-lit-suffixes.rs:27:5
|
||||
|
|
||||
LL | 1.0e10suffix;
|
||||
| ^^^^^^^^^^^^ invalid suffix `suffix`
|
||||
|
|
||||
= help: valid suffixes are `f32` and `f64`
|
||||
|
||||
error: aborting due to 16 previous errors
|
||||
error: aborting due to 20 previous errors
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 <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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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::<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 {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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<'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()
|
||||
|
|
|
@ -260,7 +260,9 @@ impl Rewrite for ast::NestedMetaItem {
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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<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,
|
||||
),
|
||||
|
@ -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<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() {
|
||||
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue