Merge commit '0621446356' into clippy-subtree-update

This commit is contained in:
Philipp Krones 2025-04-22 16:10:59 +02:00
parent ed892e72dd
commit ff428d91c2
746 changed files with 13925 additions and 7188 deletions

View file

@ -348,11 +348,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
safety: rs,
define_opaque: _,
}),
) => eq_id(*li, *ri)
&& lm == rm
&& ls == rs
&& eq_ty(lt, rt)
&& eq_expr_opt(le.as_ref(), re.as_ref()),
) => eq_id(*li, *ri) && lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()),
(
Const(box ConstItem {
defaultness: ld,
@ -370,11 +366,13 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
expr: re,
define_opaque: _,
}),
) => eq_defaultness(*ld, *rd)
) => {
eq_defaultness(*ld, *rd)
&& eq_id(*li, *ri)
&& eq_generics(lg, rg)
&& eq_ty(lt, rt)
&& eq_expr_opt(le.as_ref(), re.as_ref()),
&& eq_expr_opt(le.as_ref(), re.as_ref())
},
(
Fn(box ast::Fn {
defaultness: ld,
@ -440,7 +438,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
},
(Enum(li, le, lg), Enum(ri, re, rg)) => {
eq_id(*li, *ri) && over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg)
}
},
(Struct(li, lv, lg), Struct(ri, rv, rg)) | (Union(li, lv, lg), Union(ri, rv, rg)) => {
eq_id(*li, *ri) && eq_variant_data(lv, rv) && eq_generics(lg, rg)
},
@ -471,7 +469,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
},
(TraitAlias(li, lg, lb), TraitAlias(ri, rg, rb)) => {
eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound)
}
},
(
Impl(box ast::Impl {
safety: lu,
@ -506,7 +504,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
(MacroDef(li, ld), MacroDef(ri, rd)) => {
eq_id(*li, *ri) && ld.macro_rules == rd.macro_rules && eq_delim_args(&ld.body, &rd.body)
}
},
_ => false,
}
}
@ -531,13 +529,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
safety: rs,
define_opaque: _,
}),
) => {
eq_id(*li, *ri)
&& eq_ty(lt, rt)
&& lm == rm
&& eq_expr_opt(le.as_ref(), re.as_ref())
&& ls == rs
}
) => eq_id(*li, *ri) && eq_ty(lt, rt) && lm == rm && eq_expr_opt(le.as_ref(), re.as_ref()) && ls == rs,
(
Fn(box ast::Fn {
defaultness: ld,
@ -620,7 +612,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
&& eq_generics(lg, rg)
&& eq_ty(lt, rt)
&& eq_expr_opt(le.as_ref(), re.as_ref())
}
},
(
Fn(box ast::Fn {
defaultness: ld,

View file

@ -43,14 +43,16 @@ pub enum Constant<'tcx> {
Char(char),
/// An integer's bit representation.
Int(u128),
/// An `f16`.
F16(f16),
/// An `f16` bitcast to a `u16`.
// FIXME(f16_f128): use `f16` once builtins are available on all host tools platforms.
F16(u16),
/// An `f32`.
F32(f32),
/// An `f64`.
F64(f64),
/// An `f128`.
F128(f128),
/// An `f128` bitcast to a `u128`.
// FIXME(f16_f128): use `f128` once builtins are available on all host tools platforms.
F128(u128),
/// `true` or `false`.
Bool(bool),
/// An array of constants.
@ -177,7 +179,7 @@ impl Hash for Constant<'_> {
},
Self::F16(f) => {
// FIXME(f16_f128): once conversions to/from `f128` are available on all platforms,
f.to_bits().hash(state);
f.hash(state);
},
Self::F32(f) => {
f64::from(f).to_bits().hash(state);
@ -186,7 +188,7 @@ impl Hash for Constant<'_> {
f.to_bits().hash(state);
},
Self::F128(f) => {
f.to_bits().hash(state);
f.hash(state);
},
Self::Bool(b) => {
b.hash(state);
@ -292,12 +294,12 @@ impl Constant<'_> {
fn parse_f16(s: &str) -> Self {
let f: Half = s.parse().unwrap();
Self::F16(f16::from_bits(f.to_bits().try_into().unwrap()))
Self::F16(f.to_bits().try_into().unwrap())
}
fn parse_f128(s: &str) -> Self {
let f: Quad = s.parse().unwrap();
Self::F128(f128::from_bits(f.to_bits()))
Self::F128(f.to_bits())
}
}
@ -868,10 +870,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option
ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))),
ty::Float(FloatTy::F16) => Some(Constant::F16(f16::from_bits(int.into()))),
ty::Float(FloatTy::F16) => Some(Constant::F16(int.into())),
ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(int.into()))),
ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(int.into()))),
ty::Float(FloatTy::F128) => Some(Constant::F128(f128::from_bits(int.into()))),
ty::Float(FloatTy::F128) => Some(Constant::F128(int.into())),
ty::RawPtr(_, _) => Some(Constant::RawPtr(int.to_bits(int.size()))),
_ => None,
},
@ -892,10 +894,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option
let range = alloc_range(offset + size * idx, size);
let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?;
res.push(match flt {
FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().discard_err()?)),
FloatTy::F16 => Constant::F16(val.to_u16().discard_err()?),
FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().discard_err()?)),
FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().discard_err()?)),
FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().discard_err()?)),
FloatTy::F128 => Constant::F128(val.to_u128().discard_err()?),
});
}
Some(Constant::Vec(res))

View file

@ -17,16 +17,16 @@ use rustc_span::Span;
use std::env;
fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) {
if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
if let Some(lint) = lint.name_lower().strip_prefix("clippy::") {
diag.help(format!(
"for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}",
&option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
// extract just major + minor version and ignore patch versions
format!("rust-{}", n.rsplit_once('.').unwrap().1)
})
));
}
if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err()
&& let Some(lint) = lint.name_lower().strip_prefix("clippy::")
{
diag.help(format!(
"for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}",
&option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
// extract just major + minor version and ignore patch versions
format!("rust-{}", n.rsplit_once('.').unwrap().1)
})
));
}
}

View file

