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.

This commit changes the language very slightly. Some programs that used
to not compile now will compile. This is because some invalid literals
that are removed by `cfg` or attribute macros will no longer trigger
errors. See this comment for more details:
https://github.com/rust-lang/rust/pull/102944#issuecomment-1277476773
This commit is contained in:
Nicholas Nethercote 2022-10-10 13:40:56 +11:00
parent 8a2d0f255d
commit f2d83ed1ac
12 changed files with 90 additions and 77 deletions

View File

@ -73,12 +73,21 @@ fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) {
}
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)
{

View File

@ -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 @@ fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr)
(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 @@ fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr)
{
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 @@ fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr)
{
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 @@ fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr)
(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,

View File

@ -5,11 +5,13 @@
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 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
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 @@ pub fn new(lint_fraction_readability: bool) -> Self {
}
}
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 @@ fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
| 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 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
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);
});
}
}

View File

@ -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]),

View File

@ -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;

View File

@ -9,7 +9,8 @@
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 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
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");
}
}
}

View File

@ -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,

View File

@ -56,11 +56,11 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
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);
}
}
}

View File

@ -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 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
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(

View File

@ -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 {

View File

@ -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),

View File

@ -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()