1#![feature(deref_patterns)]2#![feature(macro_metavar_expr)]3#![feature(rustc_private)]4#![feature(unwrap_infallible)]5#![recursion_limit = "512"]6#![expect(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]7#![warn(8 rust_2018_idioms,9 trivial_casts,10 trivial_numeric_casts,11 unused_lifetimes,12 unused_qualifications,13 rustc::internal14)]1516// FIXME: switch to something more ergonomic here, once available.17// (Currently there is no way to opt into sysroot crates without `extern crate`.)18extern crate rustc_abi;19extern crate rustc_ast;20extern crate rustc_attr_parsing;21extern crate rustc_const_eval;22extern crate rustc_data_structures;23#[expect(24 unused_extern_crates,25 reason = "The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate."26)]27extern crate rustc_driver;28extern crate rustc_errors;29extern crate rustc_hir;30extern crate rustc_hir_analysis;31extern crate rustc_hir_typeck;32extern crate rustc_index;33extern crate rustc_infer;34extern crate rustc_lexer;35extern crate rustc_lint;36extern crate rustc_middle;37extern crate rustc_mir_dataflow;38extern crate rustc_session;39extern crate rustc_span;40extern crate rustc_trait_selection;4142pub mod ast_utils;43#[deny(missing_docs)]44pub mod attrs;45mod check_proc_macro;46pub mod comparisons;47pub mod consts;48pub mod diagnostics;49pub mod eager_or_lazy;50pub mod higher;51mod hir_utils;52pub mod macros;53pub mod mir;54pub mod msrvs;55pub mod numeric_literal;56pub mod paths;57pub mod qualify_min_const_fn;58pub mod res;59pub mod source;60pub mod str_utils;61pub mod sugg;62pub mod sym;63pub mod ty;64pub mod usage;65pub mod visitors;6667pub use self::attrs::*;68pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};69pub use self::hir_utils::{70 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,71 hash_stmt, is_bool, over,72};7374use core::mem;75use core::ops::ControlFlow;76use std::collections::hash_map::Entry;77use std::iter::{once, repeat_n, zip};78use std::sync::{Mutex, OnceLock};7980use itertools::Itertools as _;81use rustc_abi::Integer;82use rustc_ast::ast::{self, LitKind, RangeLimits};83use rustc_ast::{LitIntType, join_path_syms};84use rustc_data_structures::fx::FxHashMap;85use rustc_data_structures::indexmap;86use rustc_data_structures::packed::Pu128;87use rustc_data_structures::unhash::UnindexMap;88use rustc_hir::attrs::CfgEntry;89use rustc_hir::attrs::lang_items::LangItem;90use rustc_hir::attrs::lang_items::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};91use rustc_hir::def::{DefKind, Res};92use rustc_hir::def_id::{DefId, LocalDefId, LocalModId};93use rustc_hir::definitions::{DefPath, DefPathData};94use rustc_hir::intravisit::{Visitor, walk_expr};95use rustc_hir::{96 self as hir, AnonConst, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, CRATE_HIR_ID, Closure, ConstArg,97 ConstArgKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind,98 FieldDef, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, HirIdSet, Impl, ImplItem, ImplItemKind, Item,99 ItemKind, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind,100 Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, Variant, def,101 find_attr,102};103use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};104use rustc_lint::{LateContext, Level, Lint, LintContext as _};105use rustc_middle::hir::nested_filter;106use rustc_middle::hir::place::PlaceBase;107use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};108use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion};109use rustc_middle::ty::layout::IntegerExt as _;110use rustc_middle::ty::{111 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,112 TypeFlags, TypeVisitableExt as _, TypeckResults, UintTy, UpvarCapture,113};114use rustc_span::hygiene::{ExpnKind, MacroKind};115use rustc_span::source_map::SourceMap;116use rustc_span::symbol::{Ident, Symbol, kw};117use rustc_span::{InnerSpan, Span, SyntaxContext};118use source::{SpanExt as _, walk_span_to_context};119use visitors::{Visitable, for_each_unconsumed_temporary};120121use crate::ast_utils::unordered_over;122use crate::higher::Range;123use crate::msrvs::Msrv;124use crate::res::{MaybeDef as _, MaybeResPath as _};125use crate::source::HasSourceMap;126use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};127use crate::visitors::for_each_expr_without_closures;128129/// Methods on `Vec` that also exists on slices.130pub const VEC_METHODS_SHADOWING_SLICE_METHODS: [Symbol; 3] = [sym::as_ptr, sym::is_empty, sym::len];131132#[macro_export]133macro_rules! extract_msrv_attr {134 () => {135 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {136 let sess = rustc_lint::LintContext::sess(cx);137 self.msrv.check_attributes(attrs);138 }139140 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {141 let sess = rustc_lint::LintContext::sess(cx);142 self.msrv.check_attributes_post(attrs);143 }144 };145}146147/// If the given expression is a local binding, find the initializer expression.148/// If that initializer expression is another local binding, find its initializer again.149///150/// This process repeats as long as possible (but usually no more than once). Initializer151/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]152/// instead.153///154/// Examples:155/// ```no_run156/// let abc = 1;157/// // ^ output158/// let def = abc;159/// dbg!(def);160/// // ^^^ input161///162/// // or...163/// let abc = 1;164/// let def = abc + 2;165/// // ^^^^^^^ output166/// dbg!(def);167/// // ^^^ input168/// ```169pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {170 while let Some(init) = expr171 .res_local_id()172 .and_then(|id| find_binding_init(cx, id))173 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())174 {175 expr = init;176 }177 expr178}179180/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.181///182/// By only considering immutable bindings, we guarantee that the returned expression represents the183/// value of the binding wherever it is referenced.184///185/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.186/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the187/// canonical binding `HirId`.188pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {189 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)190 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))191 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)192 {193 return local.init;194 }195 None196}197198/// Checks if the given local has an initializer or is from something other than a `let` statement199///200/// e.g. returns true for `x` in `fn f(x: usize) { .. }` and `let x = 1;` but false for `let x;`201pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {202 for (_, node) in cx.tcx.hir_parent_iter(local) {203 match node {204 Node::Pat(..) | Node::PatField(..) => {},205 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),206 _ => return true,207 }208 }209210 false211}212213/// Checks if we are currently in a const context (e.g. `const fn`, `static`/`const` initializer).214///215/// The current context is determined based on the current body which is set before calling a lint's216/// entry point (any function on `LateLintPass`). If you need to check in a different context use217/// `tcx.hir_is_inside_const_context(_)`.218///219/// Do not call this unless the `LateContext` has an enclosing body. For release build this case220/// will safely return `false`, but debug builds will ICE. Note that `check_expr`, `check_block`,221/// `check_pat` and a few other entry points will always have an enclosing body. Some entry points222/// like `check_path` or `check_ty` may or may not have one.223pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {224 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");225 cx.enclosing_body.is_some_and(|id| {226 cx.tcx227 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))228 .is_some()229 })230}231232/// Returns `true` if the given `HirId` is inside an always constant context.233///234/// This context includes:235/// * const/static items236/// * const blocks (or inline consts)237/// * associated constants238pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {239 use rustc_hir::ConstContext::{Const, ConstFn, Static};240 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {241 return false;242 };243 match ctx {244 ConstFn => false,245 Static(_)246 | Const {247 allow_const_fn_promotion: _,248 } => true,249 }250}251252/// Checks if `{ctor_call_id}(...)` is `{enum_item}::{variant_name}(...)`.253pub fn is_enum_variant_ctor(254 cx: &LateContext<'_>,255 enum_item: Symbol,256 variant_name: Symbol,257 ctor_call_id: DefId,258) -> bool {259 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {260 return false;261 };262263 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();264 variants265 .filter(|variant| variant.name == variant_name)266 .filter_map(|variant| variant.ctor.as_ref())267 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)268}269270/// Checks if the `DefId` matches the given diagnostic item or it's constructor.271pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {272 let did = match cx.tcx.def_kind(did) {273 DefKind::Ctor(..) => cx.tcx.parent(did),274 // Constructors for types in external crates seem to have `DefKind::Variant`275 DefKind::Variant => match cx.tcx.opt_parent(did) {276 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,277 _ => did,278 },279 _ => did,280 };281282 cx.tcx.is_diagnostic_item(item, did)283}284285/// Checks if the `DefId` matches the given `LangItem` or it's constructor.286pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {287 let did = match cx.tcx.def_kind(did) {288 DefKind::Ctor(..) => cx.tcx.parent(did),289 // Constructors for types in external crates seem to have `DefKind::Variant`290 DefKind::Variant => match cx.tcx.opt_parent(did) {291 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,292 _ => did,293 },294 _ => did,295 };296297 cx.tcx.lang_items().get(item) == Some(did)298}299300/// Checks is `expr` is `None`301pub fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {302 expr.basic_res().ctor_parent(cx).is_lang_item(cx, OptionNone)303}304305/// If `expr` is `Some(inner)`, returns `inner`306pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {307 if let ExprKind::Call(e, [arg]) = expr.kind308 && e.basic_res().ctor_parent(cx).is_lang_item(cx, OptionSome)309 {310 Some(arg)311 } else {312 None313 }314}315316/// Check if the given `Expr` is an empty block (i.e. `{}`) or not.317pub fn is_empty_block(expr: &Expr<'_>) -> bool {318 matches!(319 expr.kind,320 ExprKind::Block(321 Block {322 stmts: [],323 expr: None,324 ..325 },326 _,327 )328 )329}330331/// Checks if `expr` is an empty block or an empty tuple.332pub fn is_unit_expr(expr: &Expr<'_>) -> bool {333 matches!(334 expr.kind,335 ExprKind::Block(336 Block {337 stmts: [],338 expr: None,339 ..340 },341 _342 ) | ExprKind::Tup([])343 )344}345346/// Checks if given pattern is a wildcard (`_`)347pub fn is_wild(pat: &Pat<'_>) -> bool {348 matches!(pat.kind, PatKind::Wild)349}350351/// If `pat` is:352/// - `Some(inner)`, returns `inner`353/// - it will _usually_ contain just one element, but could have two, given patterns like354/// `Some(inner, ..)` or `Some(.., inner)`355/// - `Some`, returns `[]`356/// - otherwise, returns `None`357pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> {358 if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind359 && cx360 .qpath_res(qpath, pat.hir_id)361 .ctor_parent(cx)362 .is_lang_item(cx, OptionSome)363 {364 Some(inner)365 } else {366 None367 }368}369370/// Checks if the `pat` is `None`.371pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {372 matches!(pat.kind,373 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })374 if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone))375}376377/// Checks if `arm` has the form `None => None`.378pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {379 is_none_pattern(cx, arm.pat)380 && matches!(381 peel_blocks(arm.body).kind,382 ExprKind::Path(qpath)383 if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)384 )385}386387/// Checks if the given `QPath` belongs to a type alias.388pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {389 match *qpath {390 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),391 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),392 QPath::TypeRelative(..) => false,393 }394}395396/// Checks if the `def_id` belongs to a function that is part of a trait impl.397pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {398 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))399 && let ItemKind::Impl(imp) = item.kind400 {401 imp.of_trait.is_some()402 } else {403 false404 }405}406407pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {408 match *path {409 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),410 QPath::TypeRelative(_, seg) => seg,411 }412}413414pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {415 last_path_segment(qpath)416 .args417 .map_or(&[][..], |a| a.args)418 .iter()419 .filter_map(|a| match a {420 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),421 _ => None,422 })423}424425/// If the expression is a path to a local (with optional projections),426/// returns the canonical `HirId` of the local.427///428/// For example, `x.field[0].field2` would return the `HirId` of `x`.429pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {430 match expr.kind {431 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),432 ExprKind::Path(QPath::Resolved(433 _,434 Path {435 res: Res::Local(local), ..436 },437 )) => Some(*local),438 _ => None,439 }440}441442/// Gets the `hir::TraitRef` of the trait the given method is implemented for.443///444/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:445///446/// ```no_run447/// struct Point(isize, isize);448///449/// impl std::ops::Add for Point {450/// type Output = Self;451///452/// fn add(self, other: Self) -> Self {453/// Point(0, 0)454/// }455/// }456/// ```457pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {458 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))459 && let ItemKind::Impl(impl_) = &item.kind460 && let Some(of_trait) = impl_.of_trait461 {462 return Some(&of_trait.trait_ref);463 }464 None465}466467/// This method will return tuple of projection stack and root of the expression,468/// used in `can_mut_borrow_both`.469///470/// For example, if `e` represents the `v[0].a.b[x]`471/// this method will return a tuple, composed of a `Vec`472/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`473/// and an `Expr` for root of them, `v`474fn projection_stack<'a, 'hir>(475 mut e: &'a Expr<'hir>,476 ctxt: SyntaxContext,477) -> Option<(Vec<&'a Expr<'hir>>, &'a Expr<'hir>)> {478 let mut result = vec![];479 let root = loop {480 match e.kind {481 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) if e.span.ctxt() == ctxt => {482 result.push(e);483 e = ep;484 },485 ExprKind::Index(..) | ExprKind::Field(..) => return None,486 _ => break e,487 }488 };489 result.reverse();490 Some((result, root))491}492493/// Gets the mutability of the custom deref adjustment, if any.494pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {495 cx.typeck_results()496 .expr_adjustments(e)497 .iter()498 .find_map(|a| match a.kind {499 Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)),500 Adjust::Deref(DerefAdjustKind::Builtin) => None,501 _ => Some(None),502 })503 .and_then(|x| x)504}505506/// Checks if two expressions can be mutably borrowed simultaneously507/// and they aren't dependent on borrowing same thing twice508pub fn can_mut_borrow_both(cx: &LateContext<'_>, ctxt: SyntaxContext, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {509 let Some((s1, r1)) = projection_stack(e1, ctxt) else {510 return false;511 };512 let Some((s2, r2)) = projection_stack(e2, ctxt) else {513 return false;514 };515 if !eq_expr_value(cx, ctxt, r1, r2) {516 return true;517 }518 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {519 return false;520 }521522 for (x1, x2) in zip(&s1, &s2) {523 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {524 return false;525 }526527 match (&x1.kind, &x2.kind) {528 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {529 if i1 != i2 {530 return true;531 }532 },533 _ => return false,534 }535 }536 false537}538539/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"540/// constructor from the std library541fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {542 let std_types_symbols = &[543 sym::Vec,544 sym::VecDeque,545 sym::LinkedList,546 sym::HashMap,547 sym::BTreeMap,548 sym::HashSet,549 sym::BTreeSet,550 sym::BinaryHeap,551 ];552553 if let QPath::TypeRelative(_, method) = path554 && method.ident.name == sym::new555 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)556 && let Some(adt) = cx557 .tcx558 .type_of(impl_did)559 .instantiate_identity()560 .skip_norm_wip()561 .ty_adt_def()562 {563 return Some(adt.did()) == cx.tcx.lang_items().string()564 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));565 }566 false567}568569/// Returns true if the expr is equal to `Default::default` when evaluated.570pub fn is_default_equivalent_call(571 cx: &LateContext<'_>,572 repl_func: &Expr<'_>,573 whole_call_expr: Option<&Expr<'_>>,574) -> bool {575 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind576 && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx)577 && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default)578 || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath))579 {580 return true;581 }582583 // Get the type of the whole method call expression, find the exact method definition, look at584 // its body and check if it is similar to the corresponding `Default::default()` body.585 let Some(e) = whole_call_expr else { return false };586 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {587 return false;588 };589 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {590 return false;591 };592 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {593 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {594 cx.tcx.lifetimes.re_erased.into()595 } else if param.index == 0 && param.name == kw::SelfUpper {596 ty.into()597 } else {598 param.to_error(cx.tcx)599 }600 });601 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);602603 let Ok(Some(instance)) = instance else { return false };604 if let rustc_ty::InstanceKind::Item(def) = instance.def605 && !cx.tcx.is_mir_available(def)606 {607 return false;608 }609 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {610 return false;611 };612 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {613 return false;614 };615616 // Get the MIR Body for the `<Ty as Default>::default()` function.617 // If it is a value or call (either fn or ctor), we compare its `DefId` against the one for the618 // resolution of the expression we had in the path. This lets us identify, for example, that619 // the body of `<Vec<T> as Default>::default()` is a `Vec::new()`, and the field was being620 // initialized to `Vec::new()` as well.621 let body = cx.tcx.instance_mir(instance.def);622 for block_data in body.basic_blocks.iter() {623 if block_data.statements.len() == 1624 && let StatementKind::Assign(assign) = &block_data.statements[0].kind625 && assign.0.local == RETURN_PLACE626 && let Rvalue::Aggregate(kind, _places) = &assign.1627 && let AggregateKind::Adt(did, variant_index, _, _, _) = **kind628 && let def = cx.tcx.adt_def(did)629 && let variant = &def.variant(variant_index)630 && variant.fields.is_empty()631 && let Some((_, did)) = variant.ctor632 && did == repl_def_id633 {634 return true;635 } else if block_data.statements.is_empty()636 && let Some(term) = &block_data.terminator637 {638 match &term.kind {639 TerminatorKind::Call {640 func: Operand::Constant(c),641 ..642 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()643 && *did == repl_def_id =>644 {645 return true;646 },647 TerminatorKind::TailCall {648 func: Operand::Constant(c),649 ..650 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()651 && *did == repl_def_id =>652 {653 return true;654 },655 _ => {},656 }657 }658 }659 false660}661662/// Returns true if the expr is equal to `Default::default()` of its type when evaluated.663///664/// It doesn't cover all cases, like struct literals, but it is a close approximation.665pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {666 match &e.kind {667 ExprKind::Lit(lit) => match lit.node {668 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,669 LitKind::Str(s, _) => s.is_empty(),670 _ => false,671 },672 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),673 ExprKind::Repeat(x, len) => {674 if let ConstArgKind::Anon(anon_const) = len.kind675 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind676 && let LitKind::Int(v, _) = const_lit.node677 && v <= 32678 && is_default_equivalent(cx, x)679 {680 true681 } else {682 false683 }684 },685 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),686 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),687 ExprKind::Path(qpath) => cx688 .qpath_res(qpath, e.hir_id)689 .ctor_parent(cx)690 .is_lang_item(cx, OptionNone),691 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),692 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),693 _ => false,694 }695}696697fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {698 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind699 && seg.ident.name == sym::from700 {701 match arg.kind {702 ExprKind::Lit(hir::Lit {703 node: LitKind::Str(sym, _),704 ..705 }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String),706 ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec),707 ExprKind::Repeat(_, len) => {708 if let ConstArgKind::Anon(anon_const) = len.kind709 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind710 && let LitKind::Int(v, _) = const_lit.node711 {712 return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec);713 }714 },715 _ => (),716 }717 }718 false719}720721/// Checks if the top level expression can be moved into a closure as is.722/// Currently checks for:723/// * Break/Continue outside the given loop HIR ids.724/// * Yield/Return statements.725/// * Inline assembly.726/// * Usages of a field of a local where the type of the local can be partially moved.727///728/// For example, given the following function:729///730/// ```no_run731/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {732/// for item in iter {733/// let s = item.1;734/// if item.0 > 10 {735/// continue;736/// } else {737/// s.clear();738/// }739/// }740/// }741/// ```742///743/// When called on the expression `item.0` this will return false unless the local `item` is in the744/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it745/// isn't always safe to move into a closure when only a single field is needed.746///747/// When called on the `continue` expression this will return false unless the outer loop expression748/// is in the `loop_ids` set.749///750/// Note that this check is not recursive, so passing the `if` expression will always return true751/// even though sub-expressions might return false.752pub fn can_move_expr_to_closure_no_visit<'tcx>(753 cx: &LateContext<'tcx>,754 expr: &'tcx Expr<'_>,755 loop_ids: &[HirId],756 ignore_locals: &HirIdSet,757) -> bool {758 match expr.kind {759 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)760 | ExprKind::Continue(Destination { target_id: Ok(id), .. })761 if loop_ids.contains(&id) =>762 {763 true764 },765 ExprKind::Break(..)766 | ExprKind::Continue(_)767 | ExprKind::Ret(_)768 | ExprKind::Yield(..)769 | ExprKind::InlineAsm(_) => false,770 // Accessing a field of a local value can only be done if the type isn't771 // partially moved.772 ExprKind::Field(773 &Expr {774 hir_id,775 kind:776 ExprKind::Path(QPath::Resolved(777 _,778 Path {779 res: Res::Local(local_id),780 ..781 },782 )),783 ..784 },785 _,786 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {787 // TODO: check if the local has been partially moved. Assume it has for now.788 false789 },790 _ => true,791 }792}793794/// How a local is captured by a closure795#[derive(Debug, Clone, Copy, PartialEq, Eq)]796pub enum CaptureKind {797 Value,798 Use,799 Ref(Mutability),800}801impl CaptureKind {802 pub fn is_imm_ref(self) -> bool {803 self == Self::Ref(Mutability::Not)804 }805}806impl std::ops::BitOr for CaptureKind {807 type Output = Self;808 fn bitor(self, rhs: Self) -> Self::Output {809 match (self, rhs) {810 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,811 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,812 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))813 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),814 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),815 }816 }817}818impl std::ops::BitOrAssign for CaptureKind {819 fn bitor_assign(&mut self, rhs: Self) {820 *self = *self | rhs;821 }822}823824/// Given an expression referencing a local, determines how it would be captured in a closure.825///826/// Note as this will walk up to parent expressions until the capture can be determined it should827/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or828/// function argument (other than a receiver).829pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {830 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {831 let mut capture = CaptureKind::Ref(Mutability::Not);832 pat.each_binding_or_first(&mut |_, id, span, _| match cx833 .typeck_results()834 .extract_binding_mode(cx.sess(), id, span)835 .0836 {837 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {838 capture = CaptureKind::Value;839 },840 ByRef::Yes(_, Mutability::Mut) if capture != CaptureKind::Value => {841 capture = CaptureKind::Ref(Mutability::Mut);842 },843 _ => (),844 });845 capture846 }847848 debug_assert!(matches!(849 e.kind,850 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))851 ));852853 let mut capture = CaptureKind::Value;854 let mut capture_expr_ty = e;855856 for (parent, child_id) in hir_parent_with_src_iter(cx.tcx, e.hir_id) {857 if let [858 Adjustment {859 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),860 target,861 },862 ref adjust @ ..,863 ] = *cx864 .typeck_results()865 .adjustments()866 .get(child_id)867 .map_or(&[][..], |x| &**x)868 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =869 *adjust.last().map_or(target, |a| a.target).kind()870 {871 return CaptureKind::Ref(mutability);872 }873874 match parent {875 Node::Expr(e) => match e.kind {876 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),877 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),878 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {879 return CaptureKind::Ref(Mutability::Mut);880 },881 ExprKind::Field(..) => {882 if capture == CaptureKind::Value {883 capture_expr_ty = e;884 }885 },886 ExprKind::Let(let_expr) => {887 let mutability = match pat_capture_kind(cx, let_expr.pat) {888 CaptureKind::Value | CaptureKind::Use => Mutability::Not,889 CaptureKind::Ref(m) => m,890 };891 return CaptureKind::Ref(mutability);892 },893 ExprKind::Match(_, arms, _) => {894 let mut mutability = Mutability::Not;895 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {896 match capture {897 CaptureKind::Value | CaptureKind::Use => break,898 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,899 CaptureKind::Ref(Mutability::Not) => (),900 }901 }902 return CaptureKind::Ref(mutability);903 },904 _ => break,905 },906 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {907 CaptureKind::Value | CaptureKind::Use => break,908 capture @ CaptureKind::Ref(_) => return capture,909 },910 _ => break,911 }912 }913914 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {915 // Copy types are never automatically captured by value.916 CaptureKind::Ref(Mutability::Not)917 } else {918 capture919 }920}921922/// Checks if the expression can be moved into a closure as is. This will return a list of captures923/// if so, otherwise, `None`.924pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {925 struct V<'cx, 'tcx> {926 cx: &'cx LateContext<'tcx>,927 // Stack of potential break targets contained in the expression.928 loops: Vec<HirId>,929 /// Local variables created in the expression. These don't need to be captured.930 locals: HirIdSet,931 /// Whether this expression can be turned into a closure.932 allow_closure: bool,933 /// Locals which need to be captured, and whether they need to be by value, reference, or934 /// mutable reference.935 captures: HirIdMap<CaptureKind>,936 }937 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {938 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {939 if !self.allow_closure {940 return;941 }942943 match e.kind {944 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {945 if !self.locals.contains(&l) {946 let cap = capture_local_usage(self.cx, e);947 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);948 }949 },950 ExprKind::Closure(closure) => {951 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {952 let local_id = match capture.place.base {953 PlaceBase::Local(id) => id,954 PlaceBase::Upvar(var) => var.var_path.hir_id,955 _ => continue,956 };957 if !self.locals.contains(&local_id) {958 let capture = match capture.info.capture_kind {959 UpvarCapture::ByValue => CaptureKind::Value,960 UpvarCapture::ByUse => CaptureKind::Use,961 UpvarCapture::ByRef(kind) => match kind {962 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),963 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {964 CaptureKind::Ref(Mutability::Mut)965 },966 },967 };968 self.captures969 .entry(local_id)970 .and_modify(|e| *e |= capture)971 .or_insert(capture);972 }973 }974 },975 ExprKind::Loop(b, ..) => {976 self.loops.push(e.hir_id);977 self.visit_block(b);978 self.loops.pop();979 },980 _ => {981 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);982 walk_expr(self, e);983 },984 }985 }986987 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {988 p.each_binding_or_first(&mut |_, id, _, _| {989 self.locals.insert(id);990 });991 }992 }993994 let mut v = V {995 cx,996 loops: Vec::new(),997 locals: HirIdSet::default(),998 allow_closure: true,999 captures: HirIdMap::default(),1000 };1001 v.visit_expr(expr);1002 v.allow_closure.then_some(v.captures)1003}10041005/// Arguments of a method: the receiver and all the additional arguments.1006pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;10071008/// Returns the method names and argument list of nested method call expressions that make up1009/// `expr`. method/span lists are sorted with the most recent call first.1010pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {1011 let mut method_names = Vec::with_capacity(max_depth);1012 let mut arg_lists = Vec::with_capacity(max_depth);1013 let mut spans = Vec::with_capacity(max_depth);10141015 let mut current = expr;1016 for _ in 0..max_depth {1017 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {1018 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {1019 break;1020 }1021 method_names.push(path.ident.name);1022 arg_lists.push((*receiver, &**args));1023 spans.push(path.ident.span);1024 current = receiver;1025 } else {1026 break;1027 }1028 }10291030 (method_names, arg_lists, spans)1031}10321033/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.1034///1035/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,1036/// `method_chain_args(expr, &[sym::bar, sym::baz])` will return a `Vec`1037/// containing the `Expr`s for1038/// `.bar()` and `.baz()`1039pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {1040 let mut current = expr;1041 let mut matched = Vec::with_capacity(methods.len());1042 for method_name in methods.iter().rev() {1043 // method chains are stored last -> first1044 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {1045 if path.ident.name == *method_name {1046 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {1047 return None;1048 }1049 matched.push((receiver, args)); // build up `matched` backwards1050 current = receiver; // go to parent expression1051 } else {1052 return None;1053 }1054 } else {1055 return None;1056 }1057 }1058 // Reverse `matched` so that it is in the same order as `methods`.1059 matched.reverse();1060 Some(matched)1061}10621063/// Returns `true` if the provided `def_id` is an entrypoint to a program.1064pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {1065 cx.tcx1066 .entry_fn(())1067 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)1068}10691070/// Returns `true` if the expression is in the program's `#[panic_handler]`.1071pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {1072 let parent = cx.tcx.hir_get_parent_item(e.hir_id);1073 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()1074}10751076/// Gets the name of the item the expression is in, if available.1077pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {1078 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;1079 match cx.tcx.hir_node_by_def_id(parent_id) {1080 Node::Item(item) => item.kind.ident().map(|ident| ident.name),1081 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),1082 _ => None,1083 }1084}10851086pub struct ContainsName<'a, 'tcx> {1087 pub cx: &'a LateContext<'tcx>,1088 pub name: Symbol,1089}10901091impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {1092 type Result = ControlFlow<()>;1093 type NestedFilter = nested_filter::OnlyBodies;10941095 fn visit_name(&mut self, name: Symbol) -> Self::Result {1096 if self.name == name {1097 ControlFlow::Break(())1098 } else {1099 ControlFlow::Continue(())1100 }1101 }11021103 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {1104 self.cx.tcx1105 }1106}11071108/// Checks if an `Expr` contains a certain name.1109pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {1110 let mut cn = ContainsName { cx, name };1111 cn.visit_expr(expr).is_break()1112}11131114/// Returns `true` if `expr` contains a return expression1115pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {1116 for_each_expr_without_closures(expr, |e| {1117 if matches!(e.kind, ExprKind::Ret(..)) {1118 ControlFlow::Break(())1119 } else {1120 ControlFlow::Continue(())1121 }1122 })1123 .is_some()1124}11251126/// Gets the parent expression, if any –- this is useful to constrain a lint.1127pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {1128 get_parent_expr_for_hir(cx, e.hir_id)1129}11301131/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for1132/// constraint lints1133pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {1134 match cx.tcx.parent_hir_node(hir_id) {1135 Node::Expr(parent) => Some(parent),1136 _ => None,1137 }1138}11391140/// Gets the enclosing block, if any.1141pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {1142 let enclosing_node = cx1143 .tcx1144 .hir_get_enclosing_scope(hir_id)1145 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));1146 enclosing_node.and_then(|node| match node {1147 Node::Block(block) => Some(block),1148 Node::Item(&Item {1149 kind: ItemKind::Fn { body: eid, .. },1150 ..1151 })1152 | Node::ImplItem(&ImplItem {1153 kind: ImplItemKind::Fn(_, eid),1154 ..1155 })1156 | Node::TraitItem(&TraitItem {1157 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),1158 ..1159 }) => match cx.tcx.hir_body(eid).value.kind {1160 ExprKind::Block(block, _) => Some(block),1161 _ => None,1162 },1163 _ => None,1164 })1165}11661167/// Returns the [`Closure`] enclosing `hir_id`, if any.1168pub fn get_enclosing_closure<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Closure<'tcx>> {1169 cx.tcx.hir_parent_iter(hir_id).find_map(|(_, node)| {1170 if let Node::Expr(expr) = node1171 && let ExprKind::Closure(closure) = expr.kind1172 {1173 Some(closure)1174 } else {1175 None1176 }1177 })1178}11791180/// Checks whether a local identified by `local_id` is captured as an upvar by the given `closure`.1181pub fn is_upvar_in_closure(cx: &LateContext<'_>, closure: &Closure<'_>, local_id: HirId) -> bool {1182 cx.typeck_results()1183 .closure_min_captures1184 .get(&closure.def_id)1185 .is_some_and(|x| x.contains_key(&local_id))1186}11871188/// Gets the loop or closure enclosing the given expression, if any.1189pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(1190 cx: &LateContext<'tcx>,1191 expr: &Expr<'_>,1192) -> Option<&'tcx Expr<'tcx>> {1193 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {1194 match node {1195 Node::Expr(e) => match e.kind {1196 ExprKind::Closure { .. }1197 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()1198 && subs.as_closure().kind() == ClosureKind::FnOnce => {},11991200 // Note: A closure's kind is determined by how it's used, not it's captures.1201 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),1202 _ => (),1203 },1204 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),1205 _ => break,1206 }1207 }1208 None1209}12101211/// Gets the parent node if it's an impl block.1212pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {1213 match tcx.hir_parent_iter(id).next() {1214 Some((1215 _,1216 Node::Item(Item {1217 kind: ItemKind::Impl(imp),1218 ..1219 }),1220 )) => Some(imp),1221 _ => None,1222 }1223}12241225/// Removes blocks around an expression, only if the block contains just one expression1226/// and no statements. Unsafe blocks are not removed.1227///1228/// Examples:1229/// * `{}` -> `{}`1230/// * `{ x }` -> `x`1231/// * `{{ x }}` -> `x`1232/// * `{ x; }` -> `{ x; }`1233/// * `{ x; y }` -> `{ x; y }`1234/// * `{ unsafe { x } }` -> `unsafe { x }`1235pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {1236 while let ExprKind::Block(1237 Block {1238 stmts: [],1239 expr: Some(inner),1240 rules: BlockCheckMode::DefaultBlock,1241 ..1242 },1243 _,1244 ) = expr.kind1245 {1246 expr = inner;1247 }1248 expr1249}12501251/// Removes blocks around an expression, only if the block contains just one expression1252/// or just one expression statement with a semicolon. Unsafe blocks are not removed.1253///1254/// Examples:1255/// * `{}` -> `{}`1256/// * `{ x }` -> `x`1257/// * `{ x; }` -> `x`1258/// * `{{ x; }}` -> `x`1259/// * `{ x; y }` -> `{ x; y }`1260/// * `{ unsafe { x } }` -> `unsafe { x }`1261pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {1262 while let ExprKind::Block(1263 Block {1264 stmts: [],1265 expr: Some(inner),1266 rules: BlockCheckMode::DefaultBlock,1267 ..1268 }1269 | Block {1270 stmts:1271 [1272 Stmt {1273 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),1274 ..1275 },1276 ],1277 expr: None,1278 rules: BlockCheckMode::DefaultBlock,1279 ..1280 },1281 _,1282 ) = expr.kind1283 {1284 expr = inner;1285 }1286 expr1287}12881289/// Checks if the given expression is the else clause of either an `if` or `if let` expression.1290pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {1291 let mut iter = tcx.hir_parent_iter(expr.hir_id);1292 match iter.next() {1293 Some((1294 _,1295 Node::Expr(Expr {1296 kind: ExprKind::If(_, _, Some(else_expr)),1297 ..1298 }),1299 )) => else_expr.hir_id == expr.hir_id,1300 _ => false,1301 }1302}13031304/// Checks if the given expression is a part of `let else`1305/// returns `true` for both the `init` and the `else` part1306pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {1307 hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {1308 matches!(1309 node,1310 Node::LetStmt(LetStmt {1311 init: Some(init),1312 els: Some(els),1313 ..1314 })1315 if init.hir_id == child_id || els.hir_id == child_id1316 )1317 })1318}13191320/// Checks if the given expression is the else clause of a `let else` expression1321pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {1322 hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {1323 matches!(1324 node,1325 Node::LetStmt(LetStmt { els: Some(els), .. })1326 if els.hir_id == child_id1327 )1328 })1329}13301331/// Checks whether the given `Expr` is a range over the entire container.1332pub fn is_full_collection_range(cx: &LateContext<'_>, container: Option<HirId>, expr: &Expr<'_>) -> bool {1333 if let Some(Range { start, end, ty, .. }) = Range::hir(cx, expr) {1334 start.is_none_or(|start| is_integer_literal(start, 0))1335 && end.is_none_or(|end| {1336 if ty.limits() == RangeLimits::HalfOpen1337 && let Some(container) = container1338 && let ExprKind::MethodCall(seg, recv, [], _) = end.kind1339 {1340 seg.ident.name == sym::len && recv.res_local_id() == Some(container)1341 } else {1342 false1343 }1344 })1345 } else {1346 false1347 }1348}13491350/// Checks whether the given expression is a constant literal of the given value.1351pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {1352 if let ExprKind::Lit(spanned) = expr.kind1353 && let LitKind::Int(v, _) = spanned.node1354 {1355 return v == value;1356 }1357 false1358}13591360/// Checks whether the given expression is an untyped integer literal.1361pub fn is_integer_literal_untyped(expr: &Expr<'_>) -> bool {1362 if let ExprKind::Lit(spanned) = expr.kind1363 && let LitKind::Int(_, suffix) = spanned.node1364 {1365 return suffix == LitIntType::Unsuffixed;1366 }13671368 false1369}13701371/// Checks whether the given expression is a constant literal of the given value.1372pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {1373 if let ExprKind::Lit(spanned) = expr.kind1374 && let LitKind::Float(v, _) = spanned.node1375 {1376 v.as_str().parse() == Ok(value)1377 } else {1378 false1379 }1380}13811382/// Returns `true` if the given `Expr` has been coerced before.1383///1384/// Examples of coercions can be found in the Nomicon at1385/// <https://doc.rust-lang.org/nomicon/coercions.html>.1386///1387/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for1388/// more information on adjustments and coercions.1389pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {1390 cx.typeck_results().adjustments().get(e.hir_id).is_some()1391}13921393/// Returns the pre-expansion span if this comes from an expansion of the1394/// macro `name`.1395/// See also [`is_direct_expn_of`].1396#[must_use]1397pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {1398 loop {1399 if span.from_expansion() {1400 let data = span.ctxt().outer_expn_data();1401 let new_span = data.call_site;14021403 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind1404 && mac_name == name1405 {1406 return Some(new_span);1407 }14081409 span = new_span;1410 } else {1411 return None;1412 }1413 }1414}14151416/// Returns the pre-expansion span if the span directly comes from an expansion1417/// of the macro `name`.1418/// The difference with [`is_expn_of`] is that in1419/// ```no_run1420/// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }1421/// # macro_rules! bar { ($e:expr) => { $e } }1422/// foo!(bar!(42));1423/// ```1424/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only1425/// from `bar!` by `is_direct_expn_of`.1426#[must_use]1427pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {1428 if span.from_expansion() {1429 let data = span.ctxt().outer_expn_data();1430 let new_span = data.call_site;14311432 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind1433 && mac_name == name1434 {1435 return Some(new_span);1436 }1437 }14381439 None1440}14411442/// Convenience function to get the return type of a function.1443pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {1444 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().skip_norm_wip().output();1445 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)1446}14471448/// Convenience function to get the nth argument type of a function.1449pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {1450 let arg = cx1451 .tcx1452 .fn_sig(fn_def_id)1453 .instantiate_identity()1454 .skip_norm_wip()1455 .input(nth);1456 cx.tcx.instantiate_bound_regions_with_erased(arg)1457}14581459/// Checks if an expression is constructing a tuple-like enum variant or struct1460pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {1461 if let ExprKind::Call(fun, _) = expr.kind1462 && let ExprKind::Path(ref qp) = fun.kind1463 {1464 let res = cx.qpath_res(qp, fun.hir_id);1465 return match res {1466 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,1467 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),1468 _ => false,1469 };1470 }1471 false1472}14731474/// Returns `true` if a pattern is refutable.1475// TODO: should be implemented using rustc/mir_build/thir machinery1476pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {1477 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {1478 !matches!(1479 cx.qpath_res(qpath, id),1480 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)1481 )1482 }14831484 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {1485 i.into_iter().any(|pat| is_refutable(cx, pat))1486 }14871488 match pat.kind {1489 PatKind::Missing => unreachable!(),1490 PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable.1491 PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),1492 PatKind::Ref(pat, _, _) => is_refutable(cx, pat),1493 PatKind::Expr(PatExpr {1494 kind: PatExprKind::Path(qpath),1495 hir_id,1496 ..1497 }) => is_qpath_refutable(cx, qpath, *hir_id),1498 PatKind::Or(pats) => {1499 // TODO: should be the honest check, that pats is exhaustive set1500 are_refutable(cx, pats)1501 },1502 PatKind::Tuple(pats, _) => are_refutable(cx, pats),1503 PatKind::Struct(ref qpath, fields, _) => {1504 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))1505 },1506 PatKind::TupleStruct(ref qpath, pats, _) => {1507 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)1508 },1509 PatKind::Slice(head, middle, tail) => {1510 match &cx.typeck_results().node_type(pat.hir_id).kind() {1511 rustc_ty::Slice(..) => {1512 // [..] is the only irrefutable slice pattern.1513 !head.is_empty() || middle.is_none() || !tail.is_empty()1514 },1515 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),1516 _ => {1517 // unreachable!()1518 true1519 },1520 }1521 },1522 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,1523 }1524}15251526/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call1527/// the function once on the given pattern.1528pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {1529 if let PatKind::Or(pats) = pat.kind {1530 pats.iter().for_each(f);1531 } else {1532 f(pat);1533 }1534}15351536pub fn is_self(slf: &Param<'_>) -> bool {1537 if let PatKind::Binding(.., name, _) = slf.pat.kind {1538 name.name == kw::SelfLower1539 } else {1540 false1541 }1542}15431544pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {1545 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind1546 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res1547 {1548 return true;1549 }1550 false1551}15521553pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {1554 (0..decl.inputs.len()).map(move |i| &body.params[i])1555}15561557/// Checks if a given expression is a match expression expanded from the `?`1558/// operator or the `try` macro.1559pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {1560 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {1561 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind1562 && ddpos.as_opt_usize().is_none()1563 && cx1564 .qpath_res(path, arm.pat.hir_id)1565 .ctor_parent(cx)1566 .is_lang_item(cx, ResultOk)1567 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind1568 && arm.body.res_local_id() == Some(hir_id)1569 {1570 return true;1571 }1572 false1573 }15741575 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {1576 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {1577 cx.qpath_res(path, arm.pat.hir_id)1578 .ctor_parent(cx)1579 .is_lang_item(cx, ResultErr)1580 } else {1581 false1582 }1583 }15841585 if let ExprKind::Match(_, arms, ref source) = expr.kind {1586 // desugared from a `?` operator1587 if let MatchSource::TryDesugar(_) = *source {1588 return Some(expr);1589 }15901591 if arms.len() == 21592 && arms[0].guard.is_none()1593 && arms[1].guard.is_none()1594 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))1595 {1596 return Some(expr);1597 }1598 }15991600 None1601}16021603/// Returns `true` if the lint is `#[allow]`ed or `#[expect]`ed at any of the `ids`, fulfilling all1604/// of the expectations in `ids`1605///1606/// This should only be used when the lint would otherwise be emitted, for a way to check if a lint1607/// is allowed early to skip work see [`is_lint_allowed`]1608///1609/// To emit at a lint at a different context than the one current see1610/// [`span_lint_hir`](diagnostics::span_lint_hir) or1611/// [`span_lint_hir_and_then`](diagnostics::span_lint_hir_and_then)1612pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {1613 let mut suppress_lint = false;16141615 for id in ids {1616 let level_spec = cx.tcx.lint_level_spec_at_node(lint, id);1617 if let Some(expectation) = level_spec.lint_id() {1618 cx.fulfill_expectation(expectation);1619 }16201621 match level_spec.level() {1622 Level::Allow | Level::Expect => suppress_lint = true,1623 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},1624 }1625 }16261627 suppress_lint1628}16291630/// Returns `true` if the lint is allowed in the current context. This is useful for1631/// skipping long running code when it's unnecessary1632///1633/// This function should check the lint level for the same node, that the lint will1634/// be emitted at. If the information is buffered to be emitted at a later point, please1635/// make sure to use `span_lint_hir` functions to emit the lint. This ensures that1636/// expectations at the checked nodes will be fulfilled.1637pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {1638 cx.tcx.lint_level_spec_at_node(lint, id).is_allow()1639}16401641pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {1642 while let PatKind::Ref(subpat, _, _) = pat.kind {1643 pat = subpat;1644 }1645 pat1646}16471648pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {1649 Integer::from_int_ty(&tcx, ity).size().bits()1650}16511652#[expect(clippy::cast_possible_wrap)]1653/// Turn a constant int byte representation into an i1281654pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {1655 let amt = 128 - int_bits(tcx, ity);1656 ((u as i128) << amt) >> amt1657}16581659#[expect(clippy::cast_sign_loss)]1660/// clip unused bytes1661pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {1662 let amt = 128 - int_bits(tcx, ity);1663 ((u as u128) << amt) >> amt1664}16651666/// clip unused bytes1667pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {1668 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();1669 let amt = 128 - bits;1670 (u << amt) >> amt1671}16721673pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {1674 attrs.iter().any(|attr| attr.has_name(symbol))1675}16761677pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {1678 find_attr!(cx.tcx, hir_id, Repr { .. })1679}16801681pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {1682 let mut prev_enclosing_node = None;1683 let mut enclosing_node = node;1684 while Some(enclosing_node) != prev_enclosing_node {1685 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {1686 return true;1687 }1688 prev_enclosing_node = Some(enclosing_node);1689 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();1690 }16911692 false1693}16941695/// Checks if the given HIR node is inside an `impl` block with the `automatically_derived`1696/// attribute.1697pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {1698 tcx.hir_parent_owner_iter(id)1699 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))1700 .any(|(id, _)| find_attr!(tcx, id.def_id, AutomaticallyDerived))1701}17021703/// Checks if the given `DefId` matches the `libc` item.1704pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {1705 // libc is meant to be used as a flat list of names, but they're all actually defined in different1706 // modules based on the target platform. Ignore everything but crate name and the item name.1707 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())1708}17091710/// Returns the list of condition expressions and the list of blocks in a1711/// sequence of `if/else`.1712/// E.g., this returns `([a, b], [c, d, e])` for the expression1713/// `if a { c } else if b { d } else { e }`.1714pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {1715 let mut conds = Vec::new();1716 let mut blocks: Vec<&Block<'_>> = Vec::new();17171718 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {1719 conds.push(cond);1720 if let ExprKind::Block(block, _) = then.kind {1721 blocks.push(block);1722 } else {1723 panic!("ExprKind::If node is not an ExprKind::Block");1724 }17251726 if let Some(else_expr) = r#else {1727 expr = else_expr;1728 } else {1729 break;1730 }1731 }17321733 // final `else {..}`1734 if !blocks.is_empty()1735 && let ExprKind::Block(block, _) = expr.kind1736 {1737 blocks.push(block);1738 }17391740 (conds, blocks)1741}17421743/// Peels away all the compiler generated code surrounding the body of an async closure.1744pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {1745 if let ExprKind::Closure(&Closure {1746 body,1747 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),1748 ..1749 }) = expr.kind1750 && let ExprKind::Block(1751 Block {1752 expr:1753 Some(Expr {1754 kind: ExprKind::DropTemps(inner_expr),1755 ..1756 }),1757 ..1758 },1759 _,1760 ) = tcx.hir_body(body).value.kind1761 {1762 Some(inner_expr)1763 } else {1764 None1765 }1766}17671768/// Peels away all the compiler generated code surrounding the body of an async function,1769pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {1770 get_async_closure_expr(tcx, body.value)1771}17721773// check if expr is calling method or function with #[must_use] attribute1774pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {1775 let did = match expr.kind {1776 ExprKind::Call(path, _) => {1777 if let ExprKind::Path(ref qpath) = path.kind1778 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)1779 {1780 Some(did)1781 } else {1782 None1783 }1784 },1785 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),1786 _ => None,1787 };17881789 did.is_some_and(|did| find_attr!(cx.tcx, did, MustUse { .. }))1790}17911792/// Checks if a function's body represents the identity function. Looks for bodies of the form:1793/// * `|x| x`1794/// * `|x| return x`1795/// * `|x| { return x }`1796/// * `|x| { return x; }`1797/// * `|(x, y)| (x, y)`1798/// * `|[x, y]| [x, y]`1799/// * `|Foo(bar, baz)| Foo(bar, baz)`1800/// * `|Foo { bar, baz }| Foo { bar, baz }`1801/// * `|x| { let y = x; ...; let z = y; z }`1802/// * `|x| { let y = x; ...; let z = y; return z }`1803///1804/// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead.1805fn is_body_identity_function<'hir>(cx: &LateContext<'_>, func: &Body<'hir>) -> bool {1806 let [param] = func.params else {1807 return false;1808 };18091810 let mut param_pat = param.pat;18111812 // Given a sequence of `Stmt`s of the form `let p = e` where `e` is an expr identical to the1813 // current `param_pat`, advance the current `param_pat` to `p`.1814 //1815 // Note: This is similar to `clippy_utils::get_last_chain_binding_hir_id`, but it works1816 // directly over a `Pattern` rather than a `HirId`. And it checks for compatibility via1817 // `is_expr_identity_of_pat` rather than `HirId` equality1818 let mut advance_param_pat_over_stmts = |stmts: &[Stmt<'hir>]| {1819 for stmt in stmts {1820 if let StmtKind::Let(local) = stmt.kind1821 && let Some(init) = local.init1822 && is_expr_identity_of_pat(cx, param_pat, init, true)1823 {1824 param_pat = local.pat;1825 } else {1826 return false;1827 }1828 }18291830 true1831 };18321833 let mut expr = func.value;1834 loop {1835 match expr.kind {1836 ExprKind::Block(1837 &Block {1838 stmts: [],1839 expr: Some(e),1840 ..1841 },1842 _,1843 )1844 | ExprKind::Ret(Some(e)) => expr = e,1845 ExprKind::Block(1846 &Block {1847 stmts: [stmt],1848 expr: None,1849 ..1850 },1851 _,1852 ) => {1853 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind1854 && let ExprKind::Ret(Some(ret_val)) = e.kind1855 {1856 expr = ret_val;1857 } else {1858 return false;1859 }1860 },1861 ExprKind::Block(1862 &Block {1863 stmts, expr: Some(e), ..1864 },1865 _,1866 ) => {1867 if !advance_param_pat_over_stmts(stmts) {1868 return false;1869 }18701871 expr = e;1872 },1873 ExprKind::Block(&Block { stmts, expr: None, .. }, _) => {1874 if let Some((last_stmt, stmts)) = stmts.split_last()1875 && advance_param_pat_over_stmts(stmts)1876 && let StmtKind::Semi(e) | StmtKind::Expr(e) = last_stmt.kind1877 && let ExprKind::Ret(Some(ret_val)) = e.kind1878 {1879 expr = ret_val;1880 } else {1881 return false;1882 }1883 },1884 _ => return is_expr_identity_of_pat(cx, param_pat, expr, true),1885 }1886 }1887}18881889/// Checks if the given expression is an identity representation of the given pattern:1890/// * `x` is the identity representation of `x`1891/// * `(x, y)` is the identity representation of `(x, y)`1892/// * `[x, y]` is the identity representation of `[x, y]`1893/// * `Foo(bar, baz)` is the identity representation of `Foo(bar, baz)`1894/// * `Foo { bar, baz }` is the identity representation of `Foo { bar, baz }`1895///1896/// Note that `by_hir` is used to determine bindings are checked by their `HirId` or by their name.1897/// This can be useful when checking patterns in `let` bindings or `match` arms.1898pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {1899 if cx1900 .typeck_results()1901 .pat_binding_modes()1902 .get(pat.hir_id)1903 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..)))1904 {1905 // If the parameter is `(x, y)` of type `&(T, T)`, or `[x, y]` of type `&[T; 2]`, then1906 // due to match ergonomics, the inner patterns become references. Don't consider this1907 // the identity function as that changes types.1908 return false;1909 }19101911 // NOTE: we're inside a (function) body, so this won't ICE1912 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);19131914 match (pat.kind, expr.kind) {1915 (PatKind::Binding(_, id, _, _), _) if by_hir => {1916 expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty()1917 },1918 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {1919 matches!(path.segments, [ segment] if segment.ident.name == ident.name)1920 },1921 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))1922 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>1923 {1924 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))1925 },1926 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {1927 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))1928 },1929 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))1930 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>1931 {1932 // check ident1933 if let ExprKind::Path(ident) = &ident.kind1934 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)1935 // check fields1936 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))1937 {1938 true1939 } else {1940 false1941 }1942 },1943 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))1944 if field_pats.len() == fields.len() =>1945 {1946 // check ident1947 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)1948 // check fields1949 && unordered_over(field_pats, fields, |field_pat, field| {1950 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)1951 })1952 },1953 _ => false,1954 }1955}19561957/// This is the same as [`is_expr_identity_function`], but does not consider closures1958/// with type annotations for its bindings (or similar) as identity functions:1959/// * `|x: u8| x`1960/// * `std::convert::identity::<u8>`1961pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {1962 match expr.kind {1963 ExprKind::Closure(&Closure { body, fn_decl, .. })1964 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>1965 {1966 is_body_identity_function(cx, cx.tcx.hir_body(body))1967 },1968 ExprKind::Path(QPath::Resolved(_, path))1969 if path.segments.iter().all(|seg| seg.infer_args)1970 && let Some(did) = path.res.opt_def_id() =>1971 {1972 cx.tcx.is_diagnostic_item(sym::convert_identity, did)1973 },1974 _ => false,1975 }1976}19771978/// Checks if an expression represents the identity function1979/// Only examines closures and `std::convert::identity`1980///1981/// NOTE: If you want to use this function to find out if a closure is unnecessary, you likely want1982/// to call [`is_expr_untyped_identity_function`] instead, which makes sure that the closure doesn't1983/// have type annotations. This is important because removing a closure with bindings can1984/// remove type information that helped type inference before, which can then lead to compile1985/// errors.1986pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {1987 match expr.kind {1988 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),1989 _ => expr.basic_res().is_diag_item(cx, sym::convert_identity),1990 }1991}19921993/// Gets the node where an expression is either used, or it's type is unified with another branch.1994/// Returns both the node and the `HirId` of the closest child node.1995pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {1996 for (node, child_id) in hir_parent_with_src_iter(tcx, expr.hir_id) {1997 match node {1998 Node::Block(_) => {},1999 Node::Arm(arm) if arm.body.hir_id == child_id => {},2000 Node::Expr(expr) => match expr.kind {
Findings
✓ No findings reported for this file.