@ -118,18 +118,17 @@ impl<'hir> IfLet<'hir> {
) = expr.kind
{
let mut iter = cx.tcx.hir_parent_iter(expr.hir_id);
if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() {
if let Some((
if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next()
&& let Some((
_,
Node::Expr(Expr {
kind: ExprKind::Loop(_, _, LoopSource::While, _),
..
}),
)) = iter.next()
{
// while loop desugar
return None;
}
{
// while loop desugar
return None;
}
return Some(Self {
let_pat,
@ -176,6 +175,12 @@ impl<'hir> IfLetOrMatch<'hir> {
),
}
}
pub fn scrutinee(&self) -> &'hir Expr<'hir> {
match self {
Self::Match(scrutinee, _, _) | Self::IfLet(scrutinee, _, _, _, _) => scrutinee,
}
}
}
/// An `if` or `if let` expression

View file

@ -148,7 +148,7 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> {
impl HirEqInterExpr<'_, '_, '_> {
pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
match (&left.kind, &right.kind) {
(&StmtKind::Let(l), &StmtKind::Let(r)) => {
(StmtKind::Let(l), StmtKind::Let(r)) => {
// This additional check ensures that the type of the locals are equivalent even if the init
// expression or type have some inferred parts.
if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
@ -166,7 +166,7 @@ impl HirEqInterExpr<'_, '_, '_> {
&& both(l.els.as_ref(), r.els.as_ref(), |l, r| self.eq_block(l, r))
&& self.eq_pat(l.pat, r.pat)
},
(&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r),
(StmtKind::Expr(l), StmtKind::Expr(r)) | (StmtKind::Semi(l), StmtKind::Semi(r)) => self.eq_expr(l, r),
_ => false,
}
}
@ -260,7 +260,7 @@ impl HirEqInterExpr<'_, '_, '_> {
fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
macro_backtrace(expr.span).last().is_some_and(|macro_call| {
matches!(
&self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
Some(sym::todo_macro | sym::unimplemented_macro)
)
})
@ -301,58 +301,58 @@ impl HirEqInterExpr<'_, '_, '_> {
reduce_exprkind(self.inner.cx, &left.kind),
reduce_exprkind(self.inner.cx, &right.kind),
) {
(&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => {
(ExprKind::AddrOf(lb, l_mut, le), ExprKind::AddrOf(rb, r_mut, re)) => {
lb == rb && l_mut == r_mut && self.eq_expr(le, re)
},
(&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
(&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => {
(ExprKind::Array(l), ExprKind::Array(r)) => self.eq_exprs(l, r),
(ExprKind::Assign(ll, lr, _), ExprKind::Assign(rl, rr, _)) => {
self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
},
(&ExprKind::AssignOp(ref lo, ll, lr), &ExprKind::AssignOp(ref ro, rl, rr)) => {
(ExprKind::AssignOp(lo, ll, lr), ExprKind::AssignOp(ro, rl, rr)) => {
self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
},
(&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r),
(&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => {
(ExprKind::Block(l, _), ExprKind::Block(r, _)) => self.eq_block(l, r),
(ExprKind::Binary(l_op, ll, lr), ExprKind::Binary(r_op, rl, rr)) => {
l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
|| swap_binop(l_op.node, ll, lr).is_some_and(|(l_op, ll, lr)| {
l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
})
},
(&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
(ExprKind::Break(li, le), ExprKind::Break(ri, re)) => {
both(li.label.as_ref(), ri.label.as_ref(), |l, r| l.ident.name == r.ident.name)
&& both(le.as_ref(), re.as_ref(), |l, r| self.eq_expr(l, r))
},
(&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
(ExprKind::Call(l_fun, l_args), ExprKind::Call(r_fun, r_args)) => {
self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
},
(&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) => {
(ExprKind::Cast(lx, lt), ExprKind::Cast(rx, rt)) => {
self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
},
(&ExprKind::Closure(_l), &ExprKind::Closure(_r)) => false,
(&ExprKind::ConstBlock(lb), &ExprKind::ConstBlock(rb)) => self.eq_body(lb.body, rb.body),
(&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
(ExprKind::Closure(_l), ExprKind::Closure(_r)) => false,
(ExprKind::ConstBlock(lb), ExprKind::ConstBlock(rb)) => self.eq_body(lb.body, rb.body),
(ExprKind::Continue(li), ExprKind::Continue(ri)) => {
both(li.label.as_ref(), ri.label.as_ref(), |l, r| l.ident.name == r.ident.name)
},
(&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
(&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => {
(ExprKind::DropTemps(le), ExprKind::DropTemps(re)) => self.eq_expr(le, re),
(ExprKind::Field(l_f_exp, l_f_ident), ExprKind::Field(r_f_exp, r_f_ident)) => {
l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp)
},
(&ExprKind::Index(la, li, _), &ExprKind::Index(ra, ri, _)) => self.eq_expr(la, ra) && self.eq_expr(li, ri),
(&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => {
(ExprKind::Index(la, li, _), ExprKind::Index(ra, ri, _)) => self.eq_expr(la, ra) && self.eq_expr(li, ri),
(ExprKind::If(lc, lt, le), ExprKind::If(rc, rt, re)) => {
self.eq_expr(lc, rc) && self.eq_expr(lt, rt)
&& both(le.as_ref(), re.as_ref(), |l, r| self.eq_expr(l, r))
},
(&ExprKind::Let(l), &ExprKind::Let(r)) => {
(ExprKind::Let(l), ExprKind::Let(r)) => {
self.eq_pat(l.pat, r.pat)
&& both(l.ty.as_ref(), r.ty.as_ref(), |l, r| self.eq_ty(l, r))
&& self.eq_expr(l.init, r.init)
},
(ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node,
(&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => {
(ExprKind::Loop(lb, ll, lls, _), ExprKind::Loop(rb, rl, rls, _)) => {
lls == rls && self.eq_block(lb, rb)
&& both(ll.as_ref(), rl.as_ref(), |l, r| l.ident.name == r.ident.name)
},
(&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => {
(ExprKind::Match(le, la, ls), ExprKind::Match(re, ra, rs)) => {
(ls == rs || (matches!((ls, rs), (TryDesugar(_), TryDesugar(_)))))
&& self.eq_expr(le, re)
&& over(la, ra, |l, r| {
@ -362,27 +362,27 @@ impl HirEqInterExpr<'_, '_, '_> {
})
},
(
&ExprKind::MethodCall(l_path, l_receiver, l_args, _),
&ExprKind::MethodCall(r_path, r_receiver, r_args, _),
ExprKind::MethodCall(l_path, l_receiver, l_args, _),
ExprKind::MethodCall(r_path, r_receiver, r_args, _),
) => {
self.inner.allow_side_effects
&& self.eq_path_segment(l_path, r_path)
&& self.eq_expr(l_receiver, r_receiver)
&& self.eq_exprs(l_args, r_args)
},
(&ExprKind::UnsafeBinderCast(lkind, le, None), &ExprKind::UnsafeBinderCast(rkind, re, None)) =>
(ExprKind::UnsafeBinderCast(lkind, le, None), ExprKind::UnsafeBinderCast(rkind, re, None)) =>
lkind == rkind && self.eq_expr(le, re),
(&ExprKind::UnsafeBinderCast(lkind, le, Some(lt)), &ExprKind::UnsafeBinderCast(rkind, re, Some(rt))) =>
(ExprKind::UnsafeBinderCast(lkind, le, Some(lt)), ExprKind::UnsafeBinderCast(rkind, re, Some(rt))) =>
lkind == rkind && self.eq_expr(le, re) && self.eq_ty(lt, rt),
(&ExprKind::OffsetOf(l_container, l_fields), &ExprKind::OffsetOf(r_container, r_fields)) => {
(ExprKind::OffsetOf(l_container, l_fields), ExprKind::OffsetOf(r_container, r_fields)) => {
self.eq_ty(l_container, r_container) && over(l_fields, r_fields, |l, r| l.name == r.name)
},
(ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r),
(&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => {
(ExprKind::Repeat(le, ll), ExprKind::Repeat(re, rl)) => {
self.eq_expr(le, re) && self.eq_const_arg(ll, rl)
},
(ExprKind::Ret(l), ExprKind::Ret(r)) => both(l.as_ref(), r.as_ref(), |l, r| self.eq_expr(l, r)),
(&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
(ExprKind::Struct(l_path, lf, lo), ExprKind::Struct(r_path, rf, ro)) => {
self.eq_qpath(l_path, r_path)
&& match (lo, ro) {
(StructTailExpr::Base(l),StructTailExpr::Base(r)) => self.eq_expr(l, r),
@ -392,58 +392,58 @@ impl HirEqInterExpr<'_, '_, '_> {
}
&& over(lf, rf, |l, r| self.eq_expr_field(l, r))
},
(&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
(&ExprKind::Use(l_expr, _), &ExprKind::Use(r_expr, _)) => self.eq_expr(l_expr, r_expr),
(&ExprKind::Type(le, lt), &ExprKind::Type(re, rt)) => self.eq_expr(le, re) && self.eq_ty(lt, rt),
(&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
(&ExprKind::Yield(le, _), &ExprKind::Yield(re, _)) => return self.eq_expr(le, re),
(ExprKind::Tup(l_tup), ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
(ExprKind::Use(l_expr, _), ExprKind::Use(r_expr, _)) => self.eq_expr(l_expr, r_expr),
(ExprKind::Type(le, lt), ExprKind::Type(re, rt)) => self.eq_expr(le, re) && self.eq_ty(lt, rt),
(ExprKind::Unary(l_op, le), ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
(ExprKind::Yield(le, _), ExprKind::Yield(re, _)) => return self.eq_expr(le, re),
(
// Else branches for branches above, grouped as per `match_same_arms`.
| &ExprKind::AddrOf(..)
| &ExprKind::Array(..)
| &ExprKind::Assign(..)
| &ExprKind::AssignOp(..)
| &ExprKind::Binary(..)
| &ExprKind::Become(..)
| &ExprKind::Block(..)
| &ExprKind::Break(..)
| &ExprKind::Call(..)
| &ExprKind::Cast(..)
| &ExprKind::ConstBlock(..)
| &ExprKind::Continue(..)
| &ExprKind::DropTemps(..)
| &ExprKind::Field(..)
| &ExprKind::Index(..)
| &ExprKind::If(..)
| &ExprKind::Let(..)
| &ExprKind::Lit(..)
| &ExprKind::Loop(..)
| &ExprKind::Match(..)
| &ExprKind::MethodCall(..)
| &ExprKind::OffsetOf(..)
| &ExprKind::Path(..)
| &ExprKind::Repeat(..)
| &ExprKind::Ret(..)
| &ExprKind::Struct(..)
| &ExprKind::Tup(..)
| &ExprKind::Use(..)
| &ExprKind::Type(..)
| &ExprKind::Unary(..)
| &ExprKind::Yield(..)
| &ExprKind::UnsafeBinderCast(..)
| ExprKind::AddrOf(..)
| ExprKind::Array(..)
| ExprKind::Assign(..)
| ExprKind::AssignOp(..)
| ExprKind::Binary(..)
| ExprKind::Become(..)
| ExprKind::Block(..)
| ExprKind::Break(..)
| ExprKind::Call(..)
| ExprKind::Cast(..)
| ExprKind::ConstBlock(..)
| ExprKind::Continue(..)
| ExprKind::DropTemps(..)
| ExprKind::Field(..)
| ExprKind::Index(..)
| ExprKind::If(..)
| ExprKind::Let(..)
| ExprKind::Lit(..)
| ExprKind::Loop(..)
| ExprKind::Match(..)
| ExprKind::MethodCall(..)
| ExprKind::OffsetOf(..)
| ExprKind::Path(..)
| ExprKind::Repeat(..)
| ExprKind::Ret(..)
| ExprKind::Struct(..)
| ExprKind::Tup(..)
| ExprKind::Use(..)
| ExprKind::Type(..)
| ExprKind::Unary(..)
| ExprKind::Yield(..)
| ExprKind::UnsafeBinderCast(..)
// --- Special cases that do not have a positive branch.
// `Err` represents an invalid expression, so let's never assume that
// an invalid expressions is equal to anything.
| &ExprKind::Err(..)
| ExprKind::Err(..)
// For the time being, we always consider that two closures are unequal.
// This behavior may change in the future.
| &ExprKind::Closure(..)
| ExprKind::Closure(..)
// For the time being, we always consider that two instances of InlineAsm are different.
// This behavior may change in the future.
| &ExprKind::InlineAsm(_)
| ExprKind::InlineAsm(_)
, _
) => false,
};
@ -494,11 +494,11 @@ impl HirEqInterExpr<'_, '_, '_> {
fn eq_pat_expr(&mut self, left: &PatExpr<'_>, right: &PatExpr<'_>) -> bool {
match (&left.kind, &right.kind) {
(
&PatExprKind::Lit {
PatExprKind::Lit {
lit: left,
negated: left_neg,
},
&PatExprKind::Lit {
PatExprKind::Lit {
lit: right,
negated: right_neg,
},
@ -512,47 +512,47 @@ impl HirEqInterExpr<'_, '_, '_> {
/// Checks whether two patterns are the same.
fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
match (&left.kind, &right.kind) {
(&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r),
(&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => {
(PatKind::Box(l), PatKind::Box(r)) => self.eq_pat(l, r),
(PatKind::Struct(lp, la, ..), PatKind::Struct(rp, ra, ..)) => {
self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r))
},
(&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => {
(PatKind::TupleStruct(lp, la, ls), PatKind::TupleStruct(rp, ra, rs)) => {
self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
},
(&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => {
(PatKind::Binding(lb, li, _, lp), PatKind::Binding(rb, ri, _, rp)) => {
let eq = lb == rb && both(lp.as_ref(), rp.as_ref(), |l, r| self.eq_pat(l, r));
if eq {
self.locals.insert(li, ri);
self.locals.insert(*li, *ri);
}
eq
},
(&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r),
(&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
(&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
(PatKind::Expr(l), PatKind::Expr(r)) => self.eq_pat_expr(l, r),
(PatKind::Tuple(l, ls), PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
(PatKind::Range(ls, le, li), PatKind::Range(rs, re, ri)) => {
both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b))
&& both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b))
&& (li == ri)
},
(&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re),
(&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => {
(PatKind::Ref(le, lm), PatKind::Ref(re, rm)) => lm == rm && self.eq_pat(le, re),
(PatKind::Slice(ls, li, le), PatKind::Slice(rs, ri, re)) => {
over(ls, rs, |l, r| self.eq_pat(l, r))
&& over(le, re, |l, r| self.eq_pat(l, r))
&& both(li.as_ref(), ri.as_ref(), |l, r| self.eq_pat(l, r))
},
(&PatKind::Wild, &PatKind::Wild) => true,
(PatKind::Wild, PatKind::Wild) => true,
_ => false,
}
}
fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
match (left, right) {
(&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => {
(QPath::Resolved(lty, lpath), QPath::Resolved(rty, rpath)) => {
both(lty.as_ref(), rty.as_ref(), |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
},
(&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => {
(QPath::TypeRelative(lty, lseg), QPath::TypeRelative(rty, rseg)) => {
self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
},
(&QPath::LangItem(llang_item, ..), &QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item,
(QPath::LangItem(llang_item, ..), QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item,
_ => false,
}
}
@ -611,15 +611,15 @@ impl HirEqInterExpr<'_, '_, '_> {
pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
match (&left.kind, &right.kind) {
(&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec),
(&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_const_arg(ll, rl),
(TyKind::Slice(l_vec), TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec),
(TyKind::Array(lt, ll), TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_const_arg(ll, rl),
(TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty),
(TyKind::Ref(_, l_rmut), TyKind::Ref(_, r_rmut)) => {
l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty)
},
(TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r),
(&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
(&TyKind::Infer(()), &TyKind::Infer(())) => true,
(TyKind::Tup(l), TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
(TyKind::Infer(()), TyKind::Infer(())) => true,
_ => false,
}
}
@ -853,9 +853,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
std::mem::discriminant(&e.kind).hash(&mut self.s);
match e.kind {
match &e.kind {
ExprKind::AddrOf(kind, m, e) => {
std::mem::discriminant(&kind).hash(&mut self.s);
std::mem::discriminant(kind).hash(&mut self.s);
m.hash(&mut self.s);
self.hash_expr(e);
},
@ -871,7 +871,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(l);
self.hash_expr(r);
},
ExprKind::AssignOp(ref o, l, r) => {
ExprKind::AssignOp(o, l, r) => {
std::mem::discriminant(&o.node).hash(&mut self.s);
self.hash_expr(l);
self.hash_expr(r);
@ -887,11 +887,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(l);
self.hash_expr(r);
},
ExprKind::Break(i, ref j) => {
ExprKind::Break(i, j) => {
if let Some(i) = i.label {
self.hash_name(i.ident.name);
}
if let Some(j) = *j {
if let Some(j) = j {
self.hash_expr(j);
}
},
@ -903,20 +903,20 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(e);
self.hash_ty(ty);
},
ExprKind::Closure(&Closure {
ExprKind::Closure(Closure {
capture_clause, body, ..
}) => {
std::mem::discriminant(&capture_clause).hash(&mut self.s);
std::mem::discriminant(capture_clause).hash(&mut self.s);
// closures inherit TypeckResults
self.hash_expr(self.cx.tcx.hir_body(body).value);
self.hash_expr(self.cx.tcx.hir_body(*body).value);
},
ExprKind::ConstBlock(ref l_id) => {
ExprKind::ConstBlock(l_id) => {
self.hash_body(l_id.body);
},
ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
self.hash_expr(e);
},
ExprKind::Field(e, ref f) => {
ExprKind::Field(e, f) => {
self.hash_expr(e);
self.hash_name(f.name);
},
@ -991,23 +991,23 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
ExprKind::Lit(l) => {
l.node.hash(&mut self.s);
},
ExprKind::Loop(b, ref i, ..) => {
ExprKind::Loop(b, i, ..) => {
self.hash_block(b);
if let Some(i) = *i {
if let Some(i) = i {
self.hash_name(i.ident.name);
}
},
ExprKind::If(cond, then, ref else_opt) => {
ExprKind::If(cond, then, else_opt) => {
self.hash_expr(cond);
self.hash_expr(then);
if let Some(e) = *else_opt {
if let Some(e) = else_opt {
self.hash_expr(e);
}
},
ExprKind::Match(e, arms, ref s) => {
ExprKind::Match(e, arms, s) => {
self.hash_expr(e);
for arm in arms {
for arm in *arms {
self.hash_pat(arm.pat);
if let Some(e) = arm.guard {
self.hash_expr(e);
@ -1017,38 +1017,38 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
s.hash(&mut self.s);
},
ExprKind::MethodCall(path, receiver, args, ref _fn_span) => {
ExprKind::MethodCall(path, receiver, args, _fn_span) => {
self.hash_name(path.ident.name);
self.hash_expr(receiver);
self.hash_exprs(args);
},
ExprKind::OffsetOf(container, fields) => {
self.hash_ty(container);
for field in fields {
for field in *fields {
self.hash_name(field.name);
}
},
ExprKind::Path(ref qpath) => {
ExprKind::Path(qpath) => {
self.hash_qpath(qpath);
},
ExprKind::Repeat(e, len) => {
self.hash_expr(e);
self.hash_const_arg(len);
},
ExprKind::Ret(ref e) => {
if let Some(e) = *e {
ExprKind::Ret(e) => {
if let Some(e) = e {
self.hash_expr(e);
}
},
ExprKind::Struct(path, fields, ref expr) => {
ExprKind::Struct(path, fields, expr) => {
self.hash_qpath(path);
for f in fields {
for f in *fields {
self.hash_name(f.ident.name);
self.hash_expr(f.expr);
}
if let StructTailExpr::Base(e) = *expr {
if let StructTailExpr::Base(e) = expr {
self.hash_expr(e);
}
},
@ -1059,11 +1059,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(expr);
},
ExprKind::Unary(lop, le) => {
std::mem::discriminant(&lop).hash(&mut self.s);
std::mem::discriminant(lop).hash(&mut self.s);
self.hash_expr(le);
},
ExprKind::UnsafeBinderCast(kind, expr, ty) => {
std::mem::discriminant(&kind).hash(&mut self.s);
std::mem::discriminant(kind).hash(&mut self.s);
self.hash_expr(expr);
if let Some(ty) = ty {
self.hash_ty(ty);
@ -1084,7 +1084,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
}
pub fn hash_qpath(&mut self, p: &QPath<'_>) {
match *p {
match p {
QPath::Resolved(_, path) => {
self.hash_path(path);
},
@ -1092,7 +1092,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_name(path.ident.name);
},
QPath::LangItem(lang_item, ..) => {
std::mem::discriminant(&lang_item).hash(&mut self.s);
std::mem::discriminant(lang_item).hash(&mut self.s);
},
}
// self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
@ -1123,11 +1123,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
pub fn hash_pat(&mut self, pat: &Pat<'_>) {
std::mem::discriminant(&pat.kind).hash(&mut self.s);
match pat.kind {
match &pat.kind {
PatKind::Missing => unreachable!(),
PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => {
std::mem::discriminant(&by_ref).hash(&mut self.s);
std::mem::discriminant(&mutability).hash(&mut self.s);
std::mem::discriminant(by_ref).hash(&mut self.s);
std::mem::discriminant(mutability).hash(&mut self.s);
if let Some(pat) = pat {
self.hash_pat(pat);
}
@ -1135,7 +1135,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat),
PatKind::Expr(expr) => self.hash_pat_expr(expr),
PatKind::Or(pats) => {
for pat in pats {
for pat in *pats {
self.hash_pat(pat);
}
},
@ -1146,44 +1146,44 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
if let Some(e) = e {
self.hash_pat_expr(e);
}
std::mem::discriminant(&i).hash(&mut self.s);
std::mem::discriminant(i).hash(&mut self.s);
},
PatKind::Ref(pat, mu) => {
self.hash_pat(pat);
std::mem::discriminant(&mu).hash(&mut self.s);
std::mem::discriminant(mu).hash(&mut self.s);
},
PatKind::Guard(pat, guard) => {
self.hash_pat(pat);
self.hash_expr(guard);
},
PatKind::Slice(l, m, r) => {
for pat in l {
for pat in *l {
self.hash_pat(pat);
}
if let Some(pat) = m {
self.hash_pat(pat);
}
for pat in r {
for pat in *r {
self.hash_pat(pat);
}
},
PatKind::Struct(ref qpath, fields, e) => {
PatKind::Struct(qpath, fields, e) => {
self.hash_qpath(qpath);
for f in fields {
for f in *fields {
self.hash_name(f.ident.name);
self.hash_pat(f.pat);
}
e.hash(&mut self.s);
},
PatKind::Tuple(pats, e) => {
for pat in pats {
for pat in *pats {
self.hash_pat(pat);
}
e.hash(&mut self.s);
},
PatKind::TupleStruct(ref qpath, pats, e) => {
PatKind::TupleStruct(qpath, pats, e) => {
self.hash_qpath(qpath);
for pat in pats {
for pat in *pats {
self.hash_pat(pat);
}
e.hash(&mut self.s);
@ -1261,7 +1261,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
TyKind::Slice(ty) => {
self.hash_ty(ty);
},
&TyKind::Array(ty, len) => {
TyKind::Array(ty, len) => {
self.hash_ty(ty);
self.hash_const_arg(len);
},
@ -1334,11 +1334,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
for arg in arg_list {
match *arg {
match arg {
GenericArg::Lifetime(l) => self.hash_lifetime(l),
GenericArg::Type(ty) => self.hash_ty(ty.as_unambig_ty()),
GenericArg::Const(ca) => self.hash_const_arg(ca.as_unambig_ct()),
GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()),
GenericArg::Infer(inf) => self.hash_ty(&inf.to_ty()),
}
}
}

View file

@ -1,10 +1,8 @@
#![feature(array_chunks)]
#![feature(box_patterns)]
#![feature(f128)]
#![feature(f16)]
#![feature(if_let_guard)]
#![feature(macro_metavar_expr)]
#![feature(macro_metavar_expr_concat)]
#![feature(macro_metavar_expr)]
#![feature(let_chains)]
#![feature(never_type)]
#![feature(rustc_private)]
@ -53,9 +51,6 @@ extern crate rustc_span;
extern crate rustc_trait_selection;
extern crate smallvec;
#[macro_use]
pub mod sym_helper;
pub mod ast_utils;
pub mod attrs;
mod check_proc_macro;
@ -108,10 +103,10 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet};
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
use rustc_hir::{
self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext,
Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItem,
ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem,
TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
CoroutineDesugaring, CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg,
GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource,
Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath,
Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
};
use rustc_lexer::{TokenKind, tokenize};
use rustc_lint::{LateContext, Level, Lint, LintContext};
@ -129,6 +124,7 @@ use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{Ident, Symbol, kw};
use rustc_span::{InnerSpan, Span};
use source::walk_span_to_context;
use visitors::{Visitable, for_each_unconsumed_temporary};
use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
@ -370,10 +366,10 @@ pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
/// Checks if a method is defined in an impl of a diagnostic item
pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() {
return cx.tcx.is_diagnostic_item(diag_item, adt.did());
}
if let Some(impl_did) = cx.tcx.impl_of_method(def_id)
&& let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
{
return cx.tcx.is_diagnostic_item(diag_item, adt.did());
}
false
}
@ -460,10 +456,10 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
QPath::Resolved(_, path) => match_path(path, segments),
QPath::TypeRelative(ty, segment) => match ty.kind {
TyKind::Path(ref inner_path) => {
if let [prefix @ .., end] = segments {
if match_qpath(inner_path, prefix) {
return segment.ident.name.as_str() == *end;
}
if let [prefix @ .., end] = segments
&& match_qpath(inner_path, prefix)
{
return segment.ident.name.as_str() == *end;
}
false
},
@ -526,10 +522,10 @@ pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
/// If the expression is a path to a local, returns the canonical `HirId` of the local.
pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
if let Res::Local(id) = path.res {
return Some(id);
}
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
&& let Res::Local(id) = path.res
{
return Some(id);
}
None
}
@ -896,16 +892,14 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
sym::BinaryHeap,
];
if let QPath::TypeRelative(_, method) = path {
if method.ident.name == sym::new {
if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() {
return std_types_symbols.iter().any(|&symbol| {
cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
});
}
}
}
if let QPath::TypeRelative(_, method) = path
&& method.ident.name == sym::new
&& let Some(impl_did) = cx.tcx.impl_of_method(def_id)
&& let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
{
return std_types_symbols.iter().any(|&symbol| {
cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
});
}
false
}
@ -1030,6 +1024,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
_ => false,
}
}
@ -1206,12 +1201,10 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
.adjustments()
.get(child_id)
.map_or(&[][..], |x| &**x)
{
if let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
&& let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
*adjust.last().map_or(target, |a| a.target).kind()
{
return CaptureKind::Ref(mutability);
}
{
return CaptureKind::Ref(mutability);
}
match parent {
@ -1739,10 +1732,10 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool
/// Checks whether the given expression is a constant literal of the given value.
pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
// FIXME: use constant folding
if let ExprKind::Lit(spanned) = expr.kind {
if let LitKind::Int(v, _) = spanned.node {
return v == value;
}
if let ExprKind::Lit(spanned) = expr.kind
&& let LitKind::Int(v, _) = spanned.node
{
return v == value;
}
false
}
@ -1779,10 +1772,10 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
let data = span.ctxt().outer_expn_data();
let new_span = data.call_site;
if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
if mac_name.as_str() == name {
return Some(new_span);
}
if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
&& mac_name.as_str() == name
{
return Some(new_span);
}
span = new_span;
@ -1808,10 +1801,10 @@ pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
let data = span.ctxt().outer_expn_data();
let new_span = data.call_site;
if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
if mac_name.as_str() == name {
return Some(new_span);
}
if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
&& mac_name.as_str() == name
{
return Some(new_span);
}
}
@ -1832,15 +1825,15 @@ pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) ->
/// Checks if an expression is constructing a tuple-like enum variant or struct
pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let ExprKind::Call(fun, _) = expr.kind {
if let ExprKind::Path(ref qp) = fun.kind {
let res = cx.qpath_res(qp, fun.hir_id);
return match res {
Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
_ => false,
};
}
if let ExprKind::Call(fun, _) = expr.kind
&& let ExprKind::Path(ref qp) = fun.kind
{
let res = cx.qpath_res(qp, fun.hir_id);
return match res {
Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
_ => false,
};
}
false
}
@ -1914,10 +1907,10 @@ pub fn is_self(slf: &Param<'_>) -> bool {
}
pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res {
return true;
}
if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
&& let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
{
return true;
}
false
}
@ -2122,10 +2115,10 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
}
// final `else {..}`
if !blocks.is_empty() {
if let ExprKind::Block(block, _) = expr.kind {
blocks.push(block);
}
if !blocks.is_empty()
&& let ExprKind::Block(block, _) = expr.kind
{
blocks.push(block);
}
(conds, blocks)
@ -2140,26 +2133,34 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool {
}
}
/// Peels away all the compiler generated code surrounding the body of an async function,
pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind {
if let ExprKind::Block(
/// Peels away all the compiler generated code surrounding the body of an async closure.
pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
if let ExprKind::Closure(&Closure {
body,
kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
..
}) = expr.kind
&& let ExprKind::Block(
Block {
stmts: [],
expr:
Some(Expr {
kind: ExprKind::DropTemps(expr),
kind: ExprKind::DropTemps(inner_expr),
..
}),
..
},
_,
) = tcx.hir_body(body).value.kind
{
return Some(expr);
}
{
Some(inner_expr)
} else {
None
}
None
}
/// Peels away all the compiler generated code surrounding the body of an async function,
pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
get_async_closure_expr(tcx, body.value)
}
// check if expr is calling method or function with #[must_use] attribute
@ -2631,17 +2632,19 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>
}
pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
if let Res::Def(_, def_id) = path.res {
return cx.tcx.has_attr(def_id, sym::cfg_trace) || cx.tcx.has_attr(def_id, sym::cfg_attr);
}
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
&& let Res::Def(_, def_id) = path.res
{
return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
}
false
}
static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
/// Apply `f()` to the set of test item names.
/// The names are sorted using the default `Symbol` ordering.
fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
let value = map.entry(module);
@ -2653,18 +2656,16 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Sym
if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
&& let item = tcx.hir_item(id)
&& let ItemKind::Const(ident, ty, _generics, _body) = item.kind
{
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
&& let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
// We could also check for the type name `test::TestDescAndFn`
if let Res::Def(DefKind::Struct, _) = path.res {
let has_test_marker = tcx
.hir_attrs(item.hir_id())
.iter()
.any(|a| a.has_name(sym::rustc_test_marker));
if has_test_marker {
names.push(ident.name);
}
}
&& let Res::Def(DefKind::Struct, _) = path.res
{
let has_test_marker = tcx
.hir_attrs(item.hir_id())
.iter()
.any(|a| a.has_name(sym::rustc_test_marker));
if has_test_marker {
names.push(ident.name);
}
}
}
@ -2685,18 +2686,37 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
// Since you can nest functions we need to collect all until we leave
// function scope
.any(|(_id, node)| {
if let Node::Item(item) = node {
if let ItemKind::Fn { ident, .. } = item.kind {
// Note that we have sorted the item names in the visitor,
// so the binary_search gets the same as `contains`, but faster.
return names.binary_search(&ident.name).is_ok();
}
if let Node::Item(item) = node
&& let ItemKind::Fn { ident, .. } = item.kind
{
// Note that we have sorted the item names in the visitor,
// so the binary_search gets the same as `contains`, but faster.
return names.binary_search(&ident.name).is_ok();
}
false
})
})
}
/// Checks if `fn_def_id` has a `#[test]` attribute applied
///
/// This only checks directly applied attributes. To see if a node has a parent function marked with
/// `#[test]` use [`is_in_test_function`].
///
/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function
pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
let id = tcx.local_def_id_to_hir_id(fn_def_id);
if let Node::Item(item) = tcx.hir_node(id)
&& let ItemKind::Fn { ident, .. } = item.kind
{
with_test_item_names(tcx, tcx.parent_module(id), |names| {
names.binary_search(&ident.name).is_ok()
})
} else {
false
}
}
/// Checks if `id` has a `#[cfg(test)]` attribute applied
///
/// This only checks directly applied attributes, to see if a node is inside a `#[cfg(test)]` parent
@ -3728,3 +3748,20 @@ pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::
}
hir_ty
}
/// If `expr` is a desugared `.await`, return the original expression if it does not come from a
/// macro expansion.
pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
&& let ExprKind::Call(_, [into_future_arg]) = match_value.kind
&& let ctxt = expr.span.ctxt()
&& for_each_expr_without_closures(into_future_arg, |e| {
walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
})
.is_none()
{
Some(into_future_arg)
} else {
None
}
}

View file

@ -76,7 +76,7 @@ impl<'tcx> Visitor<'tcx> for V<'_> {
}
if matches!(
ctx,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move | NonMutatingUseContext::Inspect)
| PlaceContext::MutatingUse(MutatingUseContext::Borrow)
) {
self.results[i].local_consume_or_mutate_locs.push(loc);

View file

@ -1,10 +1,10 @@
use crate::sym;
use rustc_ast::Attribute;
use rustc_ast::attr::AttributeExt;
use rustc_attr_parsing::{RustcVersion, parse_version};
use rustc_lint::LateContext;
use rustc_session::Session;
use rustc_span::{Symbol, sym};
use rustc_span::Symbol;
use serde::Deserialize;
use smallvec::SmallVec;
use std::iter::once;
@ -24,10 +24,10 @@ macro_rules! msrv_aliases {
msrv_aliases! {
1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT }
1,85,0 { UINT_FLOAT_MIDPOINT }
1,84,0 { CONST_OPTION_AS_SLICE }
1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR }
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP }
1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP }
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION }
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION, DURATION_ABS_DIFF }
1,80,0 { BOX_INTO_ITER, LAZY_CELL }
1,77,0 { C_STR_LITERALS }
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
@ -40,6 +40,7 @@ msrv_aliases! {
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
1,63,0 { CLONE_INTO }
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN }
1,60,0 { ABS_DIFF }
1,59,0 { THREAD_LOCAL_CONST_INIT }
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF }
1,57,0 { MAP_WHILE }
@ -63,6 +64,7 @@ msrv_aliases! {
1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
1,34,0 { TRY_FROM }
1,33,0 { UNDERSCORE_IMPORTS }
1,32,0 { CONST_IS_POWER_OF_TWO }
1,31,0 { OPTION_REPLACE }
1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
1,29,0 { ITER_FLATTEN }
@ -182,8 +184,7 @@ impl MsrvStack {
}
fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt]) -> Option<RustcVersion> {
let sym_msrv = Symbol::intern("msrv");
let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym_msrv]));
let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym::msrv]));
if let Some(msrv_attr) = msrv_attrs.next() {
if let Some(duplicate) = msrv_attrs.next_back() {

View file

@ -30,9 +30,11 @@ pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]
pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"];
pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"];
pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"];
pub const ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"];
// Paths in clippy itself
pub const MSRV_STACK: [&str; 3] = ["clippy_utils", "msrvs", "MsrvStack"];
pub const CLIPPY_SYM_MODULE: [&str; 2] = ["clippy_utils", "sym"];
// Paths in external crates
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates

View file

@ -142,7 +142,19 @@ pub trait SpanRangeExt: SpanRange {
map_range(cx.sess().source_map(), self.into_range(), f)
}
/// Extends the range to include all preceding whitespace characters.
/// Extends the range to include all preceding whitespace characters, unless there
/// are non-whitespace characters left on the same line after `self`.
///
/// This extra condition prevents a problem when removing the '}' in:
/// ```ignore
/// ( // There was an opening bracket after the parenthesis, which has been removed
/// // This is a comment
/// })
/// ```
/// Removing the whitespaces, including the linefeed, before the '}', would put the
/// closing parenthesis at the end of the `// This is a comment` line, which would
/// make it part of the comment as well. In this case, it is best to keep the span
/// on the '}' alone.
fn with_leading_whitespace(self, cx: &impl HasSession) -> Range<BytePos> {
with_leading_whitespace(cx.sess().source_map(), self.into_range())
}
@ -263,10 +275,15 @@ fn map_range(
}
fn with_leading_whitespace(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> {
map_range(sm, sp.clone(), |src, range| {
Some(src.get(..range.start)?.trim_end().len()..range.end)
map_range(sm, sp, |src, range| {
let non_blank_after = src.len() - src.get(range.end..)?.trim_start().len();
if src.get(range.end..non_blank_after)?.contains(['\r', '\n']) {
Some(src.get(..range.start)?.trim_end().len()..range.end)
} else {
Some(range)
}
})
.unwrap_or(sp)
.unwrap()
}
fn trim_start(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> {
@ -384,10 +401,10 @@ pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option<String> {
// For some reason these attributes don't have any expansion info on them, so
// we have to check it this way until there is a better way.
pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool {
if let Some(snippet) = snippet_opt(sess, span) {
if snippet.is_empty() {
return false;
}
if let Some(snippet) = snippet_opt(sess, span)
&& snippet.is_empty()
{
return false;
}
true
}
@ -408,11 +425,11 @@ pub fn position_before_rarrow(s: &str) -> Option<usize> {
let mut rpos = rpos;
let chars: Vec<char> = s.chars().collect();
while rpos > 1 {
if let Some(c) = chars.get(rpos - 1) {
if c.is_whitespace() {
rpos -= 1;
continue;
}
if let Some(c) = chars.get(rpos - 1)
&& c.is_whitespace()
{
rpos -= 1;
continue;
}
break;
}

View file

@ -1,4 +1,4 @@
/// Dealing with sting indices can be hard, this struct ensures that both the
/// Dealing with string indices can be hard, this struct ensures that both the
/// character and byte index are provided for correct indexing.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct StrIndex {
@ -165,7 +165,7 @@ pub fn camel_case_split(s: &str) -> Vec<&str> {
offsets.windows(2).map(|w| &s[w[0]..w[1]]).collect()
}
/// Dealing with sting comparison can be complicated, this struct ensures that both the
/// Dealing with string comparison can be complicated, this struct ensures that both the
/// character and byte count are provided for correct indexing.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct StrCount {

View file

@ -326,7 +326,7 @@ impl<'a> Sugg<'a> {
/// `self` argument of a method call
/// (e.g., to build `bar.foo()` or `(1 + 2).foo()`).
#[must_use]
pub fn maybe_par(self) -> Self {
pub fn maybe_paren(self) -> Self {
match self {
Sugg::NonParen(..) => self,
// `(x)` and `(x).y()` both don't need additional parens.
@ -494,7 +494,7 @@ impl<T: Display> Display for ParenHelper<T> {
/// operators have the same
/// precedence.
pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
Sugg::MaybeParen(format!("{op}{}", expr.maybe_par()).into())
Sugg::MaybeParen(format!("{op}{}", expr.maybe_paren()).into())
}
/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
@ -946,10 +946,9 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
// some items do not need explicit deref, such as array accesses,
// so we mark them as already processed
// i.e.: don't suggest `*sub[1..4].len()` for `|sub| sub[1..4].len() == 3`
if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() {
if matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) {
projections_handled = true;
}
if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind()
&& matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) {
projections_handled = true;
}
},
}
@ -1008,12 +1007,12 @@ mod test {
}
#[test]
fn binop_maybe_par() {
fn binop_maybe_paren() {
let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "1".into(), "1".into());
assert_eq!("(1 + 1)", sugg.maybe_par().to_string());
assert_eq!("(1 + 1)", sugg.maybe_paren().to_string());
let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "(1 + 1)".into(), "(1 + 1)".into());
assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string());
assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_paren().to_string());
}
#[test]
fn not_op() {

View file

@ -1,23 +1,69 @@
#![allow(non_upper_case_globals)]
use rustc_span::symbol::{Symbol, PREDEFINED_SYMBOLS_COUNT};
use rustc_span::symbol::{PREDEFINED_SYMBOLS_COUNT, Symbol};
#[doc(no_inline)]
pub use rustc_span::sym::*;
macro_rules! val {
($name:ident) => {
stringify!($name)
};
($name:ident $value:literal) => {
$value
};
}
macro_rules! generate {
($($sym:ident,)*) => {
($($name:ident $(: $value:literal)? ,)*) => {
/// To be supplied to `rustc_interface::Config`
pub const EXTRA_SYMBOLS: &[&str] = &[
$(stringify!($sym),)*
$(
val!($name $($value)?),
)*
];
$(
pub const $sym: Symbol = Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()});
pub const $name: Symbol = Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()});
)*
};
}
generate! {
as_bytes,
as_deref_mut,
as_deref,
as_mut,
Binary,
Cargo_toml: "Cargo.toml",
CLIPPY_ARGS,
CLIPPY_CONF_DIR,
cloned,
contains,
copied,
Current,
get,
insert,
int_roundings,
IntoIter,
is_empty,
is_ok,
is_some,
LowerExp,
LowerHex,
msrv,
Octal,
or_default,
regex,
rustfmt_skip,
Start,
to_owned,
unused_extern_crates,
unwrap_err,
unwrap_or_default,
UpperExp,
UpperHex,
V4,
V6,
Weak,
}

View file

@ -1,7 +0,0 @@
#[macro_export]
/// Convenience wrapper around rustc's `Symbol::intern`
macro_rules! sym {
($tt:tt) => {
rustc_span::symbol::Symbol::intern(stringify!($tt))
};
}

View file

@ -19,9 +19,9 @@ use rustc_middle::mir::interpret::Scalar;
use rustc_middle::traits::EvaluationResult;
use rustc_middle::ty::layout::ValidityRequirement;
use rustc_middle::ty::{
self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind,
GenericArgsRef, GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
};
use rustc_span::symbol::Ident;
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
@ -128,10 +128,10 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'
// For `impl Trait<Assoc=U>`, it will register a predicate of `<T as Trait>::Assoc = U`,
// so we check the term for `U`.
ty::ClauseKind::Projection(projection_predicate) => {
if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() {
if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) {
return true;
}
if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack()
&& contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen)
{
return true;
}
},
_ => (),
@ -337,20 +337,20 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)),
ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => {
for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() {
if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
return true;
}
if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
&& cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use)
{
return true;
}
}
false
},
ty::Dynamic(binder, _, _) => {
for predicate in *binder {
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) {
return true;
}
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
&& cx.tcx.has_attr(trait_ref.def_id, sym::must_use)
{
return true;
}
}
false
@ -1352,7 +1352,7 @@ pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_n
}
}
/// Get's the type of a field by name.
/// Gets the type of a field by name.
pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> Option<Ty<'tcx>> {
match *ty.kind() {
ty::Adt(def, args) if def.is_union() || def.is_struct() => def
@ -1376,3 +1376,49 @@ pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
_ => None,
}
}
/// Check if a Ty<'_> of `Iterator` contains any mutable access to non-owning types by checking if
/// it contains fields of mutable references or pointers, or references/pointers to non-`Freeze`
/// types, or `PhantomData` types containing any of the previous. This can be used to check whether
/// skipping iterating over an iterator will change its behavior.
pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>) -> bool {
fn normalize_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty)
}
/// Check if `ty` contains mutable references or equivalent, which includes:
/// - A mutable reference/pointer.
/// - A reference/pointer to a non-`Freeze` type.
/// - A `PhantomData` type containing any of the previous.
fn has_non_owning_mutable_access_inner<'tcx>(
cx: &LateContext<'tcx>,
phantoms: &mut FxHashSet<Ty<'tcx>>,
ty: Ty<'tcx>,
) -> bool {
match ty.kind() {
ty::Adt(adt_def, args) if adt_def.is_phantom_data() => {
phantoms.insert(ty)
&& args
.types()
.any(|arg_ty| has_non_owning_mutable_access_inner(cx, phantoms, arg_ty))
},
ty::Adt(adt_def, args) => adt_def.all_fields().any(|field| {
has_non_owning_mutable_access_inner(cx, phantoms, normalize_ty(cx, field.ty(cx.tcx, args)))
}),
ty::Array(elem_ty, _) | ty::Slice(elem_ty) => has_non_owning_mutable_access_inner(cx, phantoms, *elem_ty),
ty::RawPtr(pointee_ty, mutability) | ty::Ref(_, pointee_ty, mutability) => {
mutability.is_mut() || !pointee_ty.is_freeze(cx.tcx, cx.typing_env())
},
ty::Closure(_, closure_args) => {
matches!(closure_args.types().next_back(), Some(captures) if has_non_owning_mutable_access_inner(cx, phantoms, captures))
},
ty::Tuple(tuple_args) => tuple_args
.iter()
.any(|arg_ty| has_non_owning_mutable_access_inner(cx, phantoms, arg_ty)),
_ => false,
}
}
let mut phantoms = FxHashSet::default();
has_non_owning_mutable_access_inner(cx, &mut phantoms, iter_ty)
}

View file

@ -126,10 +126,10 @@ impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) -> Self::Result {
if let Res::Local(id) = path.res {
if self.binding_ids.contains(&id) {
return ControlFlow::Break(());
}
if let Res::Local(id) = path.res
&& self.binding_ids.contains(&id)
{
return ControlFlow::Break(());
}
ControlFlow::Continue(())

View file

@ -297,10 +297,10 @@ where
/// Checks if the given resolved path is used in the given body.
pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
for_each_expr(cx, cx.tcx.hir_body(body).value, |e| {
if let ExprKind::Path(p) = &e.kind {
if cx.qpath_res(p, e.hir_id) == res {
return ControlFlow::Break(());
}
if let ExprKind::Path(p) = &e.kind
&& cx.qpath_res(p, e.hir_id) == res
{
return ControlFlow::Break(());
}
ControlFlow::Continue(())
})