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