1#![feature(deref_patterns)]
2#![feature(macro_metavar_expr)]
3#![feature(rustc_private)]
4#![feature(unwrap_infallible)]
5#![recursion_limit = "512"]
6#![expect(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
7#![warn(
8 rust_2018_idioms,
9 trivial_casts,
10 trivial_numeric_casts,
11 unused_lifetimes,
12 unused_qualifications,
13 rustc::internal
14)]
15
16extern crate rustc_abi;
19extern crate rustc_ast;
20extern crate rustc_attr_ir;
21extern crate rustc_attr_parsing;
22extern crate rustc_const_eval;
23extern crate rustc_data_structures;
24#[expect(
25 unused_extern_crates,
26 reason = "The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate."
27)]
28extern crate rustc_driver;
29extern crate rustc_errors;
30extern crate rustc_hir;
31extern crate rustc_hir_analysis;
32extern crate rustc_hir_typeck;
33extern crate rustc_index;
34extern crate rustc_infer;
35extern crate rustc_lexer;
36extern crate rustc_lint;
37extern crate rustc_middle;
38extern crate rustc_mir_dataflow;
39extern crate rustc_session;
40extern crate rustc_span;
41extern crate rustc_trait_selection;
42
43pub mod ast_utils;
44#[deny(missing_docs)]
45pub mod attrs;
46mod check_proc_macro;
47pub mod comparisons;
48pub mod consts;
49pub mod diagnostics;
50pub mod eager_or_lazy;
51pub mod higher;
52mod hir_utils;
53pub mod macros;
54pub mod mir;
55pub mod msrvs;
56pub mod numeric_literal;
57pub mod paths;
58pub mod qualify_min_const_fn;
59pub mod res;
60pub mod source;
61pub mod str_utils;
62pub mod sugg;
63pub mod sym;
64pub mod ty;
65pub mod usage;
66pub mod visitors;
67
68pub use self::attrs::*;
69pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
70pub use self::hir_utils::{
71 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
72 hash_stmt, is_bool, over,
73};
74
75use core::mem;
76use core::ops::ControlFlow;
77use std::collections::hash_map::Entry;
78use std::iter::{once, repeat_n, zip};
79use std::sync::{Mutex, OnceLock};
80
81use itertools::Itertools as _;
82use rustc_abi::Integer;
83use rustc_ast::ast::{self, LitKind, RangeLimits};
84use rustc_ast::{LitIntType, join_path_syms};
85use rustc_attr_ir::CfgEntry;
86use rustc_attr_ir::lang_items::LangItem;
87use rustc_attr_ir::lang_items::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
88use rustc_data_structures::fx::FxHashMap;
89use rustc_data_structures::indexmap;
90use rustc_data_structures::packed::Pu128;
91use rustc_data_structures::unhash::UnindexMap;
92use rustc_hir::def::{DefKind, Res};
93use rustc_hir::def_id::{DefId, LocalDefId, LocalModId};
94use rustc_hir::definitions::{DefPath, DefPathData};
95use rustc_hir::intravisit::{Visitor, walk_expr};
96use rustc_hir::{
97 self as hir, AnonConst, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, CRATE_HIR_ID, Closure, ConstArg,
98 ConstArgKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind,
99 FieldDef, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, HirIdSet, Impl, ImplItem, ImplItemKind, Item,
100 ItemKind, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind,
101 Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, Variant, def,
102 find_attr,
103};
104use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
105use rustc_lint::{LateContext, Level, Lint, LintContext as _};
106use rustc_middle::hir::nested_filter;
107use rustc_middle::hir::place::PlaceBase;
108use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
109use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion};
110use rustc_middle::ty::layout::IntegerExt as _;
111use rustc_middle::ty::{
112 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
113 TypeFlags, TypeVisitableExt as _, TypeckResults, UintTy, UpvarCapture,
114};
115use rustc_session::config::Input;
116use rustc_span::hygiene::{ExpnKind, MacroKind};
117use rustc_span::source_map::SourceMap;
118use rustc_span::symbol::{Ident, Symbol, kw};
119use rustc_span::{InnerSpan, Span, SyntaxContext};
120use source::{SpanExt as _, walk_span_to_context};
121use visitors::{Visitable, for_each_unconsumed_temporary};
122
123use crate::ast_utils::unordered_over;
124use crate::higher::Range;
125use crate::msrvs::Msrv;
126use crate::res::{MaybeDef as _, MaybeResPath as _};
127use crate::source::HasSourceMap;
128use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
129use crate::visitors::for_each_expr_without_closures;
130
131pub const VEC_METHODS_SHADOWING_SLICE_METHODS: [Symbol; 3] = [sym::as_ptr, sym::is_empty, sym::len];
133
134#[macro_export]
135macro_rules! extract_msrv_attr {
136 () => {
137 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
138 let sess = rustc_lint::LintContext::sess(cx);
139 self.msrv.check_attributes(attrs);
140 }
141
142 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
143 let sess = rustc_lint::LintContext::sess(cx);
144 self.msrv.check_attributes_post(attrs);
145 }
146 };
147}
148
149pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
172 while let Some(init) = expr
173 .res_local_id()
174 .and_then(|id| find_binding_init(cx, id))
175 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
176 {
177 expr = init;
178 }
179 expr
180}
181
182pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
191 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
192 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
193 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
194 {
195 return local.init;
196 }
197 None
198}
199
200pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
204 for (_, node) in cx.tcx.hir_parent_iter(local) {
205 match node {
206 Node::Pat(..) | Node::PatField(..) => {},
207 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
208 _ => return true,
209 }
210 }
211
212 false
213}
214
215pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
226 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
227 cx.enclosing_body.is_some_and(|id| {
228 cx.tcx
229 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
230 .is_some()
231 })
232}
233
234pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
241 use rustc_hir::ConstContext::{Const, ConstFn, Static};
242 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
243 return false;
244 };
245 match ctx {
246 ConstFn => false,
247 Static(_)
248 | Const {
249 allow_const_fn_promotion: _,
250 } => true,
251 }
252}
253
254pub fn is_enum_variant_ctor(
256 cx: &LateContext<'_>,
257 enum_item: Symbol,
258 variant_name: Symbol,
259 ctor_call_id: DefId,
260) -> bool {
261 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
262 return false;
263 };
264
265 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
266 variants
267 .filter(|variant| variant.name == variant_name)
268 .filter_map(|variant| variant.ctor.as_ref())
269 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
270}
271
272pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
274 let did = match cx.tcx.def_kind(did) {
275 DefKind::Ctor(..) => cx.tcx.parent(did),
276 DefKind::Variant => match cx.tcx.opt_parent(did) {
278 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
279 _ => did,
280 },
281 _ => did,
282 };
283
284 cx.tcx.is_diagnostic_item(item, did)
285}
286
287pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
289 let did = match cx.tcx.def_kind(did) {
290 DefKind::Ctor(..) => cx.tcx.parent(did),
291 DefKind::Variant => match cx.tcx.opt_parent(did) {
293 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
294 _ => did,
295 },
296 _ => did,
297 };
298
299 cx.tcx.lang_items().get(item) == Some(did)
300}
301
302pub fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
304 expr.basic_res().ctor_parent(cx).is_lang_item(cx, OptionNone)
305}
306
307pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
309 if let ExprKind::Call(e, [arg]) = expr.kind
310 && e.basic_res().ctor_parent(cx).is_lang_item(cx, OptionSome)
311 {
312 Some(arg)
313 } else {
314 None
315 }
316}
317
318pub fn is_empty_block(expr: &Expr<'_>) -> bool {
320 matches!(
321 expr.kind,
322 ExprKind::Block(
323 Block {
324 stmts: [],
325 expr: None,
326 ..
327 },
328 _,
329 )
330 )
331}
332
333pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
335 matches!(
336 expr.kind,
337 ExprKind::Block(
338 Block {
339 stmts: [],
340 expr: None,
341 ..
342 },
343 _
344 ) | ExprKind::Tup([])
345 )
346}
347
348pub fn is_wild(pat: &Pat<'_>) -> bool {
350 matches!(pat.kind, PatKind::Wild)
351}
352
353pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> {
360 if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind
361 && cx
362 .qpath_res(qpath, pat.hir_id)
363 .ctor_parent(cx)
364 .is_lang_item(cx, OptionSome)
365 {
366 Some(inner)
367 } else {
368 None
369 }
370}
371
372pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
374 matches!(pat.kind,
375 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
376 if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone))
377}
378
379pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
381 is_none_pattern(cx, arm.pat)
382 && matches!(
383 peel_blocks(arm.body).kind,
384 ExprKind::Path(qpath)
385 if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)
386 )
387}
388
389pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
391 match *qpath {
392 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
393 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
394 QPath::TypeRelative(..) => false,
395 }
396}
397
398pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
400 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
401 && let ItemKind::Impl(imp) = item.kind
402 {
403 imp.of_trait.is_some()
404 } else {
405 false
406 }
407}
408
409pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
410 match *path {
411 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
412 QPath::TypeRelative(_, seg) => seg,
413 }
414}
415
416pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
417 last_path_segment(qpath)
418 .args
419 .map_or(&[][..], |a| a.args)
420 .iter()
421 .filter_map(|a| match a {
422 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
423 _ => None,
424 })
425}
426
427pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
432 match expr.kind {
433 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
434 ExprKind::Path(QPath::Resolved(
435 _,
436 Path {
437 res: Res::Local(local), ..
438 },
439 )) => Some(*local),
440 _ => None,
441 }
442}
443
444pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
460 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
461 && let ItemKind::Impl(impl_) = &item.kind
462 && let Some(of_trait) = impl_.of_trait
463 {
464 return Some(&of_trait.trait_ref);
465 }
466 None
467}
468
469fn projection_stack<'a, 'hir>(
477 mut e: &'a Expr<'hir>,
478 ctxt: SyntaxContext,
479) -> Option<(Vec<&'a Expr<'hir>>, &'a Expr<'hir>)> {
480 let mut result = vec![];
481 let root = loop {
482 match e.kind {
483 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) if e.span.ctxt() == ctxt => {
484 result.push(e);
485 e = ep;
486 },
487 ExprKind::Index(..) | ExprKind::Field(..) => return None,
488 _ => break e,
489 }
490 };
491 result.reverse();
492 Some((result, root))
493}
494
495pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
497 cx.typeck_results()
498 .expr_adjustments(e)
499 .iter()
500 .find_map(|a| match a.kind {
501 Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)),
502 Adjust::Deref(DerefAdjustKind::Builtin) => None,
503 _ => Some(None),
504 })
505 .and_then(|x| x)
506}
507
508pub fn can_mut_borrow_both(cx: &LateContext<'_>, ctxt: SyntaxContext, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
511 let Some((s1, r1)) = projection_stack(e1, ctxt) else {
512 return false;
513 };
514 let Some((s2, r2)) = projection_stack(e2, ctxt) else {
515 return false;
516 };
517 if !eq_expr_value(cx, ctxt, r1, r2) {
518 return true;
519 }
520 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
521 return false;
522 }
523
524 for (x1, x2) in zip(&s1, &s2) {
525 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
526 return false;
527 }
528
529 match (&x1.kind, &x2.kind) {
530 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
531 if i1 != i2 {
532 return true;
533 }
534 },
535 _ => return false,
536 }
537 }
538 false
539}
540
541fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
544 let std_types_symbols = &[
545 sym::Vec,
546 sym::VecDeque,
547 sym::LinkedList,
548 sym::HashMap,
549 sym::BTreeMap,
550 sym::HashSet,
551 sym::BTreeSet,
552 sym::BinaryHeap,
553 ];
554
555 if let QPath::TypeRelative(_, method) = path
556 && method.ident.name == sym::new
557 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
558 && let Some(adt) = cx
559 .tcx
560 .type_of(impl_did)
561 .instantiate_identity()
562 .skip_norm_wip()
563 .ty_adt_def()
564 {
565 return Some(adt.did()) == cx.tcx.lang_items().string()
566 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
567 }
568 false
569}
570
571pub fn is_default_equivalent_call(
573 cx: &LateContext<'_>,
574 repl_func: &Expr<'_>,
575 whole_call_expr: Option<&Expr<'_>>,
576) -> bool {
577 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
578 && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx)
579 && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default)
580 || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath))
581 {
582 return true;
583 }
584
585 let Some(e) = whole_call_expr else { return false };
588 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
589 return false;
590 };
591 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
592 return false;
593 };
594 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
595 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
596 cx.tcx.lifetimes.re_erased.into()
597 } else if param.index == 0 && param.name == kw::SelfUpper {
598 ty.into()
599 } else {
600 param.to_error(cx.tcx)
601 }
602 });
603 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
604
605 let Ok(Some(instance)) = instance else { return false };
606 if let rustc_ty::InstanceKind::Item(def) = instance.def
607 && !cx.tcx.is_mir_available(def)
608 {
609 return false;
610 }
611 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
612 return false;
613 };
614 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
615 return false;
616 };
617
618 let body = cx.tcx.instance_mir(instance.def);
624 for block_data in body.basic_blocks.iter() {
625 if block_data.statements.len() == 1
626 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
627 && assign.0.local == RETURN_PLACE
628 && let Rvalue::Aggregate(kind, _places) = &assign.1
629 && let AggregateKind::Adt(did, variant_index, _, _, _) = **kind
630 && let def = cx.tcx.adt_def(did)
631 && let variant = &def.variant(variant_index)
632 && variant.fields.is_empty()
633 && let Some((_, did)) = variant.ctor
634 && did == repl_def_id
635 {
636 return true;
637 } else if block_data.statements.is_empty()
638 && let Some(term) = &block_data.terminator
639 {
640 match &term.kind {
641 TerminatorKind::Call {
642 func: Operand::Constant(c),
643 ..
644 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
645 && *did == repl_def_id =>
646 {
647 return true;
648 },
649 TerminatorKind::TailCall {
650 func: Operand::Constant(c),
651 ..
652 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
653 && *did == repl_def_id =>
654 {
655 return true;
656 },
657 _ => {},
658 }
659 }
660 }
661 false
662}
663
664pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
668 match &e.kind {
669 ExprKind::Lit(lit) => match lit.node {
670 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
671 LitKind::Str(s, _) => s.is_empty(),
672 _ => false,
673 },
674 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
675 ExprKind::Repeat(x, len) => {
676 if let ConstArgKind::Anon(anon_const) = len.kind
677 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
678 && let LitKind::Int(v, _) = const_lit.node
679 && v <= 32
680 && is_default_equivalent(cx, x)
681 {
682 true
683 } else {
684 false
685 }
686 },
687 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
688 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
689 ExprKind::Path(qpath) => cx
690 .qpath_res(qpath, e.hir_id)
691 .ctor_parent(cx)
692 .is_lang_item(cx, OptionNone),
693 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
694 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
695 _ => false,
696 }
697}
698
699fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
700 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
701 && seg.ident.name == sym::from
702 {
703 match arg.kind {
704 ExprKind::Lit(hir::Lit {
705 node: LitKind::Str(sym, _),
706 ..
707 }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String),
708 ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec),
709 ExprKind::Repeat(_, len) => {
710 if let ConstArgKind::Anon(anon_const) = len.kind
711 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
712 && let LitKind::Int(v, _) = const_lit.node
713 {
714 return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec);
715 }
716 },
717 _ => (),
718 }
719 }
720 false
721}
722
723pub fn can_move_expr_to_closure_no_visit<'tcx>(
755 cx: &LateContext<'tcx>,
756 expr: &'tcx Expr<'_>,
757 loop_ids: &[HirId],
758 ignore_locals: &HirIdSet,
759) -> bool {
760 match expr.kind {
761 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
762 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
763 if loop_ids.contains(&id) =>
764 {
765 true
766 },
767 ExprKind::Break(..)
768 | ExprKind::Continue(_)
769 | ExprKind::Ret(_)
770 | ExprKind::Yield(..)
771 | ExprKind::InlineAsm(_) => false,
772 ExprKind::Field(
775 &Expr {
776 hir_id,
777 kind:
778 ExprKind::Path(QPath::Resolved(
779 _,
780 Path {
781 res: Res::Local(local_id),
782 ..
783 },
784 )),
785 ..
786 },
787 _,
788 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
789 false
791 },
792 _ => true,
793 }
794}
795
796#[derive(Debug, Clone, Copy, PartialEq, Eq)]
798pub enum CaptureKind {
799 Value,
800 Use,
801 Ref(Mutability),
802}
803impl CaptureKind {
804 pub fn is_imm_ref(self) -> bool {
805 self == Self::Ref(Mutability::Not)
806 }
807}
808impl std::ops::BitOr for CaptureKind {
809 type Output = Self;
810 fn bitor(self, rhs: Self) -> Self::Output {
811 match (self, rhs) {
812 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
813 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
814 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
815 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
816 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
817 }
818 }
819}
820impl std::ops::BitOrAssign for CaptureKind {
821 fn bitor_assign(&mut self, rhs: Self) {
822 *self = *self | rhs;
823 }
824}
825
826pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
832 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
833 let mut capture = CaptureKind::Ref(Mutability::Not);
834 pat.each_binding_or_first(&mut |_, id, span, _| match cx
835 .typeck_results()
836 .extract_binding_mode(cx.sess(), id, span)
837 .0
838 {
839 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
840 capture = CaptureKind::Value;
841 },
842 ByRef::Yes(_, Mutability::Mut) if capture != CaptureKind::Value => {
843 capture = CaptureKind::Ref(Mutability::Mut);
844 },
845 _ => (),
846 });
847 capture
848 }
849
850 debug_assert!(matches!(
851 e.kind,
852 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
853 ));
854
855 let mut capture = CaptureKind::Value;
856 let mut capture_expr_ty = e;
857
858 for (parent, child_id) in hir_parent_with_src_iter(cx.tcx, e.hir_id) {
859 if let [
860 Adjustment {
861 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
862 target,
863 },
864 ref adjust @ ..,
865 ] = *cx
866 .typeck_results()
867 .adjustments()
868 .get(child_id)
869 .map_or(&[][..], |x| &**x)
870 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
871 *adjust.last().map_or(target, |a| a.target).kind()
872 {
873 return CaptureKind::Ref(mutability);
874 }
875
876 match parent {
877 Node::Expr(e) => match e.kind {
878 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
879 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
880 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
881 return CaptureKind::Ref(Mutability::Mut);
882 },
883 ExprKind::Field(..) => {
884 if capture == CaptureKind::Value {
885 capture_expr_ty = e;
886 }
887 },
888 ExprKind::Let(let_expr) => {
889 let mutability = match pat_capture_kind(cx, let_expr.pat) {
890 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
891 CaptureKind::Ref(m) => m,
892 };
893 return CaptureKind::Ref(mutability);
894 },
895 ExprKind::Match(_, arms, _) => {
896 let mut mutability = Mutability::Not;
897 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
898 match capture {
899 CaptureKind::Value | CaptureKind::Use => break,
900 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
901 CaptureKind::Ref(Mutability::Not) => (),
902 }
903 }
904 return CaptureKind::Ref(mutability);
905 },
906 _ => break,
907 },
908 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
909 CaptureKind::Value | CaptureKind::Use => break,
910 capture @ CaptureKind::Ref(_) => return capture,
911 },
912 _ => break,
913 }
914 }
915
916 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
917 CaptureKind::Ref(Mutability::Not)
919 } else {
920 capture
921 }
922}
923
924pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
927 struct V<'cx, 'tcx> {
928 cx: &'cx LateContext<'tcx>,
929 loops: Vec<HirId>,
931 locals: HirIdSet,
933 allow_closure: bool,
935 captures: HirIdMap<CaptureKind>,
938 }
939 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
940 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
941 if !self.allow_closure {
942 return;
943 }
944
945 match e.kind {
946 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
947 if !self.locals.contains(&l) {
948 let cap = capture_local_usage(self.cx, e);
949 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
950 }
951 },
952 ExprKind::Closure(closure) => {
953 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
954 let local_id = match capture.place.base {
955 PlaceBase::Local(id) => id,
956 PlaceBase::Upvar(var) => var.var_path.hir_id,
957 _ => continue,
958 };
959 if !self.locals.contains(&local_id) {
960 let capture = match capture.info.capture_kind {
961 UpvarCapture::ByValue => CaptureKind::Value,
962 UpvarCapture::ByUse => CaptureKind::Use,
963 UpvarCapture::ByRef(kind) => match kind {
964 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
965 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
966 CaptureKind::Ref(Mutability::Mut)
967 },
968 },
969 };
970 self.captures
971 .entry(local_id)
972 .and_modify(|e| *e |= capture)
973 .or_insert(capture);
974 }
975 }
976 },
977 ExprKind::Loop(b, ..) => {
978 self.loops.push(e.hir_id);
979 self.visit_block(b);
980 self.loops.pop();
981 },
982 _ => {
983 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
984 walk_expr(self, e);
985 },
986 }
987 }
988
989 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
990 p.each_binding_or_first(&mut |_, id, _, _| {
991 self.locals.insert(id);
992 });
993 }
994 }
995
996 let mut v = V {
997 cx,
998 loops: Vec::new(),
999 locals: HirIdSet::default(),
1000 allow_closure: true,
1001 captures: HirIdMap::default(),
1002 };
1003 v.visit_expr(expr);
1004 v.allow_closure.then_some(v.captures)
1005}
1006
1007pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1009
1010pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1013 let mut method_names = Vec::with_capacity(max_depth);
1014 let mut arg_lists = Vec::with_capacity(max_depth);
1015 let mut spans = Vec::with_capacity(max_depth);
1016
1017 let mut current = expr;
1018 for _ in 0..max_depth {
1019 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1020 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1021 break;
1022 }
1023 method_names.push(path.ident.name);
1024 arg_lists.push((*receiver, &**args));
1025 spans.push(path.ident.span);
1026 current = receiver;
1027 } else {
1028 break;
1029 }
1030 }
1031
1032 (method_names, arg_lists, spans)
1033}
1034
1035pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1042 let mut current = expr;
1043 let mut matched = Vec::with_capacity(methods.len());
1044 for method_name in methods.iter().rev() {
1045 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1047 if path.ident.name == *method_name {
1048 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1049 return None;
1050 }
1051 matched.push((receiver, args)); current = receiver; } else {
1054 return None;
1055 }
1056 } else {
1057 return None;
1058 }
1059 }
1060 matched.reverse();
1062 Some(matched)
1063}
1064
1065pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1067 cx.tcx
1068 .entry_fn(())
1069 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1070}
1071
1072pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1074 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1075 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1076}
1077
1078pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1080 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1081 match cx.tcx.hir_node_by_def_id(parent_id) {
1082 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1083 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1084 _ => None,
1085 }
1086}
1087
1088pub struct ContainsName<'a, 'tcx> {
1089 pub cx: &'a LateContext<'tcx>,
1090 pub name: Symbol,
1091}
1092
1093impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1094 type Result = ControlFlow<()>;
1095 type NestedFilter = nested_filter::OnlyBodies;
1096
1097 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1098 if self.name == name {
1099 ControlFlow::Break(())
1100 } else {
1101 ControlFlow::Continue(())
1102 }
1103 }
1104
1105 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1106 self.cx.tcx
1107 }
1108}
1109
1110pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1112 let mut cn = ContainsName { cx, name };
1113 cn.visit_expr(expr).is_break()
1114}
1115
1116pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1118 for_each_expr_without_closures(expr, |e| {
1119 if matches!(e.kind, ExprKind::Ret(..)) {
1120 ControlFlow::Break(())
1121 } else {
1122 ControlFlow::Continue(())
1123 }
1124 })
1125 .is_some()
1126}
1127
1128pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1130 get_parent_expr_for_hir(cx, e.hir_id)
1131}
1132
1133pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1136 match cx.tcx.parent_hir_node(hir_id) {
1137 Node::Expr(parent) => Some(parent),
1138 _ => None,
1139 }
1140}
1141
1142pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1144 let enclosing_node = cx
1145 .tcx
1146 .hir_get_enclosing_scope(hir_id)
1147 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1148 enclosing_node.and_then(|node| match node {
1149 Node::Block(block) => Some(block),
1150 Node::Item(&Item {
1151 kind: ItemKind::Fn { body: eid, .. },
1152 ..
1153 })
1154 | Node::ImplItem(&ImplItem {
1155 kind: ImplItemKind::Fn(_, eid),
1156 ..
1157 })
1158 | Node::TraitItem(&TraitItem {
1159 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1160 ..
1161 }) => match cx.tcx.hir_body(eid).value.kind {
1162 ExprKind::Block(block, _) => Some(block),
1163 _ => None,
1164 },
1165 _ => None,
1166 })
1167}
1168
1169pub fn get_enclosing_closure<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Closure<'tcx>> {
1171 cx.tcx.hir_parent_iter(hir_id).find_map(|(_, node)| {
1172 if let Node::Expr(expr) = node
1173 && let ExprKind::Closure(closure) = expr.kind
1174 {
1175 Some(closure)
1176 } else {
1177 None
1178 }
1179 })
1180}
1181
1182pub fn is_upvar_in_closure(cx: &LateContext<'_>, closure: &Closure<'_>, local_id: HirId) -> bool {
1184 cx.typeck_results()
1185 .closure_min_captures
1186 .get(&closure.def_id)
1187 .is_some_and(|x| x.contains_key(&local_id))
1188}
1189
1190pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1192 cx: &LateContext<'tcx>,
1193 expr: &Expr<'_>,
1194) -> Option<&'tcx Expr<'tcx>> {
1195 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1196 match node {
1197 Node::Expr(e) => match e.kind {
1198 ExprKind::Closure { .. }
1199 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1200 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1201
1202 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1204 _ => (),
1205 },
1206 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1207 _ => break,
1208 }
1209 }
1210 None
1211}
1212
1213pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1215 match tcx.hir_parent_iter(id).next() {
1216 Some((
1217 _,
1218 Node::Item(Item {
1219 kind: ItemKind::Impl(imp),
1220 ..
1221 }),
1222 )) => Some(imp),
1223 _ => None,
1224 }
1225}
1226
1227pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1238 while let ExprKind::Block(
1239 Block {
1240 stmts: [],
1241 expr: Some(inner),
1242 rules: BlockCheckMode::DefaultBlock,
1243 ..
1244 },
1245 _,
1246 ) = expr.kind
1247 {
1248 expr = inner;
1249 }
1250 expr
1251}
1252
1253pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1264 while let ExprKind::Block(
1265 Block {
1266 stmts: [],
1267 expr: Some(inner),
1268 rules: BlockCheckMode::DefaultBlock,
1269 ..
1270 }
1271 | Block {
1272 stmts:
1273 [
1274 Stmt {
1275 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1276 ..
1277 },
1278 ],
1279 expr: None,
1280 rules: BlockCheckMode::DefaultBlock,
1281 ..
1282 },
1283 _,
1284 ) = expr.kind
1285 {
1286 expr = inner;
1287 }
1288 expr
1289}
1290
1291pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1293 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1294 match iter.next() {
1295 Some((
1296 _,
1297 Node::Expr(Expr {
1298 kind: ExprKind::If(_, _, Some(else_expr)),
1299 ..
1300 }),
1301 )) => else_expr.hir_id == expr.hir_id,
1302 _ => false,
1303 }
1304}
1305
1306pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1309 hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {
1310 matches!(
1311 node,
1312 Node::LetStmt(LetStmt {
1313 init: Some(init),
1314 els: Some(els),
1315 ..
1316 })
1317 if init.hir_id == child_id || els.hir_id == child_id
1318 )
1319 })
1320}
1321
1322pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1324 hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {
1325 matches!(
1326 node,
1327 Node::LetStmt(LetStmt { els: Some(els), .. })
1328 if els.hir_id == child_id
1329 )
1330 })
1331}
1332
1333pub fn is_full_collection_range(cx: &LateContext<'_>, container: Option<HirId>, expr: &Expr<'_>) -> bool {
1335 if let Some(Range { start, end, ty, .. }) = Range::hir(cx, expr) {
1336 start.is_none_or(|start| is_integer_literal(start, 0))
1337 && end.is_none_or(|end| {
1338 if ty.limits() == RangeLimits::HalfOpen
1339 && let Some(container) = container
1340 && let ExprKind::MethodCall(seg, recv, [], _) = end.kind
1341 {
1342 seg.ident.name == sym::len && recv.res_local_id() == Some(container)
1343 } else {
1344 false
1345 }
1346 })
1347 } else {
1348 false
1349 }
1350}
1351
1352pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1354 if let ExprKind::Lit(spanned) = expr.kind
1355 && let LitKind::Int(v, _) = spanned.node
1356 {
1357 return v == value;
1358 }
1359 false
1360}
1361
1362pub fn is_integer_literal_untyped(expr: &Expr<'_>) -> bool {
1364 if let ExprKind::Lit(spanned) = expr.kind
1365 && let LitKind::Int(_, suffix) = spanned.node
1366 {
1367 return suffix == LitIntType::Unsuffixed;
1368 }
1369
1370 false
1371}
1372
1373pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1375 if let ExprKind::Lit(spanned) = expr.kind
1376 && let LitKind::Float(v, _) = spanned.node
1377 {
1378 v.as_str().parse() == Ok(value)
1379 } else {
1380 false
1381 }
1382}
1383
1384pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1392 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1393}
1394
1395#[must_use]
1399pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1400 loop {
1401 if span.from_expansion() {
1402 let data = span.ctxt().outer_expn_data();
1403 let new_span = data.call_site;
1404
1405 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1406 && mac_name == name
1407 {
1408 return Some(new_span);
1409 }
1410
1411 span = new_span;
1412 } else {
1413 return None;
1414 }
1415 }
1416}
1417
1418#[must_use]
1429pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1430 if span.from_expansion() {
1431 let data = span.ctxt().outer_expn_data();
1432 let new_span = data.call_site;
1433
1434 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1435 && mac_name == name
1436 {
1437 return Some(new_span);
1438 }
1439 }
1440
1441 None
1442}
1443
1444pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1446 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().skip_norm_wip().output();
1447 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1448}
1449
1450pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1452 let arg = cx
1453 .tcx
1454 .fn_sig(fn_def_id)
1455 .instantiate_identity()
1456 .skip_norm_wip()
1457 .input(nth);
1458 cx.tcx.instantiate_bound_regions_with_erased(arg)
1459}
1460
1461pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1463 if let ExprKind::Call(fun, _) = expr.kind
1464 && let ExprKind::Path(ref qp) = fun.kind
1465 {
1466 let res = cx.qpath_res(qp, fun.hir_id);
1467 return match res {
1468 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1469 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1470 _ => false,
1471 };
1472 }
1473 false
1474}
1475
1476pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1479 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1480 !matches!(
1481 cx.qpath_res(qpath, id),
1482 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1483 )
1484 }
1485
1486 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1487 i.into_iter().any(|pat| is_refutable(cx, pat))
1488 }
1489
1490 match pat.kind {
1491 PatKind::Missing => unreachable!(),
1492 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1494 PatKind::Ref(pat, _, _) => is_refutable(cx, pat),
1495 PatKind::Expr(PatExpr {
1496 kind: PatExprKind::Path(qpath),
1497 hir_id,
1498 ..
1499 }) => is_qpath_refutable(cx, qpath, *hir_id),
1500 PatKind::Or(pats) => {
1501 are_refutable(cx, pats)
1503 },
1504 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1505 PatKind::Struct(ref qpath, fields, _) => {
1506 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1507 },
1508 PatKind::TupleStruct(ref qpath, pats, _) => {
1509 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1510 },
1511 PatKind::Slice(head, middle, tail) => {
1512 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1513 rustc_ty::Slice(..) => {
1514 !head.is_empty() || middle.is_none() || !tail.is_empty()
1516 },
1517 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1518 _ => {
1519 true
1521 },
1522 }
1523 },
1524 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1525 }
1526}
1527
1528pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1531 if let PatKind::Or(pats) = pat.kind {
1532 pats.iter().for_each(f);
1533 } else {
1534 f(pat);
1535 }
1536}
1537
1538pub fn is_self(slf: &Param<'_>) -> bool {
1539 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1540 name.name == kw::SelfLower
1541 } else {
1542 false
1543 }
1544}
1545
1546pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1547 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1548 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1549 {
1550 return true;
1551 }
1552 false
1553}
1554
1555pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1556 (0..decl.inputs.len()).map(move |i| &body.params[i])
1557}
1558
1559pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1562 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1563 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1564 && ddpos.as_opt_usize().is_none()
1565 && cx
1566 .qpath_res(path, arm.pat.hir_id)
1567 .ctor_parent(cx)
1568 .is_lang_item(cx, ResultOk)
1569 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1570 && arm.body.res_local_id() == Some(hir_id)
1571 {
1572 return true;
1573 }
1574 false
1575 }
1576
1577 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1578 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1579 cx.qpath_res(path, arm.pat.hir_id)
1580 .ctor_parent(cx)
1581 .is_lang_item(cx, ResultErr)
1582 } else {
1583 false
1584 }
1585 }
1586
1587 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1588 if let MatchSource::TryDesugar(_) = *source {
1590 return Some(expr);
1591 }
1592
1593 if arms.len() == 2
1594 && arms[0].guard.is_none()
1595 && arms[1].guard.is_none()
1596 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1597 {
1598 return Some(expr);
1599 }
1600 }
1601
1602 None
1603}
1604
1605pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1615 let mut suppress_lint = false;
1616
1617 for id in ids {
1618 let level_spec = cx.tcx.lint_level_spec_at_node(lint, id);
1619 if let Some(expectation) = level_spec.lint_id() {
1620 cx.fulfill_expectation(expectation);
1621 }
1622
1623 match level_spec.level() {
1624 Level::Allow | Level::Expect => suppress_lint = true,
1625 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1626 }
1627 }
1628
1629 suppress_lint
1630}
1631
1632pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1640 cx.tcx.lint_level_spec_at_node(lint, id).is_allow()
1641}
1642
1643pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1644 while let PatKind::Ref(subpat, _, _) = pat.kind {
1645 pat = subpat;
1646 }
1647 pat
1648}
1649
1650pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1651 Integer::from_int_ty(&tcx, ity).size().bits()
1652}
1653
1654#[expect(clippy::cast_possible_wrap)]
1655pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1657 let amt = 128 - int_bits(tcx, ity);
1658 ((u as i128) << amt) >> amt
1659}
1660
1661#[expect(clippy::cast_sign_loss)]
1662pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1664 let amt = 128 - int_bits(tcx, ity);
1665 ((u as u128) << amt) >> amt
1666}
1667
1668pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1670 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1671 let amt = 128 - bits;
1672 (u << amt) >> amt
1673}
1674
1675pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1676 attrs.iter().any(|attr| attr.has_name(symbol))
1677}
1678
1679pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1680 find_attr!(cx.tcx, hir_id, Repr { .. })
1681}
1682
1683pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1684 let mut prev_enclosing_node = None;
1685 let mut enclosing_node = node;
1686 while Some(enclosing_node) != prev_enclosing_node {
1687 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1688 return true;
1689 }
1690 prev_enclosing_node = Some(enclosing_node);
1691 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1692 }
1693
1694 false
1695}
1696
1697pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1700 tcx.hir_parent_owner_iter(id)
1701 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1702 .any(|(id, _)| find_attr!(tcx, id.def_id, AutomaticallyDerived))
1703}
1704
1705pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1707 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1711}
1712
1713pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1718 let mut conds = Vec::new();
1719 let mut blocks: Vec<&Block<'_>> = Vec::new();
1720
1721 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1722 conds.push(cond);
1723 if let ExprKind::Block(block, _) = then.kind {
1724 blocks.push(block);
1725 } else {
1726 panic!("ExprKind::If node is not an ExprKind::Block");
1727 }
1728
1729 if let Some(else_expr) = r#else {
1730 expr = else_expr;
1731 } else {
1732 break;
1733 }
1734 }
1735
1736 if !blocks.is_empty()
1738 && let ExprKind::Block(block, _) = expr.kind
1739 {
1740 blocks.push(block);
1741 }
1742
1743 (conds, blocks)
1744}
1745
1746pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1748 if let ExprKind::Closure(&Closure {
1749 body,
1750 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1751 ..
1752 }) = expr.kind
1753 && let ExprKind::Block(
1754 Block {
1755 expr:
1756 Some(Expr {
1757 kind: ExprKind::DropTemps(inner_expr),
1758 ..
1759 }),
1760 ..
1761 },
1762 _,
1763 ) = tcx.hir_body(body).value.kind
1764 {
1765 Some(inner_expr)
1766 } else {
1767 None
1768 }
1769}
1770
1771pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1773 get_async_closure_expr(tcx, body.value)
1774}
1775
1776pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1778 let did = match expr.kind {
1779 ExprKind::Call(path, _) => {
1780 if let ExprKind::Path(ref qpath) = path.kind
1781 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1782 {
1783 Some(did)
1784 } else {
1785 None
1786 }
1787 },
1788 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1789 _ => None,
1790 };
1791
1792 did.is_some_and(|did| find_attr!(cx.tcx, did, MustUse { .. }))
1793}
1794
1795fn is_body_identity_function<'hir>(cx: &LateContext<'_>, func: &Body<'hir>) -> bool {
1809 let [param] = func.params else {
1810 return false;
1811 };
1812
1813 let mut param_pat = param.pat;
1814
1815 let mut advance_param_pat_over_stmts = |stmts: &[Stmt<'hir>]| {
1822 for stmt in stmts {
1823 if let StmtKind::Let(local) = stmt.kind
1824 && let Some(init) = local.init
1825 && is_expr_identity_of_pat(cx, param_pat, init, true)
1826 {
1827 param_pat = local.pat;
1828 } else {
1829 return false;
1830 }
1831 }
1832
1833 true
1834 };
1835
1836 let mut expr = func.value;
1837 loop {
1838 match expr.kind {
1839 ExprKind::Block(
1840 &Block {
1841 stmts: [],
1842 expr: Some(e),
1843 ..
1844 },
1845 _,
1846 )
1847 | ExprKind::Ret(Some(e)) => expr = e,
1848 ExprKind::Block(
1849 &Block {
1850 stmts: [stmt],
1851 expr: None,
1852 ..
1853 },
1854 _,
1855 ) => {
1856 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1857 && let ExprKind::Ret(Some(ret_val)) = e.kind
1858 {
1859 expr = ret_val;
1860 } else {
1861 return false;
1862 }
1863 },
1864 ExprKind::Block(
1865 &Block {
1866 stmts, expr: Some(e), ..
1867 },
1868 _,
1869 ) => {
1870 if !advance_param_pat_over_stmts(stmts) {
1871 return false;
1872 }
1873
1874 expr = e;
1875 },
1876 ExprKind::Block(&Block { stmts, expr: None, .. }, _) => {
1877 if let Some((last_stmt, stmts)) = stmts.split_last()
1878 && advance_param_pat_over_stmts(stmts)
1879 && let StmtKind::Semi(e) | StmtKind::Expr(e) = last_stmt.kind
1880 && let ExprKind::Ret(Some(ret_val)) = e.kind
1881 {
1882 expr = ret_val;
1883 } else {
1884 return false;
1885 }
1886 },
1887 _ => return is_expr_identity_of_pat(cx, param_pat, expr, true),
1888 }
1889 }
1890}
1891
1892pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1902 if cx
1903 .typeck_results()
1904 .pat_binding_modes()
1905 .get(pat.hir_id)
1906 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..)))
1907 {
1908 return false;
1912 }
1913
1914 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1916
1917 match (pat.kind, expr.kind) {
1918 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1919 expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1920 },
1921 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1922 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1923 },
1924 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1925 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1926 {
1927 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1928 },
1929 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1930 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1931 },
1932 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1933 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1934 {
1935 if let ExprKind::Path(ident) = &ident.kind
1937 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1938 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
1940 {
1941 true
1942 } else {
1943 false
1944 }
1945 },
1946 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
1947 if field_pats.len() == fields.len() =>
1948 {
1949 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1951 && unordered_over(field_pats, fields, |field_pat, field| {
1953 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
1954 })
1955 },
1956 _ => false,
1957 }
1958}
1959
1960pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1965 match expr.kind {
1966 ExprKind::Closure(&Closure { body, fn_decl, .. })
1967 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1968 {
1969 is_body_identity_function(cx, cx.tcx.hir_body(body))
1970 },
1971 ExprKind::Path(QPath::Resolved(_, path))
1972 if path.segments.iter().all(|seg| seg.infer_args)
1973 && let Some(did) = path.res.opt_def_id() =>
1974 {
1975 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1976 },
1977 _ => false,
1978 }
1979}
1980
1981pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1990 match expr.kind {
1991 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1992 _ => expr.basic_res().is_diag_item(cx, sym::convert_identity),
1993 }
1994}
1995
1996pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
1999 for (node, child_id) in hir_parent_with_src_iter(tcx, expr.hir_id) {
2000 match node {
2001 Node::Block(_) => {},
2002 Node::Arm(arm) if arm.body.hir_id == child_id => {},
2003 Node::Expr(expr) => match expr.kind {
2004 ExprKind::Block(..) | ExprKind::DropTemps(_) => {},
2005 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => {},
2006 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => return None,
2007 _ => return Some((Node::Expr(expr), child_id)),
2008 },
2009 node => return Some((node, child_id)),
2010 }
2011 }
2012 None
2013}
2014
2015pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2017 !matches!(
2018 get_expr_use_or_unification_node(tcx, expr),
2019 None | Some((
2020 Node::Stmt(Stmt {
2021 kind: StmtKind::Expr(_)
2022 | StmtKind::Semi(_)
2023 | StmtKind::Let(LetStmt {
2024 pat: Pat {
2025 kind: PatKind::Wild,
2026 ..
2027 },
2028 ..
2029 }),
2030 ..
2031 }),
2032 _
2033 ))
2034 )
2035}
2036
2037pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2039 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2040}
2041
2042pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2046 !expr.is_place_expr(|base| {
2047 cx.typeck_results()
2048 .adjustments()
2049 .get(base.hir_id)
2050 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2051 })
2052}
2053
2054pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2055 if is_no_core_crate(cx) {
2056 None
2057 } else if is_no_std_crate(cx) {
2058 Some("core")
2059 } else {
2060 Some("std")
2061 }
2062}
2063
2064pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2065 find_attr!(cx.tcx, crate, NoStd)
2066}
2067
2068pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2069 find_attr!(cx.tcx, crate, NoCore)
2070}
2071
2072pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2082 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2083 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2084 } else {
2085 false
2086 }
2087}
2088
2089pub fn fn_has_unsatisfiable_clauses(cx: &LateContext<'_>, did: DefId) -> bool {
2099 use rustc_trait_selection::traits;
2100 let clauses = cx
2101 .tcx
2102 .clauses_of(did)
2103 .clauses
2104 .iter()
2105 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2106 traits::impossible_clauses(cx.tcx, traits::elaborate(cx.tcx, clauses).collect::<Vec<_>>())
2107}
2108
2109pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2111 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2112}
2113
2114pub fn fn_def_id_with_node_args<'tcx>(
2117 cx: &LateContext<'tcx>,
2118 expr: &Expr<'_>,
2119) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2120 let typeck = cx.typeck_results();
2121 match &expr.kind {
2122 ExprKind::MethodCall(..) => Some((
2123 typeck.type_dependent_def_id(expr.hir_id)?,
2124 typeck.node_args(expr.hir_id),
2125 )),
2126 ExprKind::Call(
2127 Expr {
2128 kind: ExprKind::Path(qpath),
2129 hir_id: path_hir_id,
2130 ..
2131 },
2132 ..,
2133 ) => {
2134 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2137 typeck.qpath_res(qpath, *path_hir_id)
2138 {
2139 Some((id, typeck.node_args(*path_hir_id)))
2140 } else {
2141 None
2142 }
2143 },
2144 _ => None,
2145 }
2146}
2147
2148pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2153 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2154 let expr_kind = expr_type.kind();
2155 let is_primitive = match expr_kind {
2156 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2157 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2158 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2159 is_recursively_primitive_type(*element_type)
2160 } else {
2161 unreachable!()
2162 }
2163 },
2164 _ => false,
2165 };
2166
2167 if is_primitive {
2168 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2171 rustc_ty::Slice(..) => return Some("slice".into()),
2172 rustc_ty::Array(..) => return Some("array".into()),
2173 rustc_ty::Tuple(..) => return Some("tuple".into()),
2174 _ => {
2175 let refs_peeled = expr_type.peel_refs();
2178 return Some(refs_peeled.walk().last().unwrap().to_string());
2179 },
2180 }
2181 }
2182 None
2183}
2184
2185pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2193where
2194 Hash: FnMut(&T) -> u64,
2195 Eq: FnMut(&T, &T) -> bool,
2196{
2197 match exprs {
2198 [a, b] if eq(a, b) => return vec![vec![a, b]],
2199 _ if exprs.len() <= 2 => return vec![],
2200 _ => {},
2201 }
2202
2203 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2204
2205 for expr in exprs {
2206 match buckets.entry(hash(expr)) {
2207 indexmap::map::Entry::Occupied(mut o) => {
2208 let bucket = o.get_mut();
2209 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2210 Some(group) => group.push(expr),
2211 None => bucket.push(vec![expr]),
2212 }
2213 },
2214 indexmap::map::Entry::Vacant(v) => {
2215 v.insert(vec![vec![expr]]);
2216 },
2217 }
2218 }
2219
2220 buckets
2221 .into_values()
2222 .flatten()
2223 .filter(|group| group.len() > 1)
2224 .collect()
2225}
2226
2227pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2230 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2231 if let PatKind::Ref(pat, _, _) = pat.kind {
2232 peel(pat, count + 1)
2233 } else {
2234 (pat, count)
2235 }
2236 }
2237 peel(pat, 0)
2238}
2239
2240pub fn peel_hir_expr_while<'tcx>(
2242 mut expr: &'tcx Expr<'tcx>,
2243 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2244) -> &'tcx Expr<'tcx> {
2245 while let Some(e) = f(expr) {
2246 expr = e;
2247 }
2248 expr
2249}
2250
2251pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2254 let mut remaining = count;
2255 let e = peel_hir_expr_while(expr, |e| match e.kind {
2256 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2257 remaining -= 1;
2258 Some(e)
2259 },
2260 _ => None,
2261 });
2262 (e, count - remaining)
2263}
2264
2265pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2268 let mut count: usize = 0;
2269 let mut curr_expr = expr;
2270 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2271 count = count.wrapping_add(1);
2272 curr_expr = local_expr;
2273 }
2274 (curr_expr, count)
2275}
2276
2277pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2280 let mut count = 0;
2281 let e = peel_hir_expr_while(expr, |e| match e.kind {
2282 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2283 count += 1;
2284 Some(e)
2285 },
2286 _ => None,
2287 });
2288 (e, count)
2289}
2290
2291pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2294 let mut count = 0;
2295 loop {
2296 match &ty.kind {
2297 TyKind::Ref(_, ref_ty) => {
2298 ty = ref_ty.ty;
2299 count += 1;
2300 },
2301 _ => break (ty, count),
2302 }
2303 }
2304}
2305
2306pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
2308 match &ty.kind {
2309 TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty),
2310 _ => ty,
2311 }
2312}
2313
2314pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2317 loop {
2318 match expr.kind {
2319 ExprKind::AddrOf(_, _, e) => expr = e,
2320 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2321 _ => break,
2322 }
2323 }
2324 expr
2325}
2326
2327pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2330 let mut operators = Vec::new();
2331 peel_hir_expr_while(expr, |expr| match expr.kind {
2332 ExprKind::AddrOf(_, _, e) => {
2333 operators.push(expr);
2334 Some(e)
2335 },
2336 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2337 operators.push(expr);
2338 Some(e)
2339 },
2340 _ => None,
2341 });
2342 operators
2343}
2344
2345pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2346 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2347 && let Res::Def(_, def_id) = path.res
2348 {
2349 return find_attr!(cx.tcx, def_id, CfgTrace(..) | CfgAttrTrace(..));
2350 }
2351 false
2352}
2353
2354static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModId, Vec<Symbol>>>> = OnceLock::new();
2355
2356fn test_item_names(tcx: TyCtxt<'_>, module: LocalModId) -> Vec<Symbol> {
2359 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2360 let mut map = cache.lock().unwrap();
2361 match map.entry(module) {
2362 Entry::Occupied(entry) => entry.get().clone(),
2363 Entry::Vacant(entry) => {
2364 let mut names = Vec::new();
2365 for id in tcx.hir_module_free_items(module) {
2366 if matches!(tcx.def_kind(id.owner_id), DefKind::Static { .. })
2367 && let item = tcx.hir_item(id)
2368 && let ItemKind::Static(_mut, ident, ty, _body) = item.kind
2369 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2370 && let Res::Def(DefKind::Struct, _) = path.res
2372 && find_attr!(tcx, item.hir_id(), RustcTestMarker(..))
2373 {
2374 names.push(ident.name);
2375 }
2376 }
2377 names.sort_unstable();
2378 entry.insert(names).clone()
2379 },
2380 }
2381}
2382
2383pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2387 let names = test_item_names(tcx, tcx.parent_module(id));
2388 if names.is_empty() {
2390 return false;
2391 }
2392 once((id, tcx.hir_node(id)))
2393 .chain(tcx.hir_parent_iter(id))
2394 .any(|(_id, node)| {
2397 if let Node::Item(item) = node
2398 && let ItemKind::Fn { ident, .. } = item.kind
2399 {
2400 return names.binary_search(&ident.name).is_ok();
2403 }
2404 false
2405 })
2406}
2407
2408pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2415 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2416 if let Node::Item(item) = tcx.hir_node(id)
2417 && let ItemKind::Fn { ident, .. } = item.kind
2418 {
2419 test_item_names(tcx, tcx.parent_module(id))
2420 .binary_search(&ident.name)
2421 .is_ok()
2422 } else {
2423 false
2424 }
2425}
2426
2427pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2432 if let Some(cfgs) = find_attr!(tcx, id, CfgTrace(cfgs) => cfgs)
2433 && cfgs
2434 .iter()
2435 .any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. }))
2436 {
2437 true
2438 } else {
2439 false
2440 }
2441}
2442
2443pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2445 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2446}
2447
2448pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2450 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id) || is_in_integration_test_file(tcx)
2451}
2452
2453fn is_in_integration_test_file(tcx: TyCtxt<'_>) -> bool {
2455 if let Input::File(ref path) = tcx.sess.io.input
2456 && !tcx.sess.opts.unstable_opts.ui_testing
2457 {
2458 path.starts_with("tests")
2459 } else {
2460 false
2461 }
2462}
2463
2464pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2466 find_attr!(tcx, def_id, CfgTrace(..))
2467 || find_attr!(
2468 tcx.hir_parent_id_iter(tcx.local_def_id_to_hir_id(def_id))
2469 .flat_map(|parent_id| tcx.hir_attrs(parent_id)),
2470 CfgTrace(..)
2471 )
2472}
2473
2474#[derive(Clone, Copy)]
2476pub enum DefinedTy<'tcx> {
2477 Hir(&'tcx hir::Ty<'tcx>),
2479 Mir {
2487 def_site_def_id: Option<DefId>,
2488 ty: Binder<'tcx, Ty<'tcx>>,
2489 },
2490}
2491
2492pub struct ExprUseSite<'tcx> {
2494 pub node: Node<'tcx>,
2496 pub child_id: HirId,
2498 pub adjustments: &'tcx [Adjustment<'tcx>],
2500 pub is_ty_unified: bool,
2502 pub moved_before_use: bool,
2504 pub same_ctxt: bool,
2506}
2507impl<'tcx> ExprUseSite<'tcx> {
2508 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2509 match self.node {
2510 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2511 Node::ExprField(field) => ExprUseNode::Field(field),
2512
2513 Node::Item(&Item {
2514 kind: ItemKind::Static(..) | ItemKind::Const(..),
2515 owner_id,
2516 ..
2517 })
2518 | Node::TraitItem(&TraitItem {
2519 kind: TraitItemKind::Const(..),
2520 owner_id,
2521 ..
2522 })
2523 | Node::ImplItem(&ImplItem {
2524 kind: ImplItemKind::Const(..),
2525 owner_id,
2526 ..
2527 }) => ExprUseNode::ConstStatic(owner_id),
2528
2529 Node::Item(&Item {
2530 kind: ItemKind::Fn { .. },
2531 owner_id,
2532 ..
2533 })
2534 | Node::TraitItem(&TraitItem {
2535 kind: TraitItemKind::Fn(..),
2536 owner_id,
2537 ..
2538 })
2539 | Node::ImplItem(&ImplItem {
2540 kind: ImplItemKind::Fn(..),
2541 owner_id,
2542 ..
2543 }) => ExprUseNode::Return(owner_id),
2544
2545 Node::Expr(use_expr) => match use_expr.kind {
2546 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2547 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2548 }),
2549
2550 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2551 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2552 Some(i) => ExprUseNode::FnArg(func, i),
2553 None => ExprUseNode::Callee,
2554 },
2555 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2556 use_expr.hir_id,
2557 name.args,
2558 args.iter()
2559 .position(|arg| arg.hir_id == self.child_id)
2560 .map_or(0, |i| i + 1),
2561 ),
2562 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2563 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2564 _ => ExprUseNode::Other,
2565 },
2566 _ => ExprUseNode::Other,
2567 }
2568 }
2569}
2570
2571pub enum ExprUseNode<'tcx> {
2573 LetStmt(&'tcx LetStmt<'tcx>),
2575 ConstStatic(OwnerId),
2577 Return(OwnerId),
2579 Field(&'tcx ExprField<'tcx>),
2581 FnArg(&'tcx Expr<'tcx>, usize),
2583 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2585 Callee,
2587 FieldAccess(Ident),
2589 AddrOf(ast::BorrowKind, Mutability),
2591 Other,
2592}
2593impl<'tcx> ExprUseNode<'tcx> {
2594 pub fn is_return(&self) -> bool {
2596 matches!(self, Self::Return(_))
2597 }
2598
2599 pub fn is_recv(&self) -> bool {
2601 matches!(self, Self::MethodArg(_, _, 0))
2602 }
2603
2604 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2606 match *self {
2607 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2608 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2609 def_site_def_id: Some(id.def_id.to_def_id()),
2610 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity().skip_norm_wip()),
2611 }),
2612 Self::Return(id) => {
2613 if let Node::Expr(Expr {
2614 kind: ExprKind::Closure(c),
2615 ..
2616 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2617 {
2618 match c.fn_decl.output {
2619 FnRetTy::DefaultReturn(_) => None,
2620 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2621 }
2622 } else {
2623 let ty = cx.tcx.fn_sig(id).instantiate_identity().skip_norm_wip().output();
2624 Some(DefinedTy::Mir {
2625 def_site_def_id: Some(id.def_id.to_def_id()),
2626 ty,
2627 })
2628 }
2629 },
2630 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2631 Some(Expr {
2632 hir_id,
2633 kind: ExprKind::Struct(path, ..),
2634 ..
2635 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2636 .and_then(|(adt, variant)| {
2637 variant
2638 .fields
2639 .iter()
2640 .find(|f| f.name == field.ident.name)
2641 .map(|f| (adt, f))
2642 })
2643 .map(|(adt, field_def)| DefinedTy::Mir {
2644 def_site_def_id: Some(adt.did()),
2645 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity().skip_norm_wip()),
2646 }),
2647 _ => None,
2648 },
2649 Self::FnArg(callee, i) => {
2650 let sig = expr_sig(cx, callee)?;
2651 let (hir_ty, ty) = sig.input_with_hir(i)?;
2652 Some(match hir_ty {
2653 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2654 None => DefinedTy::Mir {
2655 def_site_def_id: sig.predicates_id(),
2656 ty,
2657 },
2658 })
2659 },
2660 Self::MethodArg(id, _, i) => {
2661 let id = cx.typeck_results().type_dependent_def_id(id)?;
2662 let sig = cx.tcx.fn_sig(id).skip_binder();
2663 Some(DefinedTy::Mir {
2664 def_site_def_id: Some(id),
2665 ty: sig.input(i),
2666 })
2667 },
2668 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2669 }
2670 }
2671}
2672
2673struct ReplacingFilterMap<I, F>(I, F);
2674impl<I, F, U> Iterator for ReplacingFilterMap<I, F>
2675where
2676 I: Iterator,
2677 F: FnMut(&mut I, I::Item) -> Option<U>,
2678{
2679 type Item = U;
2680 fn next(&mut self) -> Option<U> {
2681 while let Some(x) = self.0.next() {
2682 if let Some(x) = (self.1)(&mut self.0, x) {
2683 return Some(x);
2684 }
2685 }
2686 None
2687 }
2688}
2689
2690#[expect(clippy::too_many_lines)]
2693pub fn expr_use_sites<'tcx>(
2694 tcx: TyCtxt<'tcx>,
2695 typeck: &'tcx TypeckResults<'tcx>,
2696 mut ctxt: SyntaxContext,
2697 e: &'tcx Expr<'tcx>,
2698) -> impl Iterator<Item = ExprUseSite<'tcx>> {
2699 let mut adjustments: &[_] = typeck.expr_adjustments(e);
2700 let mut is_ty_unified = false;
2701 let mut moved_before_use = false;
2702 let mut same_ctxt = true;
2703 ReplacingFilterMap(
2704 hir_parent_with_src_iter(tcx, e.hir_id),
2705 move |iter: &mut _, (parent, child_id)| {
2706 let parent_ctxt;
2707 let mut parent_adjustments: &[_] = &[];
2708 match parent {
2709 Node::Expr(parent_expr) => {
2710 parent_ctxt = parent_expr.span.ctxt();
2711 same_ctxt &= parent_ctxt == ctxt;
2712 parent_adjustments = typeck.expr_adjustments(parent_expr);
2713 match parent_expr.kind {
2714 ExprKind::Match(scrutinee, arms, _) if scrutinee.hir_id != child_id => {
2715 is_ty_unified |= arms.len() != 1;
2716 moved_before_use = true;
2717 if adjustments.is_empty() {
2718 adjustments = parent_adjustments;
2719 }
2720 return None;
2721 },
2722 ExprKind::If(cond, _, else_) if cond.hir_id != child_id => {
2723 is_ty_unified |= else_.is_some();
2724 moved_before_use = true;
2725 if adjustments.is_empty() {
2726 adjustments = parent_adjustments;
2727 }
2728 return None;
2729 },
2730 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2731 is_ty_unified = true;
2732 moved_before_use = true;
2733 *iter = hir_parent_with_src_iter(tcx, id);
2734 if adjustments.is_empty() {
2735 adjustments = parent_adjustments;
2736 }
2737 return None;
2738 },
2739 ExprKind::Block(b, _) => {
2740 is_ty_unified |= b.targeted_by_break;
2741 moved_before_use = true;
2742 if adjustments.is_empty() {
2743 adjustments = parent_adjustments;
2744 }
2745 return None;
2746 },
2747 ExprKind::DropTemps(_) | ExprKind::Type(..) => {
2748 if adjustments.is_empty() {
2749 adjustments = parent_adjustments;
2750 }
2751 return None;
2752 },
2753 _ => {},
2754 }
2755 },
2756 Node::Arm(arm) => {
2757 parent_ctxt = arm.span.ctxt();
2758 same_ctxt &= parent_ctxt == ctxt;
2759 if arm.body.hir_id == child_id {
2760 return None;
2761 }
2762 },
2763 Node::Block(b) => {
2764 same_ctxt &= b.span.ctxt() == ctxt;
2765 return None;
2766 },
2767 Node::ConstBlock(_) => parent_ctxt = ctxt,
2768 Node::ExprField(&ExprField { span, .. }) => {
2769 parent_ctxt = span.ctxt();
2770 same_ctxt &= parent_ctxt == ctxt;
2771 },
2772 Node::AnonConst(&AnonConst { span, .. })
2773 | Node::ConstArg(&ConstArg { span, .. })
2774 | Node::Field(&FieldDef { span, .. })
2775 | Node::ImplItem(&ImplItem { span, .. })
2776 | Node::Item(&Item { span, .. })
2777 | Node::LetStmt(&LetStmt { span, .. })
2778 | Node::Stmt(&Stmt { span, .. })
2779 | Node::TraitItem(&TraitItem { span, .. })
2780 | Node::Variant(&Variant { span, .. }) => {
2781 parent_ctxt = span.ctxt();
2782 same_ctxt &= parent_ctxt == ctxt;
2783 *iter = hir_parent_with_src_iter(tcx, CRATE_HIR_ID);
2784 },
2785 Node::AssocItemConstraint(_)
2786 | Node::ConstArgExprField(_)
2787 | Node::Crate(_)
2788 | Node::Ctor(_)
2789 | Node::Err(_)
2790 | Node::ForeignItem(_)
2791 | Node::GenericParam(_)
2792 | Node::Infer(_)
2793 | Node::Lifetime(_)
2794 | Node::OpaqueTy(_)
2795 | Node::Param(_)
2796 | Node::Pat(_)
2797 | Node::PatExpr(_)
2798 | Node::PatField(_)
2799 | Node::PathSegment(_)
2800 | Node::PreciseCapturingNonLifetimeArg(_)
2801 | Node::Synthetic
2802 | Node::TraitRef(_)
2803 | Node::Ty(_)
2804 | Node::TyPat(_)
2805 | Node::WherePredicate(_)
2806 | Node::TestBinderForall(_)
2807 | Node::TestBinderExists(_)
2808 | Node::TestBinderBoundTypeConstraint(_) => {
2809 debug_assert!(false, "found {parent:?} which is after the final use node");
2812 return None;
2813 },
2814 }
2815
2816 ctxt = parent_ctxt;
2817 Some(ExprUseSite {
2818 node: parent,
2819 child_id,
2820 adjustments: mem::replace(&mut adjustments, parent_adjustments),
2821 is_ty_unified: mem::replace(&mut is_ty_unified, false),
2822 moved_before_use: mem::replace(&mut moved_before_use, false),
2823 same_ctxt: mem::replace(&mut same_ctxt, true),
2824 })
2825 },
2826 )
2827}
2828
2829pub fn get_expr_use_site<'tcx>(
2830 tcx: TyCtxt<'tcx>,
2831 typeck: &'tcx TypeckResults<'tcx>,
2832 ctxt: SyntaxContext,
2833 e: &'tcx Expr<'tcx>,
2834) -> ExprUseSite<'tcx> {
2835 expr_use_sites(tcx, typeck, ctxt, e).next().unwrap_or_else(|| {
2838 debug_assert!(false, "failed to find a use site for expr {e:?}");
2839 ExprUseSite {
2840 node: Node::Synthetic, child_id: CRATE_HIR_ID,
2842 adjustments: &[],
2843 is_ty_unified: false,
2844 moved_before_use: false,
2845 same_ctxt: false,
2846 }
2847 })
2848}
2849
2850pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2852 let mut pos = 0;
2853 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2854 let end = pos + t.len;
2855 let range = pos as usize..end as usize;
2856 let inner = InnerSpan::new(range.start, range.end);
2857 pos = end;
2858 (t.kind, s.get(range).unwrap_or_default(), inner)
2859 })
2860}
2861
2862pub fn span_contains_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool {
2865 span.check_text(sm, |snippet| {
2866 tokenize(snippet, FrontmatterAllowed::No).any(|token| {
2867 matches!(
2868 token.kind,
2869 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2870 )
2871 })
2872 })
2873}
2874
2875pub fn span_contains_non_whitespace<'sm>(sm: impl HasSourceMap<'sm>, span: Span, skip_comments: bool) -> bool {
2880 span.check_text(sm, |snippet| {
2881 tokenize_with_text(snippet).any(|(token, _, _)| match token {
2882 TokenKind::Whitespace => false,
2883 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2884 _ => true,
2885 })
2886 })
2887}
2888
2889pub fn span_extract_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> String {
2893 span_extract_comments(sm, span).join("\n")
2894}
2895
2896pub fn span_extract_comments<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Vec<String> {
2900 span.with_source_text(sm, |snippet| {
2901 tokenize_with_text(snippet)
2902 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2903 .map(|(_, s, _)| s.to_string())
2904 .collect::<Vec<_>>()
2905 })
2906 .unwrap_or_default()
2907}
2908
2909pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2910 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2911}
2912
2913pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2938 cx: &LateContext<'_>,
2939 pat: &'a Pat<'hir>,
2940 else_body: &Expr<'_>,
2941) -> Option<&'a Pat<'hir>> {
2942 if let Some([inner_pat]) = as_some_pattern(cx, pat)
2943 && !is_refutable(cx, inner_pat)
2944 && let else_body = peel_blocks(else_body)
2945 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2946 && let ExprKind::Path(ret_path) = ret_val.kind
2947 && cx
2948 .qpath_res(&ret_path, ret_val.hir_id)
2949 .ctor_parent(cx)
2950 .is_lang_item(cx, OptionNone)
2951 {
2952 Some(inner_pat)
2953 } else {
2954 None
2955 }
2956}
2957
2958macro_rules! op_utils {
2959 ($($name:ident $assign:ident)*) => {
2960 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2962
2963 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2965
2966 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2968 match kind {
2969 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2970 _ => None,
2971 }
2972 }
2973 };
2974}
2975
2976op_utils! {
2977 Add AddAssign
2978 Sub SubAssign
2979 Mul MulAssign
2980 Div DivAssign
2981 Rem RemAssign
2982 BitXor BitXorAssign
2983 BitAnd BitAndAssign
2984 BitOr BitOrAssign
2985 Shl ShlAssign
2986 Shr ShrAssign
2987}
2988
2989pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2992 match *pat {
2993 PatKind::Wild => true,
2994 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2995 !visitors::is_local_used(cx, body, id)
2996 },
2997 _ => false,
2998 }
2999}
3000
3001#[derive(Clone, Copy)]
3002pub enum RequiresSemi {
3003 Yes,
3004 No,
3005}
3006impl RequiresSemi {
3007 pub fn requires_semi(self) -> bool {
3008 matches!(self, Self::Yes)
3009 }
3010}
3011
3012#[expect(clippy::too_many_lines)]
3015pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
3016 struct BreakTarget {
3017 id: HirId,
3018 unused: bool,
3019 }
3020
3021 struct V<'cx, 'tcx> {
3022 cx: &'cx LateContext<'tcx>,
3023 break_targets: Vec<BreakTarget>,
3024 break_targets_for_result_ty: u32,
3025 in_final_expr: bool,
3026 requires_semi: bool,
3027 is_never: bool,
3028 }
3029
3030 impl V<'_, '_> {
3031 fn push_break_target(&mut self, id: HirId) {
3032 self.break_targets.push(BreakTarget { id, unused: true });
3033 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3034 }
3035 }
3036
3037 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3038 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3039 if self.is_never && self.break_targets.is_empty() {
3056 if self.in_final_expr && !self.requires_semi {
3057 match e.kind {
3060 ExprKind::DropTemps(e) => self.visit_expr(e),
3061 ExprKind::If(_, then, Some(else_)) => {
3062 self.visit_expr(then);
3063 self.visit_expr(else_);
3064 },
3065 ExprKind::Match(_, arms, _) => {
3066 for arm in arms {
3067 self.visit_expr(arm.body);
3068 }
3069 },
3070 ExprKind::Loop(b, ..) => {
3071 self.push_break_target(e.hir_id);
3072 self.in_final_expr = false;
3073 self.visit_block(b);
3074 self.break_targets.pop();
3075 },
3076 ExprKind::Block(b, _) => {
3077 if b.targeted_by_break {
3078 self.push_break_target(b.hir_id);
3079 self.visit_block(b);
3080 self.break_targets.pop();
3081 } else {
3082 self.visit_block(b);
3083 }
3084 },
3085 _ => {
3086 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3087 },
3088 }
3089 }
3090 return;
3091 }
3092 match e.kind {
3093 ExprKind::DropTemps(e) => self.visit_expr(e),
3094 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3095 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3096 self.in_final_expr = false;
3097 self.visit_expr(e);
3098 self.is_never = true;
3099 },
3100 ExprKind::Break(dest, e) => {
3101 if let Some(e) = e {
3102 self.in_final_expr = false;
3103 self.visit_expr(e);
3104 }
3105 if let Ok(id) = dest.target_id
3106 && let Some((i, target)) = self
3107 .break_targets
3108 .iter_mut()
3109 .enumerate()
3110 .find(|(_, target)| target.id == id)
3111 {
3112 target.unused &= self.is_never;
3113 if i < self.break_targets_for_result_ty as usize {
3114 self.requires_semi = true;
3115 }
3116 }
3117 self.is_never = true;
3118 },
3119 ExprKind::If(cond, then, else_) => {
3120 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3121 self.visit_expr(cond);
3122 self.in_final_expr = in_final_expr;
3123
3124 if self.is_never {
3125 self.visit_expr(then);
3126 if let Some(else_) = else_ {
3127 self.visit_expr(else_);
3128 }
3129 } else {
3130 self.visit_expr(then);
3131 let is_never = mem::replace(&mut self.is_never, false);
3132 if let Some(else_) = else_ {
3133 self.visit_expr(else_);
3134 self.is_never &= is_never;
3135 }
3136 }
3137 },
3138 ExprKind::Match(scrutinee, arms, _) => {
3139 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3140 self.visit_expr(scrutinee);
3141 self.in_final_expr = in_final_expr;
3142
3143 if self.is_never {
3144 for arm in arms {
3145 self.visit_arm(arm);
3146 }
3147 } else {
3148 let mut is_never = true;
3149 for arm in arms {
3150 self.is_never = false;
3151 if let Some(guard) = arm.guard {
3152 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3153 self.visit_expr(guard);
3154 self.in_final_expr = in_final_expr;
3155 self.is_never = false;
3158 }
3159 self.visit_expr(arm.body);
3160 is_never &= self.is_never;
3161 }
3162 self.is_never = is_never;
3163 }
3164 },
3165 ExprKind::Loop(b, _, _, _) => {
3166 self.push_break_target(e.hir_id);
3167 self.in_final_expr = false;
3168 self.visit_block(b);
3169 self.is_never = self.break_targets.pop().unwrap().unused;
3170 },
3171 ExprKind::Block(b, _) => {
3172 if b.targeted_by_break {
3173 self.push_break_target(b.hir_id);
3174 self.visit_block(b);
3175 self.is_never &= self.break_targets.pop().unwrap().unused;
3176 } else {
3177 self.visit_block(b);
3178 }
3179 },
3180 _ => {
3181 self.in_final_expr = false;
3182 walk_expr(self, e);
3183 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3184 },
3185 }
3186 }
3187
3188 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3189 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3190 for s in b.stmts {
3191 self.visit_stmt(s);
3192 }
3193 self.in_final_expr = in_final_expr;
3194 if let Some(e) = b.expr {
3195 self.visit_expr(e);
3196 }
3197 }
3198
3199 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3200 if let Some(e) = l.init {
3201 self.visit_expr(e);
3202 }
3203 if let Some(else_) = l.els {
3204 let is_never = self.is_never;
3205 self.visit_block(else_);
3206 self.is_never = is_never;
3207 }
3208 }
3209
3210 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3211 if let Some(guard) = arm.guard {
3212 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3213 self.visit_expr(guard);
3214 self.in_final_expr = in_final_expr;
3215 }
3216 self.visit_expr(arm.body);
3217 }
3218 }
3219
3220 if cx.typeck_results().expr_ty(e).is_never() {
3221 Some(RequiresSemi::No)
3222 } else if let ExprKind::Block(b, _) = e.kind
3223 && !b.targeted_by_break
3224 && b.expr.is_none()
3225 {
3226 None
3228 } else {
3229 let mut v = V {
3230 cx,
3231 break_targets: Vec::new(),
3232 break_targets_for_result_ty: 0,
3233 in_final_expr: true,
3234 requires_semi: false,
3235 is_never: false,
3236 };
3237 v.visit_expr(e);
3238 v.is_never
3239 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3240 RequiresSemi::Yes
3241 } else {
3242 RequiresSemi::No
3243 })
3244 }
3245}
3246
3247pub fn get_path_from_caller_to_method_type<'tcx>(
3253 tcx: TyCtxt<'tcx>,
3254 from: LocalDefId,
3255 method: DefId,
3256 args: GenericArgsRef<'tcx>,
3257) -> String {
3258 let assoc_item = tcx.associated_item(method);
3259 let def_id = assoc_item.container_id(tcx);
3260 match assoc_item.container {
3261 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3262 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3263 let ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
3264 get_path_to_ty(tcx, from, ty, args)
3265 },
3266 }
3267}
3268
3269fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3270 match ty.kind() {
3271 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3272 rustc_ty::Array(..)
3274 | rustc_ty::Dynamic(..)
3275 | rustc_ty::Never
3276 | rustc_ty::RawPtr(_, _)
3277 | rustc_ty::Ref(..)
3278 | rustc_ty::Slice(_)
3279 | rustc_ty::Tuple(_) => format!(
3280 "<{}>",
3281 EarlyBinder::bind(tcx, ty).instantiate(tcx, args).skip_norm_wip()
3282 ),
3283 _ => ty.to_string(),
3284 }
3285}
3286
3287fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3289 if callee.is_local() {
3291 let callee_path = tcx.def_path(callee);
3292 let caller_path = tcx.def_path(from.to_def_id());
3293 maybe_get_relative_path(&caller_path, &callee_path, 2)
3294 } else {
3295 tcx.def_path_str(callee)
3296 }
3297}
3298
3299fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3312 use itertools::EitherOrBoth::{Both, Left, Right};
3313
3314 let unique_parts = to
3316 .data
3317 .iter()
3318 .zip_longest(from.data.iter())
3319 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3320 .map(|el| match el {
3321 Both(l, r) => Both(l.data, r.data),
3322 Left(l) => Left(l.data),
3323 Right(r) => Right(r.data),
3324 });
3325
3326 let mut go_up_by = 0;
3328 let mut path = Vec::new();
3329 for el in unique_parts {
3330 match el {
3331 Both(l, r) => {
3332 if let DefPathData::TypeNs(sym) = l {
3342 path.push(sym);
3343 }
3344 if let DefPathData::TypeNs(_) = r {
3345 go_up_by += 1;
3346 }
3347 },
3348 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3353 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3358 _ => {},
3359 }
3360 }
3361
3362 if go_up_by > max_super {
3363 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3365 if let DefPathData::TypeNs(sym) = el.data {
3366 Some(sym)
3367 } else {
3368 None
3369 }
3370 })))
3371 } else if go_up_by == 0 && path.is_empty() {
3372 String::from("Self")
3373 } else {
3374 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3375 }
3376}
3377
3378pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3381 matches!(
3382 cx.tcx.parent_hir_node(id),
3383 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3384 )
3385}
3386
3387pub fn is_block_like(expr: &Expr<'_>) -> bool {
3390 matches!(
3391 expr.kind,
3392 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3393 )
3394}
3395
3396pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3398 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3399 match expr.kind {
3400 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3401 _ if is_block_like(expr) => is_operand,
3402 _ => false,
3403 }
3404 }
3405
3406 contains_block(expr, false)
3407}
3408
3409pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3411 if let Some(parent_expr) = get_parent_expr(cx, expr)
3412 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3413 && receiver.hir_id == expr.hir_id
3414 {
3415 return true;
3416 }
3417 false
3418}
3419
3420pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3423 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3424 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3425 && temporary_ty
3426 .walk()
3427 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3428 {
3429 ControlFlow::Break(())
3430 } else {
3431 ControlFlow::Continue(())
3432 }
3433 })
3434 .is_break()
3435}
3436
3437pub fn leaks_droppable_temporary<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3440 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3441 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) {
3442 ControlFlow::Break(())
3443 } else {
3444 ControlFlow::Continue(())
3445 }
3446 })
3447 .is_break()
3448}
3449
3450pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3461 let expr_ty_is_adjusted = cx
3462 .typeck_results()
3463 .expr_adjustments(expr)
3464 .iter()
3465 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3467 if expr_ty_is_adjusted {
3468 return true;
3469 }
3470
3471 match expr.kind {
3474 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3475 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip();
3476
3477 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3478 return false;
3479 }
3480
3481 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3482 let mut args_with_ty_param = {
3483 fn_sig
3484 .inputs()
3485 .skip_binder()
3486 .iter()
3487 .skip(self_arg_count)
3488 .zip(args)
3489 .filter_map(|(arg_ty, arg)| {
3490 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3491 Some(arg)
3492 } else {
3493 None
3494 }
3495 })
3496 };
3497 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3498 },
3499 ExprKind::Struct(qpath, _, _) => {
3501 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3502 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3503 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3504 return true;
3506 };
3507 v_def
3508 .fields
3509 .iter()
3510 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3511 } else {
3512 false
3513 }
3514 },
3515 ExprKind::Block(
3517 &Block {
3518 expr: Some(ret_expr), ..
3519 },
3520 _,
3521 )
3522 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3523
3524 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3526 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3528 ExprKind::If(_, then, maybe_else) => {
3530 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3531 },
3532 ExprKind::Match(_, arms, _) => arms
3533 .iter()
3534 .map(|arm| arm.body)
3535 .any(|body| expr_requires_coercion(cx, body)),
3536 _ => false,
3537 }
3538}
3539
3540pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3543 if let Some(hir_id) = expr.res_local_id()
3544 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3545 {
3546 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3547 } else if let ExprKind::Path(p) = &expr.kind
3548 && let Some(mutability) = cx
3549 .qpath_res(p, expr.hir_id)
3550 .opt_def_id()
3551 .and_then(|id| cx.tcx.static_mutability(id))
3552 {
3553 mutability == Mutability::Mut
3554 } else if let ExprKind::Field(parent, _) = expr.kind {
3555 is_mutable(cx, parent)
3556 } else {
3557 true
3558 }
3559}
3560
3561pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3564 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3565 return hir_ty;
3566 };
3567 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3568 && let Some(segment) = path.segments.last()
3569 && segment.ident.name == sym::Option
3570 && let Res::Def(DefKind::Enum, def_id) = segment.res
3571 && def_id == option_def_id
3572 && let [GenericArg::Type(arg_ty)] = segment.args().args
3573 {
3574 hir_ty = arg_ty.as_unambig_ty();
3575 }
3576 hir_ty
3577}
3578
3579pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3582 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3583 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3584 && let ctxt = expr.span.ctxt()
3585 && for_each_expr_without_closures(into_future_arg, |e| {
3586 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3587 })
3588 .is_none()
3589 {
3590 Some(into_future_arg)
3591 } else {
3592 None
3593 }
3594}
3595
3596pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3598 if let ExprKind::Call(fn_expr, []) = &expr.kind
3599 && let ExprKind::Path(qpath) = &fn_expr.kind
3600 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3601 {
3602 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3603 } else {
3604 false
3605 }
3606}
3607
3608pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3624 let enclosing_body_owner = cx
3625 .tcx
3626 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3627 let mut prev_id = expr.hir_id;
3628 let mut skip_until_id = None;
3629 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3630 if hir_id == enclosing_body_owner {
3631 return true;
3632 }
3633 if let Some(id) = skip_until_id {
3634 prev_id = hir_id;
3635 if id == hir_id {
3636 skip_until_id = None;
3637 }
3638 continue;
3639 }
3640 match node {
3641 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3642 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3643 Node::Expr(expr) => match expr.kind {
3644 ExprKind::Ret(_) => return true,
3645 ExprKind::If(_, then, opt_else)
3646 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3647 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3648 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3649 ExprKind::Break(
3650 Destination {
3651 target_id: Ok(target_id),
3652 ..
3653 },
3654 _,
3655 ) => skip_until_id = Some(target_id),
3656 _ => break,
3657 },
3658 _ => break,
3659 }
3660 prev_id = hir_id;
3661 }
3662
3663 false
3666}
3667
3668pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3671 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3672 matches!(
3673 adj.kind,
3674 Adjust::Deref(DerefAdjustKind::Overloaded(_))
3675 | Adjust::Pointer(PointerCoercion::Unsize)
3676 | Adjust::NeverToAny
3677 )
3678 })
3679}
3680
3681pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3683 matches!(
3684 expr.kind,
3685 ExprKind::Closure(Closure {
3686 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3687 CoroutineDesugaring::Async,
3688 CoroutineSource::Block
3689 )),
3690 ..
3691 })
3692 )
3693}
3694
3695pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3697 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3698}
3699
3700#[inline]
3703pub fn hir_parent_with_src_iter(tcx: TyCtxt<'_>, mut id: HirId) -> impl Iterator<Item = (Node<'_>, HirId)> {
3704 tcx.hir_parent_id_iter(id)
3705 .map(move |parent| (tcx.hir_node(parent), mem::replace(&mut id, parent)))
3706}