1#![feature(array_chunks)]
2#![feature(box_patterns)]
3#![feature(if_let_guard)]
4#![feature(macro_metavar_expr_concat)]
5#![feature(macro_metavar_expr)]
6#![feature(let_chains)]
7#![feature(never_type)]
8#![feature(rustc_private)]
9#![feature(assert_matches)]
10#![feature(unwrap_infallible)]
11#![feature(array_windows)]
12#![recursion_limit = "512"]
13#![allow(
14 clippy::missing_errors_doc,
15 clippy::missing_panics_doc,
16 clippy::must_use_candidate,
17 rustc::diagnostic_outside_of_impl,
18 rustc::untranslatable_diagnostic
19)]
20#![warn(
21 trivial_casts,
22 trivial_numeric_casts,
23 rust_2018_idioms,
24 unused_lifetimes,
25 unused_qualifications,
26 rustc::internal
27)]
28
29extern crate rustc_abi;
32extern crate rustc_ast;
33extern crate rustc_attr_parsing;
34extern crate rustc_const_eval;
35extern crate rustc_data_structures;
36#[allow(unused_extern_crates)]
38extern crate rustc_driver;
39extern crate rustc_errors;
40extern crate rustc_hir;
41extern crate rustc_hir_analysis;
42extern crate rustc_hir_typeck;
43extern crate rustc_index;
44extern crate rustc_infer;
45extern crate rustc_lexer;
46extern crate rustc_lint;
47extern crate rustc_middle;
48extern crate rustc_mir_dataflow;
49extern crate rustc_session;
50extern crate rustc_span;
51extern crate rustc_trait_selection;
52extern crate smallvec;
53
54pub mod ast_utils;
55pub mod attrs;
56mod check_proc_macro;
57pub mod comparisons;
58pub mod consts;
59pub mod diagnostics;
60pub mod eager_or_lazy;
61pub mod higher;
62mod hir_utils;
63pub mod macros;
64pub mod mir;
65pub mod msrvs;
66pub mod numeric_literal;
67pub mod paths;
68pub mod ptr;
69pub mod qualify_min_const_fn;
70pub mod source;
71pub mod str_utils;
72pub mod sugg;
73pub mod sym;
74pub mod ty;
75pub mod usage;
76pub mod visitors;
77
78pub use self::attrs::*;
79pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
80pub use self::hir_utils::{
81 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over,
82};
83
84use core::mem;
85use core::ops::ControlFlow;
86use std::collections::hash_map::Entry;
87use std::hash::BuildHasherDefault;
88use std::iter::{once, repeat_n};
89use std::sync::{Mutex, MutexGuard, OnceLock};
90
91use itertools::Itertools;
92use rustc_abi::Integer;
93use rustc_ast::ast::{self, LitKind, RangeLimits};
94use rustc_attr_parsing::{AttributeKind, find_attr};
95use rustc_data_structures::fx::FxHashMap;
96use rustc_data_structures::packed::Pu128;
97use rustc_data_structures::unhash::UnhashMap;
98use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
99use rustc_hir::def::{DefKind, Res};
100use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
101use rustc_hir::definitions::{DefPath, DefPathData};
102use rustc_hir::hir_id::{HirIdMap, HirIdSet};
103use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
104use rustc_hir::{
105 self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext,
106 CoroutineDesugaring, CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg,
107 GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource,
108 Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath,
109 Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
110};
111use rustc_lexer::{TokenKind, tokenize};
112use rustc_lint::{LateContext, Level, Lint, LintContext};
113use rustc_middle::hir::place::PlaceBase;
114use rustc_middle::lint::LevelAndSource;
115use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
116use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
117use rustc_middle::ty::fast_reject::SimplifiedType;
118use rustc_middle::ty::layout::IntegerExt;
119use rustc_middle::ty::{
120 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty,
121 TyCtxt, TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
122};
123use rustc_span::hygiene::{ExpnKind, MacroKind};
124use rustc_span::source_map::SourceMap;
125use rustc_span::symbol::{Ident, Symbol, kw};
126use rustc_span::{InnerSpan, Span};
127use source::walk_span_to_context;
128use visitors::{Visitable, for_each_unconsumed_temporary};
129
130use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
131use crate::higher::Range;
132use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
133use crate::visitors::for_each_expr_without_closures;
134use rustc_middle::hir::nested_filter;
135
136#[macro_export]
137macro_rules! extract_msrv_attr {
138 () => {
139 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
140 let sess = rustc_lint::LintContext::sess(cx);
141 self.msrv.check_attributes(sess, attrs);
142 }
143
144 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
145 let sess = rustc_lint::LintContext::sess(cx);
146 self.msrv.check_attributes_post(sess, attrs);
147 }
148 };
149}
150
151pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
174 while let Some(init) = path_to_local(expr)
175 .and_then(|id| find_binding_init(cx, id))
176 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
177 {
178 expr = init;
179 }
180 expr
181}
182
183pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
192 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
193 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
194 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
195 {
196 return local.init;
197 }
198 None
199}
200
201pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
205 for (_, node) in cx.tcx.hir_parent_iter(local) {
206 match node {
207 Node::Pat(..) | Node::PatField(..) => {},
208 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
209 _ => return true,
210 }
211 }
212
213 false
214}
215
216pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
227 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
228 cx.enclosing_body.is_some_and(|id| {
229 cx.tcx
230 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
231 .is_some()
232 })
233}
234
235pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
242 use ConstContext::{Const, ConstFn, Static};
243 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
244 return false;
245 };
246 match ctx {
247 ConstFn => false,
248 Static(_) | Const { inline: _ } => true,
249 }
250}
251
252pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
255 if let Res::Def(DefKind::Ctor(..), id) = res
256 && let Some(lang_id) = cx.tcx.lang_items().get(lang_item)
257 && let Some(id) = cx.tcx.opt_parent(id)
258 {
259 id == lang_id
260 } else {
261 false
262 }
263}
264
265pub fn is_enum_variant_ctor(
267 cx: &LateContext<'_>,
268 enum_item: Symbol,
269 variant_name: Symbol,
270 ctor_call_id: DefId,
271) -> bool {
272 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
273 return false;
274 };
275
276 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
277 variants
278 .filter(|variant| variant.name == variant_name)
279 .filter_map(|variant| variant.ctor.as_ref())
280 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
281}
282
283pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
285 let did = match cx.tcx.def_kind(did) {
286 DefKind::Ctor(..) => cx.tcx.parent(did),
287 DefKind::Variant => match cx.tcx.opt_parent(did) {
289 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
290 _ => did,
291 },
292 _ => did,
293 };
294
295 cx.tcx.is_diagnostic_item(item, did)
296}
297
298pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
300 let did = match cx.tcx.def_kind(did) {
301 DefKind::Ctor(..) => cx.tcx.parent(did),
302 DefKind::Variant => match cx.tcx.opt_parent(did) {
304 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
305 _ => did,
306 },
307 _ => did,
308 };
309
310 cx.tcx.lang_items().get(item) == Some(did)
311}
312
313pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
314 matches!(
315 expr.kind,
316 ExprKind::Block(
317 Block {
318 stmts: [],
319 expr: None,
320 ..
321 },
322 _
323 ) | ExprKind::Tup([])
324 )
325}
326
327pub fn is_wild(pat: &Pat<'_>) -> bool {
329 matches!(pat.kind, PatKind::Wild)
330}
331
332pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
334 matches!(
335 arm.pat.kind,
336 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
337 if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone)
338 )
339}
340
341pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
343 match *qpath {
344 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
345 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
346 _ => false,
347 }
348}
349
350pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
353 let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
354 let trt_id = cx.tcx.trait_of_item(def_id);
355 trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path))
356}
357
358pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
360 if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
361 cx.tcx.trait_of_item(method_id).is_none()
362 } else {
363 false
364 }
365}
366
367pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
369 if let Some(impl_did) = cx.tcx.impl_of_method(def_id)
370 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
371 {
372 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
373 }
374 false
375}
376
377pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
379 if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
380 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
381 }
382 false
383}
384
385pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
387 cx.typeck_results()
388 .type_dependent_def_id(expr.hir_id)
389 .is_some_and(|did| is_diag_trait_item(cx, did, diag_item))
390}
391
392pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
394 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
395 && let ItemKind::Impl(imp) = item.kind
396 {
397 imp.of_trait.is_some()
398 } else {
399 false
400 }
401}
402
403pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
413 if let ExprKind::Path(ref qpath) = expr.kind {
414 cx.qpath_res(qpath, expr.hir_id)
415 .opt_def_id()
416 .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item))
417 } else {
418 false
419 }
420}
421
422pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
423 match *path {
424 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
425 QPath::TypeRelative(_, seg) => seg,
426 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
427 }
428}
429
430pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
431 last_path_segment(qpath)
432 .args
433 .map_or(&[][..], |a| a.args)
434 .iter()
435 .filter_map(|a| match a {
436 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
437 _ => None,
438 })
439}
440
441pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
455 match *path {
456 QPath::Resolved(_, path) => match_path(path, segments),
457 QPath::TypeRelative(ty, segment) => match ty.kind {
458 TyKind::Path(ref inner_path) => {
459 if let [prefix @ .., end] = segments
460 && match_qpath(inner_path, prefix)
461 {
462 return segment.ident.name.as_str() == *end;
463 }
464 false
465 },
466 _ => false,
467 },
468 QPath::LangItem(..) => false,
469 }
470}
471
472pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
476 path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments))
477}
478
479pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
482 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id))
483}
484
485pub fn is_path_diagnostic_item<'tcx>(
488 cx: &LateContext<'_>,
489 maybe_path: &impl MaybePath<'tcx>,
490 diag_item: Symbol,
491) -> bool {
492 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
493}
494
495pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
516 path.segments
517 .iter()
518 .rev()
519 .zip(segments.iter().rev())
520 .all(|(a, b)| a.ident.name.as_str() == *b)
521}
522
523pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
525 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
526 && let Res::Local(id) = path.res
527 {
528 return Some(id);
529 }
530 None
531}
532
533pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
536 path_to_local(expr) == Some(id)
537}
538
539pub trait MaybePath<'hir> {
540 fn hir_id(&self) -> HirId;
541 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
542}
543
544macro_rules! maybe_path {
545 ($ty:ident, $kind:ident) => {
546 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
547 fn hir_id(&self) -> HirId {
548 self.hir_id
549 }
550 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
551 match &self.kind {
552 hir::$kind::Path(qpath) => Some(qpath),
553 _ => None,
554 }
555 }
556 }
557 };
558}
559maybe_path!(Expr, ExprKind);
560impl<'hir> MaybePath<'hir> for Pat<'hir> {
561 fn hir_id(&self) -> HirId {
562 self.hir_id
563 }
564 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
565 match &self.kind {
566 PatKind::Expr(PatExpr {
567 kind: PatExprKind::Path(qpath),
568 ..
569 }) => Some(qpath),
570 _ => None,
571 }
572 }
573}
574maybe_path!(Ty, TyKind);
575
576pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
578 match maybe_path.qpath_opt() {
579 None => Res::Err,
580 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
581 }
582}
583
584pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
586 path_res(cx, maybe_path).opt_def_id()
587}
588
589fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
590 let ty = match name {
591 "bool" => SimplifiedType::Bool,
592 "char" => SimplifiedType::Char,
593 "str" => SimplifiedType::Str,
594 "array" => SimplifiedType::Array,
595 "slice" => SimplifiedType::Slice,
596 "const_ptr" => SimplifiedType::Ptr(Mutability::Not),
600 "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut),
601 "isize" => SimplifiedType::Int(IntTy::Isize),
602 "i8" => SimplifiedType::Int(IntTy::I8),
603 "i16" => SimplifiedType::Int(IntTy::I16),
604 "i32" => SimplifiedType::Int(IntTy::I32),
605 "i64" => SimplifiedType::Int(IntTy::I64),
606 "i128" => SimplifiedType::Int(IntTy::I128),
607 "usize" => SimplifiedType::Uint(UintTy::Usize),
608 "u8" => SimplifiedType::Uint(UintTy::U8),
609 "u16" => SimplifiedType::Uint(UintTy::U16),
610 "u32" => SimplifiedType::Uint(UintTy::U32),
611 "u64" => SimplifiedType::Uint(UintTy::U64),
612 "u128" => SimplifiedType::Uint(UintTy::U128),
613 "f32" => SimplifiedType::Float(FloatTy::F32),
614 "f64" => SimplifiedType::Float(FloatTy::F64),
615 _ => {
616 return [].iter().copied();
617 },
618 };
619
620 tcx.incoherent_impls(ty).iter().copied()
621}
622
623fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
624 match tcx.def_kind(def_id) {
625 DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
626 .module_children(def_id)
627 .iter()
628 .filter(|item| item.ident.name == name)
629 .map(|child| child.res.expect_non_local())
630 .collect(),
631 DefKind::Impl { .. } => tcx
632 .associated_item_def_ids(def_id)
633 .iter()
634 .copied()
635 .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
636 .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
637 .collect(),
638 _ => Vec::new(),
639 }
640}
641
642fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
643 let root_mod;
644 let item_kind = match tcx.hir_node_by_def_id(local_id) {
645 Node::Crate(r#mod) => {
646 root_mod = ItemKind::Mod(Ident::dummy(), r#mod);
647 &root_mod
648 },
649 Node::Item(item) => &item.kind,
650 _ => return Vec::new(),
651 };
652
653 let res = |ident: Ident, owner_id: OwnerId| {
654 if ident.name == name {
655 let def_id = owner_id.to_def_id();
656 Some(Res::Def(tcx.def_kind(def_id), def_id))
657 } else {
658 None
659 }
660 };
661
662 match item_kind {
663 ItemKind::Mod(_, r#mod) => r#mod
664 .item_ids
665 .iter()
666 .filter_map(|&item_id| {
667 let ident = tcx.hir_item(item_id).kind.ident()?;
668 res(ident, item_id.owner_id)
669 })
670 .collect(),
671 ItemKind::Impl(r#impl) => r#impl
672 .items
673 .iter()
674 .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
675 .collect(),
676 ItemKind::Trait(.., trait_item_refs) => trait_item_refs
677 .iter()
678 .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
679 .collect(),
680 _ => Vec::new(),
681 }
682}
683
684fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
685 if let Some(local_id) = def_id.as_local() {
686 local_item_children_by_name(tcx, local_id, name)
687 } else {
688 non_local_item_children_by_name(tcx, def_id, name)
689 }
690}
691
692pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec<Res> {
694 tcx.crates(())
695 .iter()
696 .copied()
697 .filter(move |&num| tcx.crate_name(num) == name)
698 .map(CrateNum::as_def_id)
699 .map(|id| Res::Def(tcx.def_kind(id), id))
700 .collect()
701}
702
703pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec<Res> {
713 let (base, path) = match path {
714 [primitive] => {
715 return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
716 },
717 [base, path @ ..] => (base, path),
718 _ => return Vec::new(),
719 };
720
721 let base_sym = Symbol::intern(base);
722
723 let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
724 Some(LOCAL_CRATE.as_def_id())
725 } else {
726 None
727 };
728
729 let crates = find_primitive_impls(tcx, base)
730 .chain(local_crate)
731 .map(|id| Res::Def(tcx.def_kind(id), id))
732 .chain(find_crates(tcx, base_sym))
733 .collect();
734
735 def_path_res_with_base(tcx, crates, path)
736}
737
738pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec<Res>, mut path: &[&str]) -> Vec<Res> {
743 while let [segment, rest @ ..] = path {
744 path = rest;
745 let segment = Symbol::intern(segment);
746
747 base = base
748 .into_iter()
749 .filter_map(|res| res.opt_def_id())
750 .flat_map(|def_id| {
751 let inherent_impl_children = tcx
754 .inherent_impls(def_id)
755 .iter()
756 .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
757
758 let direct_children = item_children_by_name(tcx, def_id, segment);
759
760 inherent_impl_children.chain(direct_children)
761 })
762 .collect();
763 }
764
765 base
766}
767
768pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> + use<> {
770 def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id())
771}
772
773pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option<DefId> {
778 def_path_res(tcx, path).into_iter().find_map(|res| match res {
779 Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
780 _ => None,
781 })
782}
783
784pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
800 let hir_id = cx.tcx.local_def_id_to_hir_id(def_id);
802 let parent_impl = cx.tcx.hir_get_parent_item(hir_id);
803 if parent_impl != hir::CRATE_OWNER_ID
804 && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_impl.def_id)
805 && let ItemKind::Impl(impl_) = &item.kind
806 {
807 return impl_.of_trait.as_ref();
808 }
809 None
810}
811
812fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
820 let mut result = vec![];
821 let root = loop {
822 match e.kind {
823 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
824 result.push(e);
825 e = ep;
826 },
827 _ => break e,
828 }
829 };
830 result.reverse();
831 (result, root)
832}
833
834pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
836 cx.typeck_results()
837 .expr_adjustments(e)
838 .iter()
839 .find_map(|a| match a.kind {
840 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
841 Adjust::Deref(None) => None,
842 _ => Some(None),
843 })
844 .and_then(|x| x)
845}
846
847pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
850 let (s1, r1) = projection_stack(e1);
851 let (s2, r2) = projection_stack(e2);
852 if !eq_expr_value(cx, r1, r2) {
853 return true;
854 }
855 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
856 return false;
857 }
858
859 for (x1, x2) in s1.iter().zip(s2.iter()) {
860 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
861 return false;
862 }
863
864 match (&x1.kind, &x2.kind) {
865 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
866 if i1 != i2 {
867 return true;
868 }
869 },
870 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
871 if !eq_expr_value(cx, i1, i2) {
872 return false;
873 }
874 },
875 _ => return false,
876 }
877 }
878 false
879}
880
881fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
884 let std_types_symbols = &[
885 sym::Vec,
886 sym::VecDeque,
887 sym::LinkedList,
888 sym::HashMap,
889 sym::BTreeMap,
890 sym::HashSet,
891 sym::BTreeSet,
892 sym::BinaryHeap,
893 ];
894
895 if let QPath::TypeRelative(_, method) = path
896 && method.ident.name == sym::new
897 && let Some(impl_did) = cx.tcx.impl_of_method(def_id)
898 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
899 {
900 return std_types_symbols.iter().any(|&symbol| {
901 cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
902 });
903 }
904 false
905}
906
907pub fn is_default_equivalent_call(
909 cx: &LateContext<'_>,
910 repl_func: &Expr<'_>,
911 whole_call_expr: Option<&Expr<'_>>,
912) -> bool {
913 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
914 && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
915 && (is_diag_trait_item(cx, repl_def_id, sym::Default)
916 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath))
917 {
918 return true;
919 }
920
921 let Some(e) = whole_call_expr else { return false };
924 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
925 return false;
926 };
927 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
928 return false;
929 };
930 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
931 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
932 cx.tcx.lifetimes.re_erased.into()
933 } else if param.index == 0 && param.name == kw::SelfUpper {
934 ty.into()
935 } else {
936 param.to_error(cx.tcx)
937 }
938 });
939 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
940
941 let Ok(Some(instance)) = instance else { return false };
942 if let rustc_ty::InstanceKind::Item(def) = instance.def
943 && !cx.tcx.is_mir_available(def)
944 {
945 return false;
946 }
947 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
948 return false;
949 };
950 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
951 return false;
952 };
953
954 let body = cx.tcx.instance_mir(instance.def);
960 for block_data in body.basic_blocks.iter() {
961 if block_data.statements.len() == 1
962 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
963 && assign.0.local == RETURN_PLACE
964 && let Rvalue::Aggregate(kind, _places) = &assign.1
965 && let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
966 && let def = cx.tcx.adt_def(did)
967 && let variant = &def.variant(*variant_index)
968 && variant.fields.is_empty()
969 && let Some((_, did)) = variant.ctor
970 && did == repl_def_id
971 {
972 return true;
973 } else if block_data.statements.is_empty()
974 && let Some(term) = &block_data.terminator
975 {
976 match &term.kind {
977 TerminatorKind::Call {
978 func: Operand::Constant(c),
979 ..
980 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
981 && *did == repl_def_id =>
982 {
983 return true;
984 },
985 TerminatorKind::TailCall {
986 func: Operand::Constant(c),
987 ..
988 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
989 && *did == repl_def_id =>
990 {
991 return true;
992 },
993 _ => {},
994 }
995 }
996 }
997 false
998}
999
1000pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1004 match &e.kind {
1005 ExprKind::Lit(lit) => match lit.node {
1006 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
1007 LitKind::Str(s, _) => s.is_empty(),
1008 _ => false,
1009 },
1010 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
1011 ExprKind::Repeat(x, len) => {
1012 if let ConstArgKind::Anon(anon_const) = len.kind
1013 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
1014 && let LitKind::Int(v, _) = const_lit.node
1015 && v <= 32
1016 && is_default_equivalent(cx, x)
1017 {
1018 true
1019 } else {
1020 false
1021 }
1022 },
1023 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
1024 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
1025 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
1026 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
1027 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
1028 _ => false,
1029 }
1030}
1031
1032fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
1033 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
1034 && seg.ident.name == sym::from
1035 {
1036 match arg.kind {
1037 ExprKind::Lit(hir::Lit {
1038 node: LitKind::Str(sym, _),
1039 ..
1040 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
1041 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
1042 ExprKind::Repeat(_, len) => {
1043 if let ConstArgKind::Anon(anon_const) = len.kind
1044 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
1045 && let LitKind::Int(v, _) = const_lit.node
1046 {
1047 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
1048 }
1049 },
1050 _ => (),
1051 }
1052 }
1053 false
1054}
1055
1056pub fn can_move_expr_to_closure_no_visit<'tcx>(
1088 cx: &LateContext<'tcx>,
1089 expr: &'tcx Expr<'_>,
1090 loop_ids: &[HirId],
1091 ignore_locals: &HirIdSet,
1092) -> bool {
1093 match expr.kind {
1094 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
1095 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
1096 if loop_ids.contains(&id) =>
1097 {
1098 true
1099 },
1100 ExprKind::Break(..)
1101 | ExprKind::Continue(_)
1102 | ExprKind::Ret(_)
1103 | ExprKind::Yield(..)
1104 | ExprKind::InlineAsm(_) => false,
1105 ExprKind::Field(
1108 &Expr {
1109 hir_id,
1110 kind:
1111 ExprKind::Path(QPath::Resolved(
1112 _,
1113 Path {
1114 res: Res::Local(local_id),
1115 ..
1116 },
1117 )),
1118 ..
1119 },
1120 _,
1121 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
1122 false
1124 },
1125 _ => true,
1126 }
1127}
1128
1129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1131pub enum CaptureKind {
1132 Value,
1133 Use,
1134 Ref(Mutability),
1135}
1136impl CaptureKind {
1137 pub fn is_imm_ref(self) -> bool {
1138 self == Self::Ref(Mutability::Not)
1139 }
1140}
1141impl std::ops::BitOr for CaptureKind {
1142 type Output = Self;
1143 fn bitor(self, rhs: Self) -> Self::Output {
1144 match (self, rhs) {
1145 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
1146 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
1147 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
1148 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
1149 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
1150 }
1151 }
1152}
1153impl std::ops::BitOrAssign for CaptureKind {
1154 fn bitor_assign(&mut self, rhs: Self) {
1155 *self = *self | rhs;
1156 }
1157}
1158
1159pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
1165 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
1166 let mut capture = CaptureKind::Ref(Mutability::Not);
1167 pat.each_binding_or_first(&mut |_, id, span, _| match cx
1168 .typeck_results()
1169 .extract_binding_mode(cx.sess(), id, span)
1170 .0
1171 {
1172 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
1173 capture = CaptureKind::Value;
1174 },
1175 ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
1176 capture = CaptureKind::Ref(Mutability::Mut);
1177 },
1178 _ => (),
1179 });
1180 capture
1181 }
1182
1183 debug_assert!(matches!(
1184 e.kind,
1185 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
1186 ));
1187
1188 let mut child_id = e.hir_id;
1189 let mut capture = CaptureKind::Value;
1190 let mut capture_expr_ty = e;
1191
1192 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
1193 if let [
1194 Adjustment {
1195 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
1196 target,
1197 },
1198 ref adjust @ ..,
1199 ] = *cx
1200 .typeck_results()
1201 .adjustments()
1202 .get(child_id)
1203 .map_or(&[][..], |x| &**x)
1204 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
1205 *adjust.last().map_or(target, |a| a.target).kind()
1206 {
1207 return CaptureKind::Ref(mutability);
1208 }
1209
1210 match parent {
1211 Node::Expr(e) => match e.kind {
1212 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
1213 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
1214 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
1215 return CaptureKind::Ref(Mutability::Mut);
1216 },
1217 ExprKind::Field(..) => {
1218 if capture == CaptureKind::Value {
1219 capture_expr_ty = e;
1220 }
1221 },
1222 ExprKind::Let(let_expr) => {
1223 let mutability = match pat_capture_kind(cx, let_expr.pat) {
1224 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
1225 CaptureKind::Ref(m) => m,
1226 };
1227 return CaptureKind::Ref(mutability);
1228 },
1229 ExprKind::Match(_, arms, _) => {
1230 let mut mutability = Mutability::Not;
1231 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
1232 match capture {
1233 CaptureKind::Value | CaptureKind::Use => break,
1234 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
1235 CaptureKind::Ref(Mutability::Not) => (),
1236 }
1237 }
1238 return CaptureKind::Ref(mutability);
1239 },
1240 _ => break,
1241 },
1242 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
1243 CaptureKind::Value | CaptureKind::Use => break,
1244 capture @ CaptureKind::Ref(_) => return capture,
1245 },
1246 _ => break,
1247 }
1248
1249 child_id = parent_id;
1250 }
1251
1252 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
1253 CaptureKind::Ref(Mutability::Not)
1255 } else {
1256 capture
1257 }
1258}
1259
1260pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
1263 struct V<'cx, 'tcx> {
1264 cx: &'cx LateContext<'tcx>,
1265 loops: Vec<HirId>,
1267 locals: HirIdSet,
1269 allow_closure: bool,
1271 captures: HirIdMap<CaptureKind>,
1274 }
1275 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1276 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1277 if !self.allow_closure {
1278 return;
1279 }
1280
1281 match e.kind {
1282 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1283 if !self.locals.contains(&l) {
1284 let cap = capture_local_usage(self.cx, e);
1285 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1286 }
1287 },
1288 ExprKind::Closure(closure) => {
1289 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
1290 let local_id = match capture.place.base {
1291 PlaceBase::Local(id) => id,
1292 PlaceBase::Upvar(var) => var.var_path.hir_id,
1293 _ => continue,
1294 };
1295 if !self.locals.contains(&local_id) {
1296 let capture = match capture.info.capture_kind {
1297 UpvarCapture::ByValue => CaptureKind::Value,
1298 UpvarCapture::ByUse => CaptureKind::Use,
1299 UpvarCapture::ByRef(kind) => match kind {
1300 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
1301 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
1302 CaptureKind::Ref(Mutability::Mut)
1303 },
1304 },
1305 };
1306 self.captures
1307 .entry(local_id)
1308 .and_modify(|e| *e |= capture)
1309 .or_insert(capture);
1310 }
1311 }
1312 },
1313 ExprKind::Loop(b, ..) => {
1314 self.loops.push(e.hir_id);
1315 self.visit_block(b);
1316 self.loops.pop();
1317 },
1318 _ => {
1319 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1320 walk_expr(self, e);
1321 },
1322 }
1323 }
1324
1325 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1326 p.each_binding_or_first(&mut |_, id, _, _| {
1327 self.locals.insert(id);
1328 });
1329 }
1330 }
1331
1332 let mut v = V {
1333 cx,
1334 loops: Vec::new(),
1335 locals: HirIdSet::default(),
1336 allow_closure: true,
1337 captures: HirIdMap::default(),
1338 };
1339 v.visit_expr(expr);
1340 v.allow_closure.then_some(v.captures)
1341}
1342
1343pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1345
1346pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1349 let mut method_names = Vec::with_capacity(max_depth);
1350 let mut arg_lists = Vec::with_capacity(max_depth);
1351 let mut spans = Vec::with_capacity(max_depth);
1352
1353 let mut current = expr;
1354 for _ in 0..max_depth {
1355 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1356 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1357 break;
1358 }
1359 method_names.push(path.ident.name);
1360 arg_lists.push((*receiver, &**args));
1361 spans.push(path.ident.span);
1362 current = receiver;
1363 } else {
1364 break;
1365 }
1366 }
1367
1368 (method_names, arg_lists, spans)
1369}
1370
1371pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1378 let mut current = expr;
1379 let mut matched = Vec::with_capacity(methods.len());
1380 for method_name in methods.iter().rev() {
1381 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1383 if path.ident.name.as_str() == *method_name {
1384 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1385 return None;
1386 }
1387 matched.push((receiver, args)); current = receiver; } else {
1390 return None;
1391 }
1392 } else {
1393 return None;
1394 }
1395 }
1396 matched.reverse();
1398 Some(matched)
1399}
1400
1401pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1403 cx.tcx
1404 .entry_fn(())
1405 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1406}
1407
1408pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1410 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1411 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1412}
1413
1414pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1416 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1417 match cx.tcx.hir_node_by_def_id(parent_id) {
1418 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1419 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1420 _ => None,
1421 }
1422}
1423
1424pub struct ContainsName<'a, 'tcx> {
1425 pub cx: &'a LateContext<'tcx>,
1426 pub name: Symbol,
1427}
1428
1429impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1430 type Result = ControlFlow<()>;
1431 type NestedFilter = nested_filter::OnlyBodies;
1432
1433 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1434 if self.name == name {
1435 ControlFlow::Break(())
1436 } else {
1437 ControlFlow::Continue(())
1438 }
1439 }
1440
1441 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1442 self.cx.tcx
1443 }
1444}
1445
1446pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1448 let mut cn = ContainsName { cx, name };
1449 cn.visit_expr(expr).is_break()
1450}
1451
1452pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1454 for_each_expr_without_closures(expr, |e| {
1455 if matches!(e.kind, ExprKind::Ret(..)) {
1456 ControlFlow::Break(())
1457 } else {
1458 ControlFlow::Continue(())
1459 }
1460 })
1461 .is_some()
1462}
1463
1464pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1466 get_parent_expr_for_hir(cx, e.hir_id)
1467}
1468
1469pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1472 match cx.tcx.parent_hir_node(hir_id) {
1473 Node::Expr(parent) => Some(parent),
1474 _ => None,
1475 }
1476}
1477
1478pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1480 let enclosing_node = cx
1481 .tcx
1482 .hir_get_enclosing_scope(hir_id)
1483 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1484 enclosing_node.and_then(|node| match node {
1485 Node::Block(block) => Some(block),
1486 Node::Item(&Item {
1487 kind: ItemKind::Fn { body: eid, .. },
1488 ..
1489 })
1490 | Node::ImplItem(&ImplItem {
1491 kind: ImplItemKind::Fn(_, eid),
1492 ..
1493 })
1494 | Node::TraitItem(&TraitItem {
1495 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1496 ..
1497 }) => match cx.tcx.hir_body(eid).value.kind {
1498 ExprKind::Block(block, _) => Some(block),
1499 _ => None,
1500 },
1501 _ => None,
1502 })
1503}
1504
1505pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1507 cx: &LateContext<'tcx>,
1508 expr: &Expr<'_>,
1509) -> Option<&'tcx Expr<'tcx>> {
1510 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1511 match node {
1512 Node::Expr(e) => match e.kind {
1513 ExprKind::Closure { .. }
1514 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1515 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1516
1517 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1519 _ => (),
1520 },
1521 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1522 _ => break,
1523 }
1524 }
1525 None
1526}
1527
1528pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1530 match tcx.hir_parent_iter(id).next() {
1531 Some((
1532 _,
1533 Node::Item(Item {
1534 kind: ItemKind::Impl(imp),
1535 ..
1536 }),
1537 )) => Some(imp),
1538 _ => None,
1539 }
1540}
1541
1542pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1553 while let ExprKind::Block(
1554 Block {
1555 stmts: [],
1556 expr: Some(inner),
1557 rules: BlockCheckMode::DefaultBlock,
1558 ..
1559 },
1560 _,
1561 ) = expr.kind
1562 {
1563 expr = inner;
1564 }
1565 expr
1566}
1567
1568pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1579 while let ExprKind::Block(
1580 Block {
1581 stmts: [],
1582 expr: Some(inner),
1583 rules: BlockCheckMode::DefaultBlock,
1584 ..
1585 }
1586 | Block {
1587 stmts:
1588 [
1589 Stmt {
1590 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1591 ..
1592 },
1593 ],
1594 expr: None,
1595 rules: BlockCheckMode::DefaultBlock,
1596 ..
1597 },
1598 _,
1599 ) = expr.kind
1600 {
1601 expr = inner;
1602 }
1603 expr
1604}
1605
1606pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1608 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1609 match iter.next() {
1610 Some((
1611 _,
1612 Node::Expr(Expr {
1613 kind: ExprKind::If(_, _, Some(else_expr)),
1614 ..
1615 }),
1616 )) => else_expr.hir_id == expr.hir_id,
1617 _ => false,
1618 }
1619}
1620
1621pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1624 let mut child_id = expr.hir_id;
1625 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1626 if let Node::LetStmt(LetStmt {
1627 init: Some(init),
1628 els: Some(els),
1629 ..
1630 }) = node
1631 && (init.hir_id == child_id || els.hir_id == child_id)
1632 {
1633 return true;
1634 }
1635
1636 child_id = parent_id;
1637 }
1638
1639 false
1640}
1641
1642pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1644 let mut child_id = expr.hir_id;
1645 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1646 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1647 && els.hir_id == child_id
1648 {
1649 return true;
1650 }
1651
1652 child_id = parent_id;
1653 }
1654
1655 false
1656}
1657
1658pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1673 let ty = cx.typeck_results().expr_ty(expr);
1674 if let Some(Range { start, end, limits }) = Range::hir(expr) {
1675 let start_is_none_or_min = start.is_none_or(|start| {
1676 if let rustc_ty::Adt(_, subst) = ty.kind()
1677 && let bnd_ty = subst.type_at(0)
1678 && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx)
1679 && let Some(min_const) = mir_to_const(cx.tcx, min_const)
1680 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1681 {
1682 start_const == min_const
1683 } else {
1684 false
1685 }
1686 });
1687 let end_is_none_or_max = end.is_none_or(|end| match limits {
1688 RangeLimits::Closed => {
1689 if let rustc_ty::Adt(_, subst) = ty.kind()
1690 && let bnd_ty = subst.type_at(0)
1691 && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx)
1692 && let Some(max_const) = mir_to_const(cx.tcx, max_const)
1693 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1694 {
1695 end_const == max_const
1696 } else {
1697 false
1698 }
1699 },
1700 RangeLimits::HalfOpen => {
1701 if let Some(container_path) = container_path
1702 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1703 && name.ident.name == sym::len
1704 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1705 {
1706 container_path.res == path.res
1707 } else {
1708 false
1709 }
1710 },
1711 });
1712 return start_is_none_or_min && end_is_none_or_max;
1713 }
1714 false
1715}
1716
1717pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1720 if is_integer_literal(e, value) {
1721 return true;
1722 }
1723 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1724 if let Some(Constant::Int(v)) =
1725 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1726 {
1727 return value == v;
1728 }
1729 false
1730}
1731
1732pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1734 if let ExprKind::Lit(spanned) = expr.kind
1736 && let LitKind::Int(v, _) = spanned.node
1737 {
1738 return v == value;
1739 }
1740 false
1741}
1742
1743pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1745 if let ExprKind::Lit(spanned) = expr.kind
1746 && let LitKind::Float(v, _) = spanned.node
1747 {
1748 v.as_str().parse() == Ok(value)
1749 } else {
1750 false
1751 }
1752}
1753
1754pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1762 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1763}
1764
1765#[must_use]
1769pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
1770 loop {
1771 if span.from_expansion() {
1772 let data = span.ctxt().outer_expn_data();
1773 let new_span = data.call_site;
1774
1775 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1776 && mac_name.as_str() == name
1777 {
1778 return Some(new_span);
1779 }
1780
1781 span = new_span;
1782 } else {
1783 return None;
1784 }
1785 }
1786}
1787
1788#[must_use]
1799pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
1800 if span.from_expansion() {
1801 let data = span.ctxt().outer_expn_data();
1802 let new_span = data.call_site;
1803
1804 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1805 && mac_name.as_str() == name
1806 {
1807 return Some(new_span);
1808 }
1809 }
1810
1811 None
1812}
1813
1814pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1816 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1817 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1818}
1819
1820pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1822 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1823 cx.tcx.instantiate_bound_regions_with_erased(arg)
1824}
1825
1826pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1828 if let ExprKind::Call(fun, _) = expr.kind
1829 && let ExprKind::Path(ref qp) = fun.kind
1830 {
1831 let res = cx.qpath_res(qp, fun.hir_id);
1832 return match res {
1833 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1834 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1835 _ => false,
1836 };
1837 }
1838 false
1839}
1840
1841pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1844 fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1845 matches!(
1846 cx.qpath_res(qpath, id),
1847 Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
1848 )
1849 }
1850
1851 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1852 i.into_iter().any(|pat| is_refutable(cx, pat))
1853 }
1854
1855 match pat.kind {
1856 PatKind::Missing => unreachable!(),
1857 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1859 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1860 PatKind::Expr(PatExpr {
1861 kind: PatExprKind::Path(qpath),
1862 hir_id,
1863 ..
1864 }) => is_enum_variant(cx, qpath, *hir_id),
1865 PatKind::Or(pats) => {
1866 are_refutable(cx, pats)
1868 },
1869 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1870 PatKind::Struct(ref qpath, fields, _) => {
1871 is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1872 },
1873 PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
1874 PatKind::Slice(head, middle, tail) => {
1875 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1876 rustc_ty::Slice(..) => {
1877 !head.is_empty() || middle.is_none() || !tail.is_empty()
1879 },
1880 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1881 _ => {
1882 true
1884 },
1885 }
1886 },
1887 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1888 }
1889}
1890
1891pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1894 if let PatKind::Or(pats) = pat.kind {
1895 pats.iter().for_each(f);
1896 } else {
1897 f(pat);
1898 }
1899}
1900
1901pub fn is_self(slf: &Param<'_>) -> bool {
1902 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1903 name.name == kw::SelfLower
1904 } else {
1905 false
1906 }
1907}
1908
1909pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1910 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1911 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1912 {
1913 return true;
1914 }
1915 false
1916}
1917
1918pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1919 (0..decl.inputs.len()).map(move |i| &body.params[i])
1920}
1921
1922pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1925 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1926 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1927 && ddpos.as_opt_usize().is_none()
1928 && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk)
1929 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1930 && path_to_local_id(arm.body, hir_id)
1931 {
1932 return true;
1933 }
1934 false
1935 }
1936
1937 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1938 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1939 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1940 } else {
1941 false
1942 }
1943 }
1944
1945 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1946 if let MatchSource::TryDesugar(_) = *source {
1948 return Some(expr);
1949 }
1950
1951 if arms.len() == 2
1952 && arms[0].guard.is_none()
1953 && arms[1].guard.is_none()
1954 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1955 {
1956 return Some(expr);
1957 }
1958 }
1959
1960 None
1961}
1962
1963pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1973 let mut suppress_lint = false;
1974
1975 for id in ids {
1976 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1977 if let Some(expectation) = lint_id {
1978 cx.fulfill_expectation(expectation);
1979 }
1980
1981 match level {
1982 Level::Allow | Level::Expect => suppress_lint = true,
1983 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1984 }
1985 }
1986
1987 suppress_lint
1988}
1989
1990pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1998 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1999}
2000
2001pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
2002 while let PatKind::Ref(subpat, _) = pat.kind {
2003 pat = subpat;
2004 }
2005 pat
2006}
2007
2008pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
2009 Integer::from_int_ty(&tcx, ity).size().bits()
2010}
2011
2012#[expect(clippy::cast_possible_wrap)]
2013pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
2015 let amt = 128 - int_bits(tcx, ity);
2016 ((u as i128) << amt) >> amt
2017}
2018
2019#[expect(clippy::cast_sign_loss)]
2020pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
2022 let amt = 128 - int_bits(tcx, ity);
2023 ((u as u128) << amt) >> amt
2024}
2025
2026pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
2028 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
2029 let amt = 128 - bits;
2030 (u << amt) >> amt
2031}
2032
2033pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
2034 attrs.iter().any(|attr| attr.has_name(symbol))
2035}
2036
2037pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2038 find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr(..))
2039}
2040
2041pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
2042 let mut prev_enclosing_node = None;
2043 let mut enclosing_node = node;
2044 while Some(enclosing_node) != prev_enclosing_node {
2045 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
2046 return true;
2047 }
2048 prev_enclosing_node = Some(enclosing_node);
2049 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
2050 }
2051
2052 false
2053}
2054
2055pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
2058 tcx.hir_parent_owner_iter(id)
2059 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
2060 .any(|(id, _)| {
2061 has_attr(
2062 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
2063 sym::automatically_derived,
2064 )
2065 })
2066}
2067
2068pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
2073 let search_path = cx.get_def_path(did);
2074 paths
2075 .iter()
2076 .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
2077}
2078
2079pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool {
2081 let path = cx.get_def_path(did);
2083 syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
2084}
2085
2086pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
2088 let path = cx.get_def_path(did);
2089 path.first().is_some_and(|s| *s == sym::libc) && path.last().is_some_and(|s| s.as_str() == name)
2092}
2093
2094pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
2099 let mut conds = Vec::new();
2100 let mut blocks: Vec<&Block<'_>> = Vec::new();
2101
2102 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
2103 conds.push(cond);
2104 if let ExprKind::Block(block, _) = then.kind {
2105 blocks.push(block);
2106 } else {
2107 panic!("ExprKind::If node is not an ExprKind::Block");
2108 }
2109
2110 if let Some(else_expr) = r#else {
2111 expr = else_expr;
2112 } else {
2113 break;
2114 }
2115 }
2116
2117 if !blocks.is_empty()
2119 && let ExprKind::Block(block, _) = expr.kind
2120 {
2121 blocks.push(block);
2122 }
2123
2124 (conds, blocks)
2125}
2126
2127pub fn is_async_fn(kind: FnKind<'_>) -> bool {
2129 match kind {
2130 FnKind::ItemFn(_, _, header) => header.asyncness.is_async(),
2131 FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
2132 FnKind::Closure => false,
2133 }
2134}
2135
2136pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
2138 if let ExprKind::Closure(&Closure {
2139 body,
2140 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
2141 ..
2142 }) = expr.kind
2143 && let ExprKind::Block(
2144 Block {
2145 expr:
2146 Some(Expr {
2147 kind: ExprKind::DropTemps(inner_expr),
2148 ..
2149 }),
2150 ..
2151 },
2152 _,
2153 ) = tcx.hir_body(body).value.kind
2154 {
2155 Some(inner_expr)
2156 } else {
2157 None
2158 }
2159}
2160
2161pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
2163 get_async_closure_expr(tcx, body.value)
2164}
2165
2166pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2168 let did = match expr.kind {
2169 ExprKind::Call(path, _) => {
2170 if let ExprKind::Path(ref qpath) = path.kind
2171 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
2172 {
2173 Some(did)
2174 } else {
2175 None
2176 }
2177 },
2178 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
2179 _ => None,
2180 };
2181
2182 did.is_some_and(|did| cx.tcx.has_attr(did, sym::must_use))
2183}
2184
2185fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
2194 fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
2195 if cx
2196 .typeck_results()
2197 .pat_binding_modes()
2198 .get(pat.hir_id)
2199 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
2200 {
2201 return false;
2205 }
2206
2207 match (pat.kind, expr.kind) {
2208 (PatKind::Binding(_, id, _, _), _) => {
2209 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
2210 },
2211 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
2212 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
2213 {
2214 pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr))
2215 },
2216 _ => false,
2217 }
2218 }
2219
2220 let [param] = func.params else {
2221 return false;
2222 };
2223
2224 let mut expr = func.value;
2225 loop {
2226 match expr.kind {
2227 ExprKind::Block(
2228 &Block {
2229 stmts: [],
2230 expr: Some(e),
2231 ..
2232 },
2233 _,
2234 )
2235 | ExprKind::Ret(Some(e)) => expr = e,
2236 ExprKind::Block(
2237 &Block {
2238 stmts: [stmt],
2239 expr: None,
2240 ..
2241 },
2242 _,
2243 ) => {
2244 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
2245 && let ExprKind::Ret(Some(ret_val)) = e.kind
2246 {
2247 expr = ret_val;
2248 } else {
2249 return false;
2250 }
2251 },
2252 _ => return check_pat(cx, param.pat, expr),
2253 }
2254 }
2255}
2256
2257pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2262 match expr.kind {
2263 ExprKind::Closure(&Closure { body, fn_decl, .. })
2264 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
2265 {
2266 is_body_identity_function(cx, cx.tcx.hir_body(body))
2267 },
2268 ExprKind::Path(QPath::Resolved(_, path))
2269 if path.segments.iter().all(|seg| seg.infer_args)
2270 && let Some(did) = path.res.opt_def_id() =>
2271 {
2272 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
2273 },
2274 _ => false,
2275 }
2276}
2277
2278pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2287 match expr.kind {
2288 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
2289 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
2290 }
2291}
2292
2293pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2296 let mut child_id = expr.hir_id;
2297 let mut iter = tcx.hir_parent_iter(child_id);
2298 loop {
2299 match iter.next() {
2300 None => break None,
2301 Some((id, Node::Block(_))) => child_id = id,
2302 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2303 Some((_, Node::Expr(expr))) => match expr.kind {
2304 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2305 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2306 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2307 _ => break Some((Node::Expr(expr), child_id)),
2308 },
2309 Some((_, node)) => break Some((node, child_id)),
2310 }
2311 }
2312}
2313
2314pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2316 !matches!(
2317 get_expr_use_or_unification_node(tcx, expr),
2318 None | Some((
2319 Node::Stmt(Stmt {
2320 kind: StmtKind::Expr(_)
2321 | StmtKind::Semi(_)
2322 | StmtKind::Let(LetStmt {
2323 pat: Pat {
2324 kind: PatKind::Wild,
2325 ..
2326 },
2327 ..
2328 }),
2329 ..
2330 }),
2331 _
2332 ))
2333 )
2334}
2335
2336pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2338 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2339}
2340
2341pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2345 !expr.is_place_expr(|base| {
2346 cx.typeck_results()
2347 .adjustments()
2348 .get(base.hir_id)
2349 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2350 })
2351}
2352
2353pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2354 if !is_no_std_crate(cx) {
2355 Some("std")
2356 } else if !is_no_core_crate(cx) {
2357 Some("core")
2358 } else {
2359 None
2360 }
2361}
2362
2363pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2364 cx.tcx
2365 .hir_attrs(hir::CRATE_HIR_ID)
2366 .iter()
2367 .any(|attr| attr.has_name(sym::no_std))
2368}
2369
2370pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2371 cx.tcx
2372 .hir_attrs(hir::CRATE_HIR_ID)
2373 .iter()
2374 .any(|attr| attr.has_name(sym::no_core))
2375}
2376
2377pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2387 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2388 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2389 } else {
2390 false
2391 }
2392}
2393
2394pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2404 use rustc_trait_selection::traits;
2405 let predicates = cx
2406 .tcx
2407 .predicates_of(did)
2408 .predicates
2409 .iter()
2410 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2411 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2412}
2413
2414pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2416 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2417}
2418
2419pub fn fn_def_id_with_node_args<'tcx>(
2422 cx: &LateContext<'tcx>,
2423 expr: &Expr<'_>,
2424) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2425 let typeck = cx.typeck_results();
2426 match &expr.kind {
2427 ExprKind::MethodCall(..) => Some((
2428 typeck.type_dependent_def_id(expr.hir_id)?,
2429 typeck.node_args(expr.hir_id),
2430 )),
2431 ExprKind::Call(
2432 Expr {
2433 kind: ExprKind::Path(qpath),
2434 hir_id: path_hir_id,
2435 ..
2436 },
2437 ..,
2438 ) => {
2439 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2442 typeck.qpath_res(qpath, *path_hir_id)
2443 {
2444 Some((id, typeck.node_args(*path_hir_id)))
2445 } else {
2446 None
2447 }
2448 },
2449 _ => None,
2450 }
2451}
2452
2453pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2458 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2459 let expr_kind = expr_type.kind();
2460 let is_primitive = match expr_kind {
2461 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2462 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2463 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2464 is_recursively_primitive_type(*element_type)
2465 } else {
2466 unreachable!()
2467 }
2468 },
2469 _ => false,
2470 };
2471
2472 if is_primitive {
2473 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2476 rustc_ty::Slice(..) => return Some("slice".into()),
2477 rustc_ty::Array(..) => return Some("array".into()),
2478 rustc_ty::Tuple(..) => return Some("tuple".into()),
2479 _ => {
2480 let refs_peeled = expr_type.peel_refs();
2483 return Some(refs_peeled.walk().last().unwrap().to_string());
2484 },
2485 }
2486 }
2487 None
2488}
2489
2490pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<(&T, &T)>
2497where
2498 Hash: FnMut(&T) -> u64,
2499 Eq: FnMut(&T, &T) -> bool,
2500{
2501 match exprs {
2502 [a, b] if eq(a, b) => return vec![(a, b)],
2503 _ if exprs.len() <= 2 => return vec![],
2504 _ => {},
2505 }
2506
2507 let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
2508
2509 let mut map: UnhashMap<u64, Vec<&_>> =
2510 UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
2511
2512 for expr in exprs {
2513 match map.entry(hash(expr)) {
2514 Entry::Occupied(mut o) => {
2515 for o in o.get() {
2516 if eq(o, expr) {
2517 match_expr_list.push((o, expr));
2518 }
2519 }
2520 o.get_mut().push(expr);
2521 },
2522 Entry::Vacant(v) => {
2523 v.insert(vec![expr]);
2524 },
2525 }
2526 }
2527
2528 match_expr_list
2529}
2530
2531pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2534 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2535 if let PatKind::Ref(pat, _) = pat.kind {
2536 peel(pat, count + 1)
2537 } else {
2538 (pat, count)
2539 }
2540 }
2541 peel(pat, 0)
2542}
2543
2544pub fn peel_hir_expr_while<'tcx>(
2546 mut expr: &'tcx Expr<'tcx>,
2547 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2548) -> &'tcx Expr<'tcx> {
2549 while let Some(e) = f(expr) {
2550 expr = e;
2551 }
2552 expr
2553}
2554
2555pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2558 let mut remaining = count;
2559 let e = peel_hir_expr_while(expr, |e| match e.kind {
2560 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2561 remaining -= 1;
2562 Some(e)
2563 },
2564 _ => None,
2565 });
2566 (e, count - remaining)
2567}
2568
2569pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2572 let mut count: usize = 0;
2573 let mut curr_expr = expr;
2574 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2575 count = count.wrapping_add(1);
2576 curr_expr = local_expr;
2577 }
2578 (curr_expr, count)
2579}
2580
2581pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2584 let mut count = 0;
2585 let e = peel_hir_expr_while(expr, |e| match e.kind {
2586 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2587 count += 1;
2588 Some(e)
2589 },
2590 _ => None,
2591 });
2592 (e, count)
2593}
2594
2595pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2598 let mut count = 0;
2599 loop {
2600 match &ty.kind {
2601 TyKind::Ref(_, ref_ty) => {
2602 ty = ref_ty.ty;
2603 count += 1;
2604 },
2605 _ => break (ty, count),
2606 }
2607 }
2608}
2609
2610pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2613 let mut count = 0;
2614 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2615 ty = *dest_ty;
2616 count += 1;
2617 }
2618 (ty, count)
2619}
2620
2621pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2624 loop {
2625 match expr.kind {
2626 ExprKind::AddrOf(_, _, e) => expr = e,
2627 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2628 _ => break,
2629 }
2630 }
2631 expr
2632}
2633
2634pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2635 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2636 && let Res::Def(_, def_id) = path.res
2637 {
2638 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2639 }
2640 false
2641}
2642
2643static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2644
2645fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2648 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2649 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2650 let value = map.entry(module);
2651 match value {
2652 Entry::Occupied(entry) => f(entry.get()),
2653 Entry::Vacant(entry) => {
2654 let mut names = Vec::new();
2655 for id in tcx.hir_module_free_items(module) {
2656 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2657 && let item = tcx.hir_item(id)
2658 && let ItemKind::Const(ident, ty, _generics, _body) = item.kind
2659 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2660 && let Res::Def(DefKind::Struct, _) = path.res
2662 {
2663 let has_test_marker = tcx
2664 .hir_attrs(item.hir_id())
2665 .iter()
2666 .any(|a| a.has_name(sym::rustc_test_marker));
2667 if has_test_marker {
2668 names.push(ident.name);
2669 }
2670 }
2671 }
2672 names.sort_unstable();
2673 f(entry.insert(names))
2674 },
2675 }
2676}
2677
2678pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2682 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2683 let node = tcx.hir_node(id);
2684 once((id, node))
2685 .chain(tcx.hir_parent_iter(id))
2686 .any(|(_id, node)| {
2689 if let Node::Item(item) = node
2690 && let ItemKind::Fn { ident, .. } = item.kind
2691 {
2692 return names.binary_search(&ident.name).is_ok();
2695 }
2696 false
2697 })
2698 })
2699}
2700
2701pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2708 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2709 if let Node::Item(item) = tcx.hir_node(id)
2710 && let ItemKind::Fn { ident, .. } = item.kind
2711 {
2712 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2713 names.binary_search(&ident.name).is_ok()
2714 })
2715 } else {
2716 false
2717 }
2718}
2719
2720pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2725 tcx.hir_attrs(id).iter().any(|attr| {
2726 if attr.has_name(sym::cfg_trace)
2727 && let Some(items) = attr.meta_item_list()
2728 && let [item] = &*items
2729 && item.has_name(sym::test)
2730 {
2731 true
2732 } else {
2733 false
2734 }
2735 })
2736}
2737
2738pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2740 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2741}
2742
2743pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2745 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2746}
2747
2748pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2750 tcx.has_attr(def_id, sym::cfg_trace)
2751 || tcx
2752 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2753 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2754 .any(|attr| attr.has_name(sym::cfg_trace))
2755}
2756
2757pub fn walk_to_expr_usage<'tcx, T>(
2768 cx: &LateContext<'tcx>,
2769 e: &Expr<'tcx>,
2770 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2771) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2772 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2773 let mut child_id = e.hir_id;
2774
2775 while let Some((parent_id, parent)) = iter.next() {
2776 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2777 return Some(ControlFlow::Break(x));
2778 }
2779 let parent_expr = match parent {
2780 Node::Expr(e) => e,
2781 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2782 child_id = parent_id;
2783 continue;
2784 },
2785 Node::Arm(a) if a.body.hir_id == child_id => {
2786 child_id = parent_id;
2787 continue;
2788 },
2789 _ => return Some(ControlFlow::Continue((parent, child_id))),
2790 };
2791 match parent_expr.kind {
2792 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2793 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2794 child_id = id;
2795 iter = cx.tcx.hir_parent_iter(id);
2796 },
2797 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2798 _ => return Some(ControlFlow::Continue((parent, child_id))),
2799 }
2800 }
2801 debug_assert!(false, "no parent node found for `{child_id:?}`");
2802 None
2803}
2804
2805#[derive(Clone, Copy)]
2807pub enum DefinedTy<'tcx> {
2808 Hir(&'tcx hir::Ty<'tcx>),
2810 Mir {
2818 def_site_def_id: Option<DefId>,
2819 ty: Binder<'tcx, Ty<'tcx>>,
2820 },
2821}
2822
2823pub struct ExprUseCtxt<'tcx> {
2825 pub node: Node<'tcx>,
2827 pub child_id: HirId,
2829 pub adjustments: &'tcx [Adjustment<'tcx>],
2831 pub is_ty_unified: bool,
2833 pub moved_before_use: bool,
2835 pub same_ctxt: bool,
2837}
2838impl<'tcx> ExprUseCtxt<'tcx> {
2839 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2840 match self.node {
2841 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2842 Node::ExprField(field) => ExprUseNode::Field(field),
2843
2844 Node::Item(&Item {
2845 kind: ItemKind::Static(..) | ItemKind::Const(..),
2846 owner_id,
2847 ..
2848 })
2849 | Node::TraitItem(&TraitItem {
2850 kind: TraitItemKind::Const(..),
2851 owner_id,
2852 ..
2853 })
2854 | Node::ImplItem(&ImplItem {
2855 kind: ImplItemKind::Const(..),
2856 owner_id,
2857 ..
2858 }) => ExprUseNode::ConstStatic(owner_id),
2859
2860 Node::Item(&Item {
2861 kind: ItemKind::Fn { .. },
2862 owner_id,
2863 ..
2864 })
2865 | Node::TraitItem(&TraitItem {
2866 kind: TraitItemKind::Fn(..),
2867 owner_id,
2868 ..
2869 })
2870 | Node::ImplItem(&ImplItem {
2871 kind: ImplItemKind::Fn(..),
2872 owner_id,
2873 ..
2874 }) => ExprUseNode::Return(owner_id),
2875
2876 Node::Expr(use_expr) => match use_expr.kind {
2877 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2878 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2879 }),
2880
2881 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2882 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2883 Some(i) => ExprUseNode::FnArg(func, i),
2884 None => ExprUseNode::Callee,
2885 },
2886 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2887 use_expr.hir_id,
2888 name.args,
2889 args.iter()
2890 .position(|arg| arg.hir_id == self.child_id)
2891 .map_or(0, |i| i + 1),
2892 ),
2893 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2894 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2895 _ => ExprUseNode::Other,
2896 },
2897 _ => ExprUseNode::Other,
2898 }
2899 }
2900}
2901
2902pub enum ExprUseNode<'tcx> {
2904 LetStmt(&'tcx LetStmt<'tcx>),
2906 ConstStatic(OwnerId),
2908 Return(OwnerId),
2910 Field(&'tcx ExprField<'tcx>),
2912 FnArg(&'tcx Expr<'tcx>, usize),
2914 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2916 Callee,
2918 FieldAccess(Ident),
2920 AddrOf(ast::BorrowKind, Mutability),
2922 Other,
2923}
2924impl<'tcx> ExprUseNode<'tcx> {
2925 pub fn is_return(&self) -> bool {
2927 matches!(self, Self::Return(_))
2928 }
2929
2930 pub fn is_recv(&self) -> bool {
2932 matches!(self, Self::MethodArg(_, _, 0))
2933 }
2934
2935 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2937 match *self {
2938 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2939 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2940 def_site_def_id: Some(id.def_id.to_def_id()),
2941 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2942 }),
2943 Self::Return(id) => {
2944 if let Node::Expr(Expr {
2945 kind: ExprKind::Closure(c),
2946 ..
2947 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2948 {
2949 match c.fn_decl.output {
2950 FnRetTy::DefaultReturn(_) => None,
2951 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2952 }
2953 } else {
2954 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2955 Some(DefinedTy::Mir {
2956 def_site_def_id: Some(id.def_id.to_def_id()),
2957 ty,
2958 })
2959 }
2960 },
2961 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2962 Some(Expr {
2963 hir_id,
2964 kind: ExprKind::Struct(path, ..),
2965 ..
2966 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2967 .and_then(|(adt, variant)| {
2968 variant
2969 .fields
2970 .iter()
2971 .find(|f| f.name == field.ident.name)
2972 .map(|f| (adt, f))
2973 })
2974 .map(|(adt, field_def)| DefinedTy::Mir {
2975 def_site_def_id: Some(adt.did()),
2976 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2977 }),
2978 _ => None,
2979 },
2980 Self::FnArg(callee, i) => {
2981 let sig = expr_sig(cx, callee)?;
2982 let (hir_ty, ty) = sig.input_with_hir(i)?;
2983 Some(match hir_ty {
2984 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2985 None => DefinedTy::Mir {
2986 def_site_def_id: sig.predicates_id(),
2987 ty,
2988 },
2989 })
2990 },
2991 Self::MethodArg(id, _, i) => {
2992 let id = cx.typeck_results().type_dependent_def_id(id)?;
2993 let sig = cx.tcx.fn_sig(id).skip_binder();
2994 Some(DefinedTy::Mir {
2995 def_site_def_id: Some(id),
2996 ty: sig.input(i),
2997 })
2998 },
2999 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
3000 }
3001 }
3002}
3003
3004pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprUseCtxt<'tcx> {
3006 let mut adjustments = [].as_slice();
3007 let mut is_ty_unified = false;
3008 let mut moved_before_use = false;
3009 let mut same_ctxt = true;
3010 let ctxt = e.span.ctxt();
3011 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
3012 if adjustments.is_empty()
3013 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
3014 {
3015 adjustments = cx.typeck_results().expr_adjustments(e);
3016 }
3017 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
3018 if let Node::Expr(e) = parent {
3019 match e.kind {
3020 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
3021 is_ty_unified = true;
3022 moved_before_use = true;
3023 },
3024 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
3025 is_ty_unified = true;
3026 moved_before_use = true;
3027 },
3028 ExprKind::Block(..) => moved_before_use = true,
3029 _ => {},
3030 }
3031 }
3032 ControlFlow::Continue(())
3033 });
3034 match node {
3035 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
3036 node,
3037 child_id,
3038 adjustments,
3039 is_ty_unified,
3040 moved_before_use,
3041 same_ctxt,
3042 },
3043 #[allow(unreachable_patterns)]
3044 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
3045 None => ExprUseCtxt {
3046 node: Node::Crate(cx.tcx.hir_root_module()),
3047 child_id: HirId::INVALID,
3048 adjustments: &[],
3049 is_ty_unified: true,
3050 moved_before_use: true,
3051 same_ctxt: false,
3052 },
3053 }
3054}
3055
3056pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
3058 let mut pos = 0;
3059 tokenize(s).map(move |t| {
3060 let end = pos + t.len;
3061 let range = pos as usize..end as usize;
3062 let inner = InnerSpan::new(range.start, range.end);
3063 pos = end;
3064 (t.kind, s.get(range).unwrap_or_default(), inner)
3065 })
3066}
3067
3068pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
3071 let Ok(snippet) = sm.span_to_snippet(span) else {
3072 return false;
3073 };
3074 return tokenize(&snippet).any(|token| {
3075 matches!(
3076 token.kind,
3077 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
3078 )
3079 });
3080}
3081
3082pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
3086 span_extract_comments(sm, span).join("\n")
3087}
3088
3089pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
3093 let snippet = sm.span_to_snippet(span).unwrap_or_default();
3094 tokenize_with_text(&snippet)
3095 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
3096 .map(|(_, s, _)| s.to_string())
3097 .collect::<Vec<_>>()
3098}
3099
3100pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
3101 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
3102}
3103
3104pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
3129 cx: &LateContext<'_>,
3130 pat: &'a Pat<'hir>,
3131 else_body: &Expr<'_>,
3132) -> Option<&'a Pat<'hir>> {
3133 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
3134 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
3135 && !is_refutable(cx, inner_pat)
3136 && let else_body = peel_blocks(else_body)
3137 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
3138 && let ExprKind::Path(ret_path) = ret_val.kind
3139 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
3140 {
3141 Some(inner_pat)
3142 } else {
3143 None
3144 }
3145}
3146
3147macro_rules! op_utils {
3148 ($($name:ident $assign:ident)*) => {
3149 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
3151
3152 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
3154
3155 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
3157 match kind {
3158 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
3159 _ => None,
3160 }
3161 }
3162 };
3163}
3164
3165op_utils! {
3166 Add AddAssign
3167 Sub SubAssign
3168 Mul MulAssign
3169 Div DivAssign
3170 Rem RemAssign
3171 BitXor BitXorAssign
3172 BitAnd BitAndAssign
3173 BitOr BitOrAssign
3174 Shl ShlAssign
3175 Shr ShrAssign
3176}
3177
3178pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
3181 match *pat {
3182 PatKind::Wild => true,
3183 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
3184 !visitors::is_local_used(cx, body, id)
3185 },
3186 _ => false,
3187 }
3188}
3189
3190#[derive(Clone, Copy)]
3191pub enum RequiresSemi {
3192 Yes,
3193 No,
3194}
3195impl RequiresSemi {
3196 pub fn requires_semi(self) -> bool {
3197 matches!(self, Self::Yes)
3198 }
3199}
3200
3201#[expect(clippy::too_many_lines)]
3204pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
3205 struct BreakTarget {
3206 id: HirId,
3207 unused: bool,
3208 }
3209
3210 struct V<'cx, 'tcx> {
3211 cx: &'cx LateContext<'tcx>,
3212 break_targets: Vec<BreakTarget>,
3213 break_targets_for_result_ty: u32,
3214 in_final_expr: bool,
3215 requires_semi: bool,
3216 is_never: bool,
3217 }
3218
3219 impl V<'_, '_> {
3220 fn push_break_target(&mut self, id: HirId) {
3221 self.break_targets.push(BreakTarget { id, unused: true });
3222 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3223 }
3224 }
3225
3226 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3227 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3228 if self.is_never && self.break_targets.is_empty() {
3245 if self.in_final_expr && !self.requires_semi {
3246 match e.kind {
3249 ExprKind::DropTemps(e) => self.visit_expr(e),
3250 ExprKind::If(_, then, Some(else_)) => {
3251 self.visit_expr(then);
3252 self.visit_expr(else_);
3253 },
3254 ExprKind::Match(_, arms, _) => {
3255 for arm in arms {
3256 self.visit_expr(arm.body);
3257 }
3258 },
3259 ExprKind::Loop(b, ..) => {
3260 self.push_break_target(e.hir_id);
3261 self.in_final_expr = false;
3262 self.visit_block(b);
3263 self.break_targets.pop();
3264 },
3265 ExprKind::Block(b, _) => {
3266 if b.targeted_by_break {
3267 self.push_break_target(b.hir_id);
3268 self.visit_block(b);
3269 self.break_targets.pop();
3270 } else {
3271 self.visit_block(b);
3272 }
3273 },
3274 _ => {
3275 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3276 },
3277 }
3278 }
3279 return;
3280 }
3281 match e.kind {
3282 ExprKind::DropTemps(e) => self.visit_expr(e),
3283 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3284 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3285 self.in_final_expr = false;
3286 self.visit_expr(e);
3287 self.is_never = true;
3288 },
3289 ExprKind::Break(dest, e) => {
3290 if let Some(e) = e {
3291 self.in_final_expr = false;
3292 self.visit_expr(e);
3293 }
3294 if let Ok(id) = dest.target_id
3295 && let Some((i, target)) = self
3296 .break_targets
3297 .iter_mut()
3298 .enumerate()
3299 .find(|(_, target)| target.id == id)
3300 {
3301 target.unused &= self.is_never;
3302 if i < self.break_targets_for_result_ty as usize {
3303 self.requires_semi = true;
3304 }
3305 }
3306 self.is_never = true;
3307 },
3308 ExprKind::If(cond, then, else_) => {
3309 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3310 self.visit_expr(cond);
3311 self.in_final_expr = in_final_expr;
3312
3313 if self.is_never {
3314 self.visit_expr(then);
3315 if let Some(else_) = else_ {
3316 self.visit_expr(else_);
3317 }
3318 } else {
3319 self.visit_expr(then);
3320 let is_never = mem::replace(&mut self.is_never, false);
3321 if let Some(else_) = else_ {
3322 self.visit_expr(else_);
3323 self.is_never &= is_never;
3324 }
3325 }
3326 },
3327 ExprKind::Match(scrutinee, arms, _) => {
3328 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3329 self.visit_expr(scrutinee);
3330 self.in_final_expr = in_final_expr;
3331
3332 if self.is_never {
3333 for arm in arms {
3334 self.visit_arm(arm);
3335 }
3336 } else {
3337 let mut is_never = true;
3338 for arm in arms {
3339 self.is_never = false;
3340 if let Some(guard) = arm.guard {
3341 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3342 self.visit_expr(guard);
3343 self.in_final_expr = in_final_expr;
3344 self.is_never = false;
3346 }
3347 self.visit_expr(arm.body);
3348 is_never &= self.is_never;
3349 }
3350 self.is_never = is_never;
3351 }
3352 },
3353 ExprKind::Loop(b, _, _, _) => {
3354 self.push_break_target(e.hir_id);
3355 self.in_final_expr = false;
3356 self.visit_block(b);
3357 self.is_never = self.break_targets.pop().unwrap().unused;
3358 },
3359 ExprKind::Block(b, _) => {
3360 if b.targeted_by_break {
3361 self.push_break_target(b.hir_id);
3362 self.visit_block(b);
3363 self.is_never &= self.break_targets.pop().unwrap().unused;
3364 } else {
3365 self.visit_block(b);
3366 }
3367 },
3368 _ => {
3369 self.in_final_expr = false;
3370 walk_expr(self, e);
3371 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3372 },
3373 }
3374 }
3375
3376 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3377 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3378 for s in b.stmts {
3379 self.visit_stmt(s);
3380 }
3381 self.in_final_expr = in_final_expr;
3382 if let Some(e) = b.expr {
3383 self.visit_expr(e);
3384 }
3385 }
3386
3387 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3388 if let Some(e) = l.init {
3389 self.visit_expr(e);
3390 }
3391 if let Some(else_) = l.els {
3392 let is_never = self.is_never;
3393 self.visit_block(else_);
3394 self.is_never = is_never;
3395 }
3396 }
3397
3398 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3399 if let Some(guard) = arm.guard {
3400 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3401 self.visit_expr(guard);
3402 self.in_final_expr = in_final_expr;
3403 }
3404 self.visit_expr(arm.body);
3405 }
3406 }
3407
3408 if cx.typeck_results().expr_ty(e).is_never() {
3409 Some(RequiresSemi::No)
3410 } else if let ExprKind::Block(b, _) = e.kind
3411 && !b.targeted_by_break
3412 && b.expr.is_none()
3413 {
3414 None
3416 } else {
3417 let mut v = V {
3418 cx,
3419 break_targets: Vec::new(),
3420 break_targets_for_result_ty: 0,
3421 in_final_expr: true,
3422 requires_semi: false,
3423 is_never: false,
3424 };
3425 v.visit_expr(e);
3426 v.is_never
3427 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3428 RequiresSemi::Yes
3429 } else {
3430 RequiresSemi::No
3431 })
3432 }
3433}
3434
3435pub fn get_path_from_caller_to_method_type<'tcx>(
3441 tcx: TyCtxt<'tcx>,
3442 from: LocalDefId,
3443 method: DefId,
3444 args: GenericArgsRef<'tcx>,
3445) -> String {
3446 let assoc_item = tcx.associated_item(method);
3447 let def_id = assoc_item.container_id(tcx);
3448 match assoc_item.container {
3449 rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
3450 rustc_ty::AssocItemContainer::Impl => {
3451 let ty = tcx.type_of(def_id).instantiate_identity();
3452 get_path_to_ty(tcx, from, ty, args)
3453 },
3454 }
3455}
3456
3457fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3458 match ty.kind() {
3459 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3460 rustc_ty::Array(..)
3462 | rustc_ty::Dynamic(..)
3463 | rustc_ty::Never
3464 | rustc_ty::RawPtr(_, _)
3465 | rustc_ty::Ref(..)
3466 | rustc_ty::Slice(_)
3467 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3468 _ => ty.to_string(),
3469 }
3470}
3471
3472fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3474 if callee.is_local() {
3476 let callee_path = tcx.def_path(callee);
3477 let caller_path = tcx.def_path(from.to_def_id());
3478 maybe_get_relative_path(&caller_path, &callee_path, 2)
3479 } else {
3480 tcx.def_path_str(callee)
3481 }
3482}
3483
3484fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3497 use itertools::EitherOrBoth::{Both, Left, Right};
3498
3499 let unique_parts = to
3501 .data
3502 .iter()
3503 .zip_longest(from.data.iter())
3504 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3505 .map(|el| match el {
3506 Both(l, r) => Both(l.data, r.data),
3507 Left(l) => Left(l.data),
3508 Right(r) => Right(r.data),
3509 });
3510
3511 let mut go_up_by = 0;
3513 let mut path = Vec::new();
3514 for el in unique_parts {
3515 match el {
3516 Both(l, r) => {
3517 if let DefPathData::TypeNs(s) = l {
3527 path.push(s.to_string());
3528 }
3529 if let DefPathData::TypeNs(_) = r {
3530 go_up_by += 1;
3531 }
3532 },
3533 Left(DefPathData::TypeNs(sym)) => path.push(sym.to_string()),
3538 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3543 _ => {},
3544 }
3545 }
3546
3547 if go_up_by > max_super {
3548 once(String::from("crate"))
3550 .chain(to.data.iter().filter_map(|el| {
3551 if let DefPathData::TypeNs(sym) = el.data {
3552 Some(sym.to_string())
3553 } else {
3554 None
3555 }
3556 }))
3557 .join("::")
3558 } else {
3559 repeat_n(String::from("super"), go_up_by).chain(path).join("::")
3560 }
3561}
3562
3563pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3566 matches!(
3567 cx.tcx.parent_hir_node(id),
3568 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3569 )
3570}
3571
3572pub fn is_block_like(expr: &Expr<'_>) -> bool {
3575 matches!(
3576 expr.kind,
3577 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3578 )
3579}
3580
3581pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3583 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3584 match expr.kind {
3585 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3586 _ if is_block_like(expr) => is_operand,
3587 _ => false,
3588 }
3589 }
3590
3591 contains_block(expr, false)
3592}
3593
3594pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3596 if let Some(parent_expr) = get_parent_expr(cx, expr)
3597 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3598 && receiver.hir_id == expr.hir_id
3599 {
3600 return true;
3601 }
3602 false
3603}
3604
3605pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3608 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3609 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3610 && temporary_ty
3611 .walk()
3612 .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static()))
3613 {
3614 ControlFlow::Break(())
3615 } else {
3616 ControlFlow::Continue(())
3617 }
3618 })
3619 .is_break()
3620}
3621
3622pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3633 let expr_ty_is_adjusted = cx
3634 .typeck_results()
3635 .expr_adjustments(expr)
3636 .iter()
3637 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3639 if expr_ty_is_adjusted {
3640 return true;
3641 }
3642
3643 match expr.kind {
3646 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3647 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3648
3649 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3650 return false;
3651 }
3652
3653 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3654 let mut args_with_ty_param = {
3655 fn_sig
3656 .inputs()
3657 .skip_binder()
3658 .iter()
3659 .skip(self_arg_count)
3660 .zip(args)
3661 .filter_map(|(arg_ty, arg)| {
3662 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3663 Some(arg)
3664 } else {
3665 None
3666 }
3667 })
3668 };
3669 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3670 },
3671 ExprKind::Struct(qpath, _, _) => {
3673 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3674 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3675 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3676 return true;
3678 };
3679 v_def
3680 .fields
3681 .iter()
3682 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3683 } else {
3684 false
3685 }
3686 },
3687 ExprKind::Block(
3689 &Block {
3690 expr: Some(ret_expr), ..
3691 },
3692 _,
3693 )
3694 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3695
3696 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3698 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3700 ExprKind::If(_, then, maybe_else) => {
3702 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3703 },
3704 ExprKind::Match(_, arms, _) => arms
3705 .iter()
3706 .map(|arm| arm.body)
3707 .any(|body| expr_requires_coercion(cx, body)),
3708 _ => false,
3709 }
3710}
3711
3712pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3715 if let Some(hir_id) = path_to_local(expr)
3716 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3717 {
3718 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3719 } else if let ExprKind::Path(p) = &expr.kind
3720 && let Some(mutability) = cx
3721 .qpath_res(p, expr.hir_id)
3722 .opt_def_id()
3723 .and_then(|id| cx.tcx.static_mutability(id))
3724 {
3725 mutability == Mutability::Mut
3726 } else if let ExprKind::Field(parent, _) = expr.kind {
3727 is_mutable(cx, parent)
3728 } else {
3729 true
3730 }
3731}
3732
3733pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3736 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3737 return hir_ty;
3738 };
3739 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3740 && let Some(segment) = path.segments.last()
3741 && segment.ident.name == sym::Option
3742 && let Res::Def(DefKind::Enum, def_id) = segment.res
3743 && def_id == option_def_id
3744 && let [GenericArg::Type(arg_ty)] = segment.args().args
3745 {
3746 hir_ty = arg_ty.as_unambig_ty();
3747 }
3748 hir_ty
3749}
3750
3751pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3754 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3755 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3756 && let ctxt = expr.span.ctxt()
3757 && for_each_expr_without_closures(into_future_arg, |e| {
3758 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3759 })
3760 .is_none()
3761 {
3762 Some(into_future_arg)
3763 } else {
3764 None
3765 }
3766}