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 eager_or_lazy;
50pub mod higher;
51mod hir_utils;
52pub mod macros;
53pub mod mir;
54pub mod msrvs;
55pub mod numeric_literal;
56pub mod paths;
57pub mod qualify_min_const_fn;
58pub mod res;
59pub mod source;
60pub mod str_utils;
61pub mod sugg;
62pub mod sym;
63pub mod ty;
64pub mod usage;
65pub mod visitors;
66
67pub use self::attrs::*;
68pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
69pub use self::hir_utils::{
70 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
71 hash_stmt, is_bool, over,
72};
73
74use core::mem;
75use core::ops::ControlFlow;
76use std::collections::hash_map::Entry;
77use std::iter::{once, repeat_n, zip};
78use std::sync::{Mutex, MutexGuard, OnceLock};
79
80use itertools::Itertools;
81use rustc_abi::Integer;
82use rustc_ast::ast::{self, LitKind, RangeLimits};
83use rustc_ast::{LitIntType, join_path_syms};
84use rustc_data_structures::fx::FxHashMap;
85use rustc_data_structures::indexmap;
86use rustc_data_structures::packed::Pu128;
87use rustc_data_structures::unhash::UnindexMap;
88use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
89use rustc_hir::attrs::CfgEntry;
90use rustc_hir::def::{DefKind, Res};
91use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
92use rustc_hir::definitions::{DefPath, DefPathData};
93use rustc_hir::hir_id::{HirIdMap, HirIdSet};
94use rustc_hir::intravisit::{Visitor, walk_expr};
95use rustc_hir::{
96 self as hir, AnonConst, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, CRATE_HIR_ID, Closure, ConstArg,
97 ConstArgKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind,
98 FieldDef, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem,
99 LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path,
100 PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, Variant, def,
101 find_attr,
102};
103use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
104use rustc_lint::{LateContext, Level, Lint, LintContext};
105use rustc_middle::hir::nested_filter;
106use rustc_middle::hir::place::PlaceBase;
107use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
108use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion};
109use rustc_middle::ty::layout::IntegerExt;
110use rustc_middle::ty::{
111 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
112 TypeFlags, TypeVisitableExt, TypeckResults, UintTy, UpvarCapture,
113};
114use rustc_span::hygiene::{ExpnKind, MacroKind};
115use rustc_span::source_map::SourceMap;
116use rustc_span::symbol::{Ident, Symbol, kw};
117use rustc_span::{InnerSpan, Span, SyntaxContext};
118use source::{SpanRangeExt, walk_span_to_context};
119use visitors::{Visitable, for_each_unconsumed_temporary};
120
121use crate::ast_utils::unordered_over;
122use crate::consts::{ConstEvalCtxt, Constant};
123use crate::higher::Range;
124use crate::msrvs::Msrv;
125use crate::res::{MaybeDef, MaybeQPath, MaybeResPath};
126use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
127use crate::visitors::for_each_expr_without_closures;
128
129pub const VEC_METHODS_SHADOWING_SLICE_METHODS: [Symbol; 3] = [sym::as_ptr, sym::is_empty, sym::len];
131
132#[macro_export]
133macro_rules! extract_msrv_attr {
134 () => {
135 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
136 let sess = rustc_lint::LintContext::sess(cx);
137 self.msrv.check_attributes(sess, attrs);
138 }
139
140 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
141 let sess = rustc_lint::LintContext::sess(cx);
142 self.msrv.check_attributes_post(sess, attrs);
143 }
144 };
145}
146
147pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
170 while let Some(init) = expr
171 .res_local_id()
172 .and_then(|id| find_binding_init(cx, id))
173 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
174 {
175 expr = init;
176 }
177 expr
178}
179
180pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
189 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
190 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
191 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
192 {
193 return local.init;
194 }
195 None
196}
197
198pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
202 for (_, node) in cx.tcx.hir_parent_iter(local) {
203 match node {
204 Node::Pat(..) | Node::PatField(..) => {},
205 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
206 _ => return true,
207 }
208 }
209
210 false
211}
212
213pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
224 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
225 cx.enclosing_body.is_some_and(|id| {
226 cx.tcx
227 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
228 .is_some()
229 })
230}
231
232pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
239 use rustc_hir::ConstContext::{Const, ConstFn, Static};
240 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
241 return false;
242 };
243 match ctx {
244 ConstFn => false,
245 Static(_) | Const { inline: _ } => true,
246 }
247}
248
249pub fn is_enum_variant_ctor(
251 cx: &LateContext<'_>,
252 enum_item: Symbol,
253 variant_name: Symbol,
254 ctor_call_id: DefId,
255) -> bool {
256 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
257 return false;
258 };
259
260 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
261 variants
262 .filter(|variant| variant.name == variant_name)
263 .filter_map(|variant| variant.ctor.as_ref())
264 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
265}
266
267pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
269 let did = match cx.tcx.def_kind(did) {
270 DefKind::Ctor(..) => cx.tcx.parent(did),
271 DefKind::Variant => match cx.tcx.opt_parent(did) {
273 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
274 _ => did,
275 },
276 _ => did,
277 };
278
279 cx.tcx.is_diagnostic_item(item, did)
280}
281
282pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
284 let did = match cx.tcx.def_kind(did) {
285 DefKind::Ctor(..) => cx.tcx.parent(did),
286 DefKind::Variant => match cx.tcx.opt_parent(did) {
288 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
289 _ => did,
290 },
291 _ => did,
292 };
293
294 cx.tcx.lang_items().get(item) == Some(did)
295}
296
297pub fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
299 expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
300}
301
302pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
304 if let ExprKind::Call(e, [arg]) = expr.kind
305 && e.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
306 {
307 Some(arg)
308 } else {
309 None
310 }
311}
312
313pub fn is_empty_block(expr: &Expr<'_>) -> bool {
315 matches!(
316 expr.kind,
317 ExprKind::Block(
318 Block {
319 stmts: [],
320 expr: None,
321 ..
322 },
323 _,
324 )
325 )
326}
327
328pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
330 matches!(
331 expr.kind,
332 ExprKind::Block(
333 Block {
334 stmts: [],
335 expr: None,
336 ..
337 },
338 _
339 ) | ExprKind::Tup([])
340 )
341}
342
343pub fn is_wild(pat: &Pat<'_>) -> bool {
345 matches!(pat.kind, PatKind::Wild)
346}
347
348pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> {
355 if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind
356 && cx
357 .qpath_res(qpath, pat.hir_id)
358 .ctor_parent(cx)
359 .is_lang_item(cx, OptionSome)
360 {
361 Some(inner)
362 } else {
363 None
364 }
365}
366
367pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
369 matches!(pat.kind,
370 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
371 if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone))
372}
373
374pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
376 is_none_pattern(cx, arm.pat)
377 && matches!(
378 peel_blocks(arm.body).kind,
379 ExprKind::Path(qpath)
380 if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)
381 )
382}
383
384pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
386 match *qpath {
387 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
388 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
389 QPath::TypeRelative(..) => false,
390 }
391}
392
393pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
395 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
396 && let ItemKind::Impl(imp) = item.kind
397 {
398 imp.of_trait.is_some()
399 } else {
400 false
401 }
402}
403
404pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
405 match *path {
406 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
407 QPath::TypeRelative(_, seg) => seg,
408 }
409}
410
411pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
412 last_path_segment(qpath)
413 .args
414 .map_or(&[][..], |a| a.args)
415 .iter()
416 .filter_map(|a| match a {
417 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
418 _ => None,
419 })
420}
421
422pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
427 match expr.kind {
428 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
429 ExprKind::Path(QPath::Resolved(
430 _,
431 Path {
432 res: Res::Local(local), ..
433 },
434 )) => Some(*local),
435 _ => None,
436 }
437}
438
439pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
455 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
456 && let ItemKind::Impl(impl_) = &item.kind
457 && let Some(of_trait) = impl_.of_trait
458 {
459 return Some(&of_trait.trait_ref);
460 }
461 None
462}
463
464fn projection_stack<'a, 'hir>(
472 mut e: &'a Expr<'hir>,
473 ctxt: SyntaxContext,
474) -> Option<(Vec<&'a Expr<'hir>>, &'a Expr<'hir>)> {
475 let mut result = vec![];
476 let root = loop {
477 match e.kind {
478 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) if e.span.ctxt() == ctxt => {
479 result.push(e);
480 e = ep;
481 },
482 ExprKind::Index(..) | ExprKind::Field(..) => return None,
483 _ => break e,
484 }
485 };
486 result.reverse();
487 Some((result, root))
488}
489
490pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
492 cx.typeck_results()
493 .expr_adjustments(e)
494 .iter()
495 .find_map(|a| match a.kind {
496 Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)),
497 Adjust::Deref(DerefAdjustKind::Builtin) => None,
498 _ => Some(None),
499 })
500 .and_then(|x| x)
501}
502
503pub fn can_mut_borrow_both(cx: &LateContext<'_>, ctxt: SyntaxContext, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
506 let Some((s1, r1)) = projection_stack(e1, ctxt) else {
507 return false;
508 };
509 let Some((s2, r2)) = projection_stack(e2, ctxt) else {
510 return false;
511 };
512 if !eq_expr_value(cx, ctxt, r1, r2) {
513 return true;
514 }
515 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
516 return false;
517 }
518
519 for (x1, x2) in zip(&s1, &s2) {
520 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
521 return false;
522 }
523
524 match (&x1.kind, &x2.kind) {
525 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
526 if i1 != i2 {
527 return true;
528 }
529 },
530 _ => return false,
531 }
532 }
533 false
534}
535
536fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
539 let std_types_symbols = &[
540 sym::Vec,
541 sym::VecDeque,
542 sym::LinkedList,
543 sym::HashMap,
544 sym::BTreeMap,
545 sym::HashSet,
546 sym::BTreeSet,
547 sym::BinaryHeap,
548 ];
549
550 if let QPath::TypeRelative(_, method) = path
551 && method.ident.name == sym::new
552 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
553 && let Some(adt) = cx
554 .tcx
555 .type_of(impl_did)
556 .instantiate_identity()
557 .skip_norm_wip()
558 .ty_adt_def()
559 {
560 return Some(adt.did()) == cx.tcx.lang_items().string()
561 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
562 }
563 false
564}
565
566pub fn is_default_equivalent_call(
568 cx: &LateContext<'_>,
569 repl_func: &Expr<'_>,
570 whole_call_expr: Option<&Expr<'_>>,
571) -> bool {
572 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
573 && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx)
574 && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default)
575 || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath))
576 {
577 return true;
578 }
579
580 let Some(e) = whole_call_expr else { return false };
583 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
584 return false;
585 };
586 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
587 return false;
588 };
589 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
590 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
591 cx.tcx.lifetimes.re_erased.into()
592 } else if param.index == 0 && param.name == kw::SelfUpper {
593 ty.into()
594 } else {
595 param.to_error(cx.tcx)
596 }
597 });
598 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
599
600 let Ok(Some(instance)) = instance else { return false };
601 if let rustc_ty::InstanceKind::Item(def) = instance.def
602 && !cx.tcx.is_mir_available(def)
603 {
604 return false;
605 }
606 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
607 return false;
608 };
609 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
610 return false;
611 };
612
613 let body = cx.tcx.instance_mir(instance.def);
619 for block_data in body.basic_blocks.iter() {
620 if block_data.statements.len() == 1
621 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
622 && assign.0.local == RETURN_PLACE
623 && let Rvalue::Aggregate(kind, _places) = &assign.1
624 && let AggregateKind::Adt(did, variant_index, _, _, _) = **kind
625 && let def = cx.tcx.adt_def(did)
626 && let variant = &def.variant(variant_index)
627 && variant.fields.is_empty()
628 && let Some((_, did)) = variant.ctor
629 && did == repl_def_id
630 {
631 return true;
632 } else if block_data.statements.is_empty()
633 && let Some(term) = &block_data.terminator
634 {
635 match &term.kind {
636 TerminatorKind::Call {
637 func: Operand::Constant(c),
638 ..
639 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
640 && *did == repl_def_id =>
641 {
642 return true;
643 },
644 TerminatorKind::TailCall {
645 func: Operand::Constant(c),
646 ..
647 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
648 && *did == repl_def_id =>
649 {
650 return true;
651 },
652 _ => {},
653 }
654 }
655 }
656 false
657}
658
659pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
663 match &e.kind {
664 ExprKind::Lit(lit) => match lit.node {
665 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
666 LitKind::Str(s, _) => s.is_empty(),
667 _ => false,
668 },
669 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
670 ExprKind::Repeat(x, len) => {
671 if let ConstArgKind::Anon(anon_const) = len.kind
672 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
673 && let LitKind::Int(v, _) = const_lit.node
674 && v <= 32
675 && is_default_equivalent(cx, x)
676 {
677 true
678 } else {
679 false
680 }
681 },
682 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
683 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
684 ExprKind::Path(qpath) => cx
685 .qpath_res(qpath, e.hir_id)
686 .ctor_parent(cx)
687 .is_lang_item(cx, OptionNone),
688 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
689 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
690 _ => false,
691 }
692}
693
694fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
695 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
696 && seg.ident.name == sym::from
697 {
698 match arg.kind {
699 ExprKind::Lit(hir::Lit {
700 node: LitKind::Str(sym, _),
701 ..
702 }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String),
703 ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec),
704 ExprKind::Repeat(_, len) => {
705 if let ConstArgKind::Anon(anon_const) = len.kind
706 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
707 && let LitKind::Int(v, _) = const_lit.node
708 {
709 return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec);
710 }
711 },
712 _ => (),
713 }
714 }
715 false
716}
717
718pub fn can_move_expr_to_closure_no_visit<'tcx>(
750 cx: &LateContext<'tcx>,
751 expr: &'tcx Expr<'_>,
752 loop_ids: &[HirId],
753 ignore_locals: &HirIdSet,
754) -> bool {
755 match expr.kind {
756 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
757 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
758 if loop_ids.contains(&id) =>
759 {
760 true
761 },
762 ExprKind::Break(..)
763 | ExprKind::Continue(_)
764 | ExprKind::Ret(_)
765 | ExprKind::Yield(..)
766 | ExprKind::InlineAsm(_) => false,
767 ExprKind::Field(
770 &Expr {
771 hir_id,
772 kind:
773 ExprKind::Path(QPath::Resolved(
774 _,
775 Path {
776 res: Res::Local(local_id),
777 ..
778 },
779 )),
780 ..
781 },
782 _,
783 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
784 false
786 },
787 _ => true,
788 }
789}
790
791#[derive(Debug, Clone, Copy, PartialEq, Eq)]
793pub enum CaptureKind {
794 Value,
795 Use,
796 Ref(Mutability),
797}
798impl CaptureKind {
799 pub fn is_imm_ref(self) -> bool {
800 self == Self::Ref(Mutability::Not)
801 }
802}
803impl std::ops::BitOr for CaptureKind {
804 type Output = Self;
805 fn bitor(self, rhs: Self) -> Self::Output {
806 match (self, rhs) {
807 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
808 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
809 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
810 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
811 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
812 }
813 }
814}
815impl std::ops::BitOrAssign for CaptureKind {
816 fn bitor_assign(&mut self, rhs: Self) {
817 *self = *self | rhs;
818 }
819}
820
821pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
827 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
828 let mut capture = CaptureKind::Ref(Mutability::Not);
829 pat.each_binding_or_first(&mut |_, id, span, _| match cx
830 .typeck_results()
831 .extract_binding_mode(cx.sess(), id, span)
832 .0
833 {
834 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
835 capture = CaptureKind::Value;
836 },
837 ByRef::Yes(_, Mutability::Mut) if capture != CaptureKind::Value => {
838 capture = CaptureKind::Ref(Mutability::Mut);
839 },
840 _ => (),
841 });
842 capture
843 }
844
845 debug_assert!(matches!(
846 e.kind,
847 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
848 ));
849
850 let mut capture = CaptureKind::Value;
851 let mut capture_expr_ty = e;
852
853 for (parent, child_id) in hir_parent_with_src_iter(cx.tcx, e.hir_id) {
854 if let [
855 Adjustment {
856 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
857 target,
858 },
859 ref adjust @ ..,
860 ] = *cx
861 .typeck_results()
862 .adjustments()
863 .get(child_id)
864 .map_or(&[][..], |x| &**x)
865 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
866 *adjust.last().map_or(target, |a| a.target).kind()
867 {
868 return CaptureKind::Ref(mutability);
869 }
870
871 match parent {
872 Node::Expr(e) => match e.kind {
873 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
874 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
875 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
876 return CaptureKind::Ref(Mutability::Mut);
877 },
878 ExprKind::Field(..) => {
879 if capture == CaptureKind::Value {
880 capture_expr_ty = e;
881 }
882 },
883 ExprKind::Let(let_expr) => {
884 let mutability = match pat_capture_kind(cx, let_expr.pat) {
885 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
886 CaptureKind::Ref(m) => m,
887 };
888 return CaptureKind::Ref(mutability);
889 },
890 ExprKind::Match(_, arms, _) => {
891 let mut mutability = Mutability::Not;
892 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
893 match capture {
894 CaptureKind::Value | CaptureKind::Use => break,
895 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
896 CaptureKind::Ref(Mutability::Not) => (),
897 }
898 }
899 return CaptureKind::Ref(mutability);
900 },
901 _ => break,
902 },
903 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
904 CaptureKind::Value | CaptureKind::Use => break,
905 capture @ CaptureKind::Ref(_) => return capture,
906 },
907 _ => break,
908 }
909 }
910
911 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
912 CaptureKind::Ref(Mutability::Not)
914 } else {
915 capture
916 }
917}
918
919pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
922 struct V<'cx, 'tcx> {
923 cx: &'cx LateContext<'tcx>,
924 loops: Vec<HirId>,
926 locals: HirIdSet,
928 allow_closure: bool,
930 captures: HirIdMap<CaptureKind>,
933 }
934 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
935 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
936 if !self.allow_closure {
937 return;
938 }
939
940 match e.kind {
941 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
942 if !self.locals.contains(&l) {
943 let cap = capture_local_usage(self.cx, e);
944 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
945 }
946 },
947 ExprKind::Closure(closure) => {
948 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
949 let local_id = match capture.place.base {
950 PlaceBase::Local(id) => id,
951 PlaceBase::Upvar(var) => var.var_path.hir_id,
952 _ => continue,
953 };
954 if !self.locals.contains(&local_id) {
955 let capture = match capture.info.capture_kind {
956 UpvarCapture::ByValue => CaptureKind::Value,
957 UpvarCapture::ByUse => CaptureKind::Use,
958 UpvarCapture::ByRef(kind) => match kind {
959 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
960 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
961 CaptureKind::Ref(Mutability::Mut)
962 },
963 },
964 };
965 self.captures
966 .entry(local_id)
967 .and_modify(|e| *e |= capture)
968 .or_insert(capture);
969 }
970 }
971 },
972 ExprKind::Loop(b, ..) => {
973 self.loops.push(e.hir_id);
974 self.visit_block(b);
975 self.loops.pop();
976 },
977 _ => {
978 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
979 walk_expr(self, e);
980 },
981 }
982 }
983
984 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
985 p.each_binding_or_first(&mut |_, id, _, _| {
986 self.locals.insert(id);
987 });
988 }
989 }
990
991 let mut v = V {
992 cx,
993 loops: Vec::new(),
994 locals: HirIdSet::default(),
995 allow_closure: true,
996 captures: HirIdMap::default(),
997 };
998 v.visit_expr(expr);
999 v.allow_closure.then_some(v.captures)
1000}
1001
1002pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1004
1005pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1008 let mut method_names = Vec::with_capacity(max_depth);
1009 let mut arg_lists = Vec::with_capacity(max_depth);
1010 let mut spans = Vec::with_capacity(max_depth);
1011
1012 let mut current = expr;
1013 for _ in 0..max_depth {
1014 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1015 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1016 break;
1017 }
1018 method_names.push(path.ident.name);
1019 arg_lists.push((*receiver, &**args));
1020 spans.push(path.ident.span);
1021 current = receiver;
1022 } else {
1023 break;
1024 }
1025 }
1026
1027 (method_names, arg_lists, spans)
1028}
1029
1030pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1037 let mut current = expr;
1038 let mut matched = Vec::with_capacity(methods.len());
1039 for method_name in methods.iter().rev() {
1040 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1042 if path.ident.name == *method_name {
1043 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1044 return None;
1045 }
1046 matched.push((receiver, args)); current = receiver; } else {
1049 return None;
1050 }
1051 } else {
1052 return None;
1053 }
1054 }
1055 matched.reverse();
1057 Some(matched)
1058}
1059
1060pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1062 cx.tcx
1063 .entry_fn(())
1064 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1065}
1066
1067pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1069 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1070 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1071}
1072
1073pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1075 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1076 match cx.tcx.hir_node_by_def_id(parent_id) {
1077 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1078 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1079 _ => None,
1080 }
1081}
1082
1083pub struct ContainsName<'a, 'tcx> {
1084 pub cx: &'a LateContext<'tcx>,
1085 pub name: Symbol,
1086}
1087
1088impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1089 type Result = ControlFlow<()>;
1090 type NestedFilter = nested_filter::OnlyBodies;
1091
1092 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1093 if self.name == name {
1094 ControlFlow::Break(())
1095 } else {
1096 ControlFlow::Continue(())
1097 }
1098 }
1099
1100 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1101 self.cx.tcx
1102 }
1103}
1104
1105pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1107 let mut cn = ContainsName { cx, name };
1108 cn.visit_expr(expr).is_break()
1109}
1110
1111pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1113 for_each_expr_without_closures(expr, |e| {
1114 if matches!(e.kind, ExprKind::Ret(..)) {
1115 ControlFlow::Break(())
1116 } else {
1117 ControlFlow::Continue(())
1118 }
1119 })
1120 .is_some()
1121}
1122
1123pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1125 get_parent_expr_for_hir(cx, e.hir_id)
1126}
1127
1128pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1131 match cx.tcx.parent_hir_node(hir_id) {
1132 Node::Expr(parent) => Some(parent),
1133 _ => None,
1134 }
1135}
1136
1137pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1139 let enclosing_node = cx
1140 .tcx
1141 .hir_get_enclosing_scope(hir_id)
1142 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1143 enclosing_node.and_then(|node| match node {
1144 Node::Block(block) => Some(block),
1145 Node::Item(&Item {
1146 kind: ItemKind::Fn { body: eid, .. },
1147 ..
1148 })
1149 | Node::ImplItem(&ImplItem {
1150 kind: ImplItemKind::Fn(_, eid),
1151 ..
1152 })
1153 | Node::TraitItem(&TraitItem {
1154 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1155 ..
1156 }) => match cx.tcx.hir_body(eid).value.kind {
1157 ExprKind::Block(block, _) => Some(block),
1158 _ => None,
1159 },
1160 _ => None,
1161 })
1162}
1163
1164pub fn get_enclosing_closure<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Closure<'tcx>> {
1166 cx.tcx.hir_parent_iter(hir_id).find_map(|(_, node)| {
1167 if let Node::Expr(expr) = node
1168 && let ExprKind::Closure(closure) = expr.kind
1169 {
1170 Some(closure)
1171 } else {
1172 None
1173 }
1174 })
1175}
1176
1177pub fn is_upvar_in_closure(cx: &LateContext<'_>, closure: &Closure<'_>, local_id: HirId) -> bool {
1179 cx.typeck_results()
1180 .closure_min_captures
1181 .get(&closure.def_id)
1182 .is_some_and(|x| x.contains_key(&local_id))
1183}
1184
1185pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1187 cx: &LateContext<'tcx>,
1188 expr: &Expr<'_>,
1189) -> Option<&'tcx Expr<'tcx>> {
1190 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1191 match node {
1192 Node::Expr(e) => match e.kind {
1193 ExprKind::Closure { .. }
1194 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1195 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1196
1197 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1199 _ => (),
1200 },
1201 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1202 _ => break,
1203 }
1204 }
1205 None
1206}
1207
1208pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1210 match tcx.hir_parent_iter(id).next() {
1211 Some((
1212 _,
1213 Node::Item(Item {
1214 kind: ItemKind::Impl(imp),
1215 ..
1216 }),
1217 )) => Some(imp),
1218 _ => None,
1219 }
1220}
1221
1222pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1233 while let ExprKind::Block(
1234 Block {
1235 stmts: [],
1236 expr: Some(inner),
1237 rules: BlockCheckMode::DefaultBlock,
1238 ..
1239 },
1240 _,
1241 ) = expr.kind
1242 {
1243 expr = inner;
1244 }
1245 expr
1246}
1247
1248pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1259 while let ExprKind::Block(
1260 Block {
1261 stmts: [],
1262 expr: Some(inner),
1263 rules: BlockCheckMode::DefaultBlock,
1264 ..
1265 }
1266 | Block {
1267 stmts:
1268 [
1269 Stmt {
1270 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1271 ..
1272 },
1273 ],
1274 expr: None,
1275 rules: BlockCheckMode::DefaultBlock,
1276 ..
1277 },
1278 _,
1279 ) = expr.kind
1280 {
1281 expr = inner;
1282 }
1283 expr
1284}
1285
1286pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1288 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1289 match iter.next() {
1290 Some((
1291 _,
1292 Node::Expr(Expr {
1293 kind: ExprKind::If(_, _, Some(else_expr)),
1294 ..
1295 }),
1296 )) => else_expr.hir_id == expr.hir_id,
1297 _ => false,
1298 }
1299}
1300
1301pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1304 hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {
1305 matches!(
1306 node,
1307 Node::LetStmt(LetStmt {
1308 init: Some(init),
1309 els: Some(els),
1310 ..
1311 })
1312 if init.hir_id == child_id || els.hir_id == child_id
1313 )
1314 })
1315}
1316
1317pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1319 hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {
1320 matches!(
1321 node,
1322 Node::LetStmt(LetStmt { els: Some(els), .. })
1323 if els.hir_id == child_id
1324 )
1325 })
1326}
1327
1328pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1343 let ty = cx.typeck_results().expr_ty(expr);
1344 if let Some(Range { start, end, limits, .. }) = Range::hir(cx, expr) {
1345 let start_is_none_or_min = start.is_none_or(|start| {
1346 if let rustc_ty::Adt(_, subst) = ty.kind()
1347 && let bnd_ty = subst.type_at(0)
1348 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1349 {
1350 start_const.is_numeric_min(cx.tcx, bnd_ty)
1351 } else {
1352 false
1353 }
1354 });
1355 let end_is_none_or_max = end.is_none_or(|end| match limits {
1356 RangeLimits::Closed => {
1357 if let rustc_ty::Adt(_, subst) = ty.kind()
1358 && let bnd_ty = subst.type_at(0)
1359 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1360 {
1361 end_const.is_numeric_max(cx.tcx, bnd_ty)
1362 } else {
1363 false
1364 }
1365 },
1366 RangeLimits::HalfOpen => {
1367 if let Some(container_path) = container_path
1368 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1369 && name.ident.name == sym::len
1370 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1371 {
1372 container_path.res == path.res
1373 } else {
1374 false
1375 }
1376 },
1377 });
1378 return start_is_none_or_min && end_is_none_or_max;
1379 }
1380 false
1381}
1382
1383pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1386 if is_integer_literal(e, value) {
1387 return true;
1388 }
1389 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1390 if let Some(Constant::Int(v)) =
1391 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1392 {
1393 return value == v;
1394 }
1395 false
1396}
1397
1398pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1400 if let ExprKind::Lit(spanned) = expr.kind
1402 && let LitKind::Int(v, _) = spanned.node
1403 {
1404 return v == value;
1405 }
1406 false
1407}
1408
1409pub fn is_integer_literal_untyped(expr: &Expr<'_>) -> bool {
1411 if let ExprKind::Lit(spanned) = expr.kind
1412 && let LitKind::Int(_, suffix) = spanned.node
1413 {
1414 return suffix == LitIntType::Unsuffixed;
1415 }
1416
1417 false
1418}
1419
1420pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1422 if let ExprKind::Lit(spanned) = expr.kind
1423 && let LitKind::Float(v, _) = spanned.node
1424 {
1425 v.as_str().parse() == Ok(value)
1426 } else {
1427 false
1428 }
1429}
1430
1431pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1439 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1440}
1441
1442#[must_use]
1446pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1447 loop {
1448 if span.from_expansion() {
1449 let data = span.ctxt().outer_expn_data();
1450 let new_span = data.call_site;
1451
1452 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1453 && mac_name == name
1454 {
1455 return Some(new_span);
1456 }
1457
1458 span = new_span;
1459 } else {
1460 return None;
1461 }
1462 }
1463}
1464
1465#[must_use]
1476pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1477 if span.from_expansion() {
1478 let data = span.ctxt().outer_expn_data();
1479 let new_span = data.call_site;
1480
1481 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1482 && mac_name == name
1483 {
1484 return Some(new_span);
1485 }
1486 }
1487
1488 None
1489}
1490
1491pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1493 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().skip_norm_wip().output();
1494 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1495}
1496
1497pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1499 let arg = cx
1500 .tcx
1501 .fn_sig(fn_def_id)
1502 .instantiate_identity()
1503 .skip_norm_wip()
1504 .input(nth);
1505 cx.tcx.instantiate_bound_regions_with_erased(arg)
1506}
1507
1508pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1510 if let ExprKind::Call(fun, _) = expr.kind
1511 && let ExprKind::Path(ref qp) = fun.kind
1512 {
1513 let res = cx.qpath_res(qp, fun.hir_id);
1514 return match res {
1515 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1516 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1517 _ => false,
1518 };
1519 }
1520 false
1521}
1522
1523pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1526 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1527 !matches!(
1528 cx.qpath_res(qpath, id),
1529 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1530 )
1531 }
1532
1533 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1534 i.into_iter().any(|pat| is_refutable(cx, pat))
1535 }
1536
1537 match pat.kind {
1538 PatKind::Missing => unreachable!(),
1539 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1541 PatKind::Box(pat) | PatKind::Ref(pat, _, _) => is_refutable(cx, pat),
1542 PatKind::Expr(PatExpr {
1543 kind: PatExprKind::Path(qpath),
1544 hir_id,
1545 ..
1546 }) => is_qpath_refutable(cx, qpath, *hir_id),
1547 PatKind::Or(pats) => {
1548 are_refutable(cx, pats)
1550 },
1551 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1552 PatKind::Struct(ref qpath, fields, _) => {
1553 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1554 },
1555 PatKind::TupleStruct(ref qpath, pats, _) => {
1556 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1557 },
1558 PatKind::Slice(head, middle, tail) => {
1559 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1560 rustc_ty::Slice(..) => {
1561 !head.is_empty() || middle.is_none() || !tail.is_empty()
1563 },
1564 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1565 _ => {
1566 true
1568 },
1569 }
1570 },
1571 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1572 }
1573}
1574
1575pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1578 if let PatKind::Or(pats) = pat.kind {
1579 pats.iter().for_each(f);
1580 } else {
1581 f(pat);
1582 }
1583}
1584
1585pub fn is_self(slf: &Param<'_>) -> bool {
1586 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1587 name.name == kw::SelfLower
1588 } else {
1589 false
1590 }
1591}
1592
1593pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1594 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1595 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1596 {
1597 return true;
1598 }
1599 false
1600}
1601
1602pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1603 (0..decl.inputs.len()).map(move |i| &body.params[i])
1604}
1605
1606pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1609 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1610 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1611 && ddpos.as_opt_usize().is_none()
1612 && cx
1613 .qpath_res(path, arm.pat.hir_id)
1614 .ctor_parent(cx)
1615 .is_lang_item(cx, ResultOk)
1616 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1617 && arm.body.res_local_id() == Some(hir_id)
1618 {
1619 return true;
1620 }
1621 false
1622 }
1623
1624 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1625 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1626 cx.qpath_res(path, arm.pat.hir_id)
1627 .ctor_parent(cx)
1628 .is_lang_item(cx, ResultErr)
1629 } else {
1630 false
1631 }
1632 }
1633
1634 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1635 if let MatchSource::TryDesugar(_) = *source {
1637 return Some(expr);
1638 }
1639
1640 if arms.len() == 2
1641 && arms[0].guard.is_none()
1642 && arms[1].guard.is_none()
1643 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1644 {
1645 return Some(expr);
1646 }
1647 }
1648
1649 None
1650}
1651
1652pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1662 let mut suppress_lint = false;
1663
1664 for id in ids {
1665 let level_spec = cx.tcx.lint_level_spec_at_node(lint, id);
1666 if let Some(expectation) = level_spec.lint_id() {
1667 cx.fulfill_expectation(expectation);
1668 }
1669
1670 match level_spec.level() {
1671 Level::Allow | Level::Expect => suppress_lint = true,
1672 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1673 }
1674 }
1675
1676 suppress_lint
1677}
1678
1679pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1687 cx.tcx.lint_level_spec_at_node(lint, id).is_allow()
1688}
1689
1690pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1691 while let PatKind::Ref(subpat, _, _) = pat.kind {
1692 pat = subpat;
1693 }
1694 pat
1695}
1696
1697pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1698 Integer::from_int_ty(&tcx, ity).size().bits()
1699}
1700
1701#[expect(clippy::cast_possible_wrap)]
1702pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1704 let amt = 128 - int_bits(tcx, ity);
1705 ((u as i128) << amt) >> amt
1706}
1707
1708#[expect(clippy::cast_sign_loss)]
1709pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1711 let amt = 128 - int_bits(tcx, ity);
1712 ((u as u128) << amt) >> amt
1713}
1714
1715pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1717 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1718 let amt = 128 - bits;
1719 (u << amt) >> amt
1720}
1721
1722pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1723 attrs.iter().any(|attr| attr.has_name(symbol))
1724}
1725
1726pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1727 find_attr!(cx.tcx, hir_id, Repr { .. })
1728}
1729
1730pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1731 let mut prev_enclosing_node = None;
1732 let mut enclosing_node = node;
1733 while Some(enclosing_node) != prev_enclosing_node {
1734 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1735 return true;
1736 }
1737 prev_enclosing_node = Some(enclosing_node);
1738 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1739 }
1740
1741 false
1742}
1743
1744pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1747 tcx.hir_parent_owner_iter(id)
1748 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1749 .any(|(id, _)| find_attr!(tcx, id.def_id, AutomaticallyDerived))
1750}
1751
1752pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1754 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1757}
1758
1759pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1764 let mut conds = Vec::new();
1765 let mut blocks: Vec<&Block<'_>> = Vec::new();
1766
1767 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1768 conds.push(cond);
1769 if let ExprKind::Block(block, _) = then.kind {
1770 blocks.push(block);
1771 } else {
1772 panic!("ExprKind::If node is not an ExprKind::Block");
1773 }
1774
1775 if let Some(else_expr) = r#else {
1776 expr = else_expr;
1777 } else {
1778 break;
1779 }
1780 }
1781
1782 if !blocks.is_empty()
1784 && let ExprKind::Block(block, _) = expr.kind
1785 {
1786 blocks.push(block);
1787 }
1788
1789 (conds, blocks)
1790}
1791
1792pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1794 if let ExprKind::Closure(&Closure {
1795 body,
1796 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1797 ..
1798 }) = expr.kind
1799 && let ExprKind::Block(
1800 Block {
1801 expr:
1802 Some(Expr {
1803 kind: ExprKind::DropTemps(inner_expr),
1804 ..
1805 }),
1806 ..
1807 },
1808 _,
1809 ) = tcx.hir_body(body).value.kind
1810 {
1811 Some(inner_expr)
1812 } else {
1813 None
1814 }
1815}
1816
1817pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1819 get_async_closure_expr(tcx, body.value)
1820}
1821
1822pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1824 let did = match expr.kind {
1825 ExprKind::Call(path, _) => {
1826 if let ExprKind::Path(ref qpath) = path.kind
1827 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1828 {
1829 Some(did)
1830 } else {
1831 None
1832 }
1833 },
1834 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1835 _ => None,
1836 };
1837
1838 did.is_some_and(|did| find_attr!(cx.tcx, did, MustUse { .. }))
1839}
1840
1841fn is_body_identity_function<'hir>(cx: &LateContext<'_>, func: &Body<'hir>) -> bool {
1855 let [param] = func.params else {
1856 return false;
1857 };
1858
1859 let mut param_pat = param.pat;
1860
1861 let mut advance_param_pat_over_stmts = |stmts: &[Stmt<'hir>]| {
1868 for stmt in stmts {
1869 if let StmtKind::Let(local) = stmt.kind
1870 && let Some(init) = local.init
1871 && is_expr_identity_of_pat(cx, param_pat, init, true)
1872 {
1873 param_pat = local.pat;
1874 } else {
1875 return false;
1876 }
1877 }
1878
1879 true
1880 };
1881
1882 let mut expr = func.value;
1883 loop {
1884 match expr.kind {
1885 ExprKind::Block(
1886 &Block {
1887 stmts: [],
1888 expr: Some(e),
1889 ..
1890 },
1891 _,
1892 )
1893 | ExprKind::Ret(Some(e)) => expr = e,
1894 ExprKind::Block(
1895 &Block {
1896 stmts: [stmt],
1897 expr: None,
1898 ..
1899 },
1900 _,
1901 ) => {
1902 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1903 && let ExprKind::Ret(Some(ret_val)) = e.kind
1904 {
1905 expr = ret_val;
1906 } else {
1907 return false;
1908 }
1909 },
1910 ExprKind::Block(
1911 &Block {
1912 stmts, expr: Some(e), ..
1913 },
1914 _,
1915 ) => {
1916 if !advance_param_pat_over_stmts(stmts) {
1917 return false;
1918 }
1919
1920 expr = e;
1921 },
1922 ExprKind::Block(&Block { stmts, expr: None, .. }, _) => {
1923 if let Some((last_stmt, stmts)) = stmts.split_last()
1924 && advance_param_pat_over_stmts(stmts)
1925 && let StmtKind::Semi(e) | StmtKind::Expr(e) = last_stmt.kind
1926 && let ExprKind::Ret(Some(ret_val)) = e.kind
1927 {
1928 expr = ret_val;
1929 } else {
1930 return false;
1931 }
1932 },
1933 _ => return is_expr_identity_of_pat(cx, param_pat, expr, true),
1934 }
1935 }
1936}
1937
1938pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1948 if cx
1949 .typeck_results()
1950 .pat_binding_modes()
1951 .get(pat.hir_id)
1952 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..)))
1953 {
1954 return false;
1958 }
1959
1960 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1962
1963 match (pat.kind, expr.kind) {
1964 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1965 expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1966 },
1967 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1968 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1969 },
1970 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1971 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1972 {
1973 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1974 },
1975 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1976 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1977 },
1978 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1979 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1980 {
1981 if let ExprKind::Path(ident) = &ident.kind
1983 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1984 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
1986 {
1987 true
1988 } else {
1989 false
1990 }
1991 },
1992 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
1993 if field_pats.len() == fields.len() =>
1994 {
1995 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1997 && unordered_over(field_pats, fields, |field_pat, field| {
1999 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
2000 })
2001 },
2002 _ => false,
2003 }
2004}
2005
2006pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2011 match expr.kind {
2012 ExprKind::Closure(&Closure { body, fn_decl, .. })
2013 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
2014 {
2015 is_body_identity_function(cx, cx.tcx.hir_body(body))
2016 },
2017 ExprKind::Path(QPath::Resolved(_, path))
2018 if path.segments.iter().all(|seg| seg.infer_args)
2019 && let Some(did) = path.res.opt_def_id() =>
2020 {
2021 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
2022 },
2023 _ => false,
2024 }
2025}
2026
2027pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2036 match expr.kind {
2037 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
2038 _ => expr.basic_res().is_diag_item(cx, sym::convert_identity),
2039 }
2040}
2041
2042pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2045 for (node, child_id) in hir_parent_with_src_iter(tcx, expr.hir_id) {
2046 match node {
2047 Node::Block(_) => {},
2048 Node::Arm(arm) if arm.body.hir_id == child_id => {},
2049 Node::Expr(expr) => match expr.kind {
2050 ExprKind::Block(..) | ExprKind::DropTemps(_) => {},
2051 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => {},
2052 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => return None,
2053 _ => return Some((Node::Expr(expr), child_id)),
2054 },
2055 node => return Some((node, child_id)),
2056 }
2057 }
2058 None
2059}
2060
2061pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2063 !matches!(
2064 get_expr_use_or_unification_node(tcx, expr),
2065 None | Some((
2066 Node::Stmt(Stmt {
2067 kind: StmtKind::Expr(_)
2068 | StmtKind::Semi(_)
2069 | StmtKind::Let(LetStmt {
2070 pat: Pat {
2071 kind: PatKind::Wild,
2072 ..
2073 },
2074 ..
2075 }),
2076 ..
2077 }),
2078 _
2079 ))
2080 )
2081}
2082
2083pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2085 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2086}
2087
2088pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2092 !expr.is_place_expr(|base| {
2093 cx.typeck_results()
2094 .adjustments()
2095 .get(base.hir_id)
2096 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2097 })
2098}
2099
2100pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2101 if is_no_core_crate(cx) {
2102 None
2103 } else if is_no_std_crate(cx) {
2104 Some("core")
2105 } else {
2106 Some("std")
2107 }
2108}
2109
2110pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2111 find_attr!(cx.tcx, crate, NoStd)
2112}
2113
2114pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2115 find_attr!(cx.tcx, crate, NoCore)
2116}
2117
2118pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2128 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2129 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2130 } else {
2131 false
2132 }
2133}
2134
2135pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2145 use rustc_trait_selection::traits;
2146 let predicates = cx
2147 .tcx
2148 .predicates_of(did)
2149 .predicates
2150 .iter()
2151 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2152 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2153}
2154
2155pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2157 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2158}
2159
2160pub fn fn_def_id_with_node_args<'tcx>(
2163 cx: &LateContext<'tcx>,
2164 expr: &Expr<'_>,
2165) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2166 let typeck = cx.typeck_results();
2167 match &expr.kind {
2168 ExprKind::MethodCall(..) => Some((
2169 typeck.type_dependent_def_id(expr.hir_id)?,
2170 typeck.node_args(expr.hir_id),
2171 )),
2172 ExprKind::Call(
2173 Expr {
2174 kind: ExprKind::Path(qpath),
2175 hir_id: path_hir_id,
2176 ..
2177 },
2178 ..,
2179 ) => {
2180 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2183 typeck.qpath_res(qpath, *path_hir_id)
2184 {
2185 Some((id, typeck.node_args(*path_hir_id)))
2186 } else {
2187 None
2188 }
2189 },
2190 _ => None,
2191 }
2192}
2193
2194pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2199 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2200 let expr_kind = expr_type.kind();
2201 let is_primitive = match expr_kind {
2202 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2203 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2204 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2205 is_recursively_primitive_type(*element_type)
2206 } else {
2207 unreachable!()
2208 }
2209 },
2210 _ => false,
2211 };
2212
2213 if is_primitive {
2214 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2217 rustc_ty::Slice(..) => return Some("slice".into()),
2218 rustc_ty::Array(..) => return Some("array".into()),
2219 rustc_ty::Tuple(..) => return Some("tuple".into()),
2220 _ => {
2221 let refs_peeled = expr_type.peel_refs();
2224 return Some(refs_peeled.walk().last().unwrap().to_string());
2225 },
2226 }
2227 }
2228 None
2229}
2230
2231pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2239where
2240 Hash: FnMut(&T) -> u64,
2241 Eq: FnMut(&T, &T) -> bool,
2242{
2243 match exprs {
2244 [a, b] if eq(a, b) => return vec![vec![a, b]],
2245 _ if exprs.len() <= 2 => return vec![],
2246 _ => {},
2247 }
2248
2249 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2250
2251 for expr in exprs {
2252 match buckets.entry(hash(expr)) {
2253 indexmap::map::Entry::Occupied(mut o) => {
2254 let bucket = o.get_mut();
2255 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2256 Some(group) => group.push(expr),
2257 None => bucket.push(vec![expr]),
2258 }
2259 },
2260 indexmap::map::Entry::Vacant(v) => {
2261 v.insert(vec![vec![expr]]);
2262 },
2263 }
2264 }
2265
2266 buckets
2267 .into_values()
2268 .flatten()
2269 .filter(|group| group.len() > 1)
2270 .collect()
2271}
2272
2273pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2276 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2277 if let PatKind::Ref(pat, _, _) = pat.kind {
2278 peel(pat, count + 1)
2279 } else {
2280 (pat, count)
2281 }
2282 }
2283 peel(pat, 0)
2284}
2285
2286pub fn peel_hir_expr_while<'tcx>(
2288 mut expr: &'tcx Expr<'tcx>,
2289 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2290) -> &'tcx Expr<'tcx> {
2291 while let Some(e) = f(expr) {
2292 expr = e;
2293 }
2294 expr
2295}
2296
2297pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2300 let mut remaining = count;
2301 let e = peel_hir_expr_while(expr, |e| match e.kind {
2302 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2303 remaining -= 1;
2304 Some(e)
2305 },
2306 _ => None,
2307 });
2308 (e, count - remaining)
2309}
2310
2311pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2314 let mut count: usize = 0;
2315 let mut curr_expr = expr;
2316 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2317 count = count.wrapping_add(1);
2318 curr_expr = local_expr;
2319 }
2320 (curr_expr, count)
2321}
2322
2323pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2326 let mut count = 0;
2327 let e = peel_hir_expr_while(expr, |e| match e.kind {
2328 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2329 count += 1;
2330 Some(e)
2331 },
2332 _ => None,
2333 });
2334 (e, count)
2335}
2336
2337pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2340 let mut count = 0;
2341 loop {
2342 match &ty.kind {
2343 TyKind::Ref(_, ref_ty) => {
2344 ty = ref_ty.ty;
2345 count += 1;
2346 },
2347 _ => break (ty, count),
2348 }
2349 }
2350}
2351
2352pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
2354 match &ty.kind {
2355 TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty),
2356 _ => ty,
2357 }
2358}
2359
2360pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2363 loop {
2364 match expr.kind {
2365 ExprKind::AddrOf(_, _, e) => expr = e,
2366 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2367 _ => break,
2368 }
2369 }
2370 expr
2371}
2372
2373pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2376 let mut operators = Vec::new();
2377 peel_hir_expr_while(expr, |expr| match expr.kind {
2378 ExprKind::AddrOf(_, _, e) => {
2379 operators.push(expr);
2380 Some(e)
2381 },
2382 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2383 operators.push(expr);
2384 Some(e)
2385 },
2386 _ => None,
2387 });
2388 operators
2389}
2390
2391pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2392 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2393 && let Res::Def(_, def_id) = path.res
2394 {
2395 return find_attr!(cx.tcx, def_id, CfgTrace(..) | CfgAttrTrace);
2396 }
2397 false
2398}
2399
2400static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2401
2402fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2405 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2406 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2407 let value = map.entry(module);
2408 match value {
2409 Entry::Occupied(entry) => f(entry.get()),
2410 Entry::Vacant(entry) => {
2411 let mut names = Vec::new();
2412 for id in tcx.hir_module_free_items(module) {
2413 if matches!(tcx.def_kind(id.owner_id), DefKind::Const { .. })
2414 && let item = tcx.hir_item(id)
2415 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2416 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2417 && let Res::Def(DefKind::Struct, _) = path.res
2419 && find_attr!(tcx, item.hir_id(), RustcTestMarker(..))
2420 {
2421 names.push(ident.name);
2422 }
2423 }
2424 names.sort_unstable();
2425 f(entry.insert(names))
2426 },
2427 }
2428}
2429
2430pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2434 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2435 let node = tcx.hir_node(id);
2436 once((id, node))
2437 .chain(tcx.hir_parent_iter(id))
2438 .any(|(_id, node)| {
2441 if let Node::Item(item) = node
2442 && let ItemKind::Fn { ident, .. } = item.kind
2443 {
2444 return names.binary_search(&ident.name).is_ok();
2447 }
2448 false
2449 })
2450 })
2451}
2452
2453pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2460 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2461 if let Node::Item(item) = tcx.hir_node(id)
2462 && let ItemKind::Fn { ident, .. } = item.kind
2463 {
2464 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2465 names.binary_search(&ident.name).is_ok()
2466 })
2467 } else {
2468 false
2469 }
2470}
2471
2472pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2477 if let Some(cfgs) = find_attr!(tcx, id, CfgTrace(cfgs) => cfgs)
2478 && cfgs
2479 .iter()
2480 .any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. }))
2481 {
2482 true
2483 } else {
2484 false
2485 }
2486}
2487
2488pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2490 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2491}
2492
2493pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2495 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2496}
2497
2498pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2500 find_attr!(tcx, def_id, CfgTrace(..))
2501 || find_attr!(
2502 tcx.hir_parent_id_iter(tcx.local_def_id_to_hir_id(def_id))
2503 .flat_map(|parent_id| tcx.hir_attrs(parent_id)),
2504 CfgTrace(..)
2505 )
2506}
2507
2508#[derive(Clone, Copy)]
2510pub enum DefinedTy<'tcx> {
2511 Hir(&'tcx hir::Ty<'tcx>),
2513 Mir {
2521 def_site_def_id: Option<DefId>,
2522 ty: Binder<'tcx, Ty<'tcx>>,
2523 },
2524}
2525
2526pub struct ExprUseSite<'tcx> {
2528 pub node: Node<'tcx>,
2530 pub child_id: HirId,
2532 pub adjustments: &'tcx [Adjustment<'tcx>],
2534 pub is_ty_unified: bool,
2536 pub moved_before_use: bool,
2538 pub same_ctxt: bool,
2540}
2541impl<'tcx> ExprUseSite<'tcx> {
2542 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2543 match self.node {
2544 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2545 Node::ExprField(field) => ExprUseNode::Field(field),
2546
2547 Node::Item(&Item {
2548 kind: ItemKind::Static(..) | ItemKind::Const(..),
2549 owner_id,
2550 ..
2551 })
2552 | Node::TraitItem(&TraitItem {
2553 kind: TraitItemKind::Const(..),
2554 owner_id,
2555 ..
2556 })
2557 | Node::ImplItem(&ImplItem {
2558 kind: ImplItemKind::Const(..),
2559 owner_id,
2560 ..
2561 }) => ExprUseNode::ConstStatic(owner_id),
2562
2563 Node::Item(&Item {
2564 kind: ItemKind::Fn { .. },
2565 owner_id,
2566 ..
2567 })
2568 | Node::TraitItem(&TraitItem {
2569 kind: TraitItemKind::Fn(..),
2570 owner_id,
2571 ..
2572 })
2573 | Node::ImplItem(&ImplItem {
2574 kind: ImplItemKind::Fn(..),
2575 owner_id,
2576 ..
2577 }) => ExprUseNode::Return(owner_id),
2578
2579 Node::Expr(use_expr) => match use_expr.kind {
2580 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2581 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2582 }),
2583
2584 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2585 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2586 Some(i) => ExprUseNode::FnArg(func, i),
2587 None => ExprUseNode::Callee,
2588 },
2589 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2590 use_expr.hir_id,
2591 name.args,
2592 args.iter()
2593 .position(|arg| arg.hir_id == self.child_id)
2594 .map_or(0, |i| i + 1),
2595 ),
2596 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2597 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2598 _ => ExprUseNode::Other,
2599 },
2600 _ => ExprUseNode::Other,
2601 }
2602 }
2603}
2604
2605pub enum ExprUseNode<'tcx> {
2607 LetStmt(&'tcx LetStmt<'tcx>),
2609 ConstStatic(OwnerId),
2611 Return(OwnerId),
2613 Field(&'tcx ExprField<'tcx>),
2615 FnArg(&'tcx Expr<'tcx>, usize),
2617 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2619 Callee,
2621 FieldAccess(Ident),
2623 AddrOf(ast::BorrowKind, Mutability),
2625 Other,
2626}
2627impl<'tcx> ExprUseNode<'tcx> {
2628 pub fn is_return(&self) -> bool {
2630 matches!(self, Self::Return(_))
2631 }
2632
2633 pub fn is_recv(&self) -> bool {
2635 matches!(self, Self::MethodArg(_, _, 0))
2636 }
2637
2638 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2640 match *self {
2641 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2642 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2643 def_site_def_id: Some(id.def_id.to_def_id()),
2644 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity().skip_norm_wip()),
2645 }),
2646 Self::Return(id) => {
2647 if let Node::Expr(Expr {
2648 kind: ExprKind::Closure(c),
2649 ..
2650 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2651 {
2652 match c.fn_decl.output {
2653 FnRetTy::DefaultReturn(_) => None,
2654 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2655 }
2656 } else {
2657 let ty = cx.tcx.fn_sig(id).instantiate_identity().skip_norm_wip().output();
2658 Some(DefinedTy::Mir {
2659 def_site_def_id: Some(id.def_id.to_def_id()),
2660 ty,
2661 })
2662 }
2663 },
2664 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2665 Some(Expr {
2666 hir_id,
2667 kind: ExprKind::Struct(path, ..),
2668 ..
2669 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2670 .and_then(|(adt, variant)| {
2671 variant
2672 .fields
2673 .iter()
2674 .find(|f| f.name == field.ident.name)
2675 .map(|f| (adt, f))
2676 })
2677 .map(|(adt, field_def)| DefinedTy::Mir {
2678 def_site_def_id: Some(adt.did()),
2679 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity().skip_norm_wip()),
2680 }),
2681 _ => None,
2682 },
2683 Self::FnArg(callee, i) => {
2684 let sig = expr_sig(cx, callee)?;
2685 let (hir_ty, ty) = sig.input_with_hir(i)?;
2686 Some(match hir_ty {
2687 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2688 None => DefinedTy::Mir {
2689 def_site_def_id: sig.predicates_id(),
2690 ty,
2691 },
2692 })
2693 },
2694 Self::MethodArg(id, _, i) => {
2695 let id = cx.typeck_results().type_dependent_def_id(id)?;
2696 let sig = cx.tcx.fn_sig(id).skip_binder();
2697 Some(DefinedTy::Mir {
2698 def_site_def_id: Some(id),
2699 ty: sig.input(i),
2700 })
2701 },
2702 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2703 }
2704 }
2705}
2706
2707struct ReplacingFilterMap<I, F>(I, F);
2708impl<I, F, U> Iterator for ReplacingFilterMap<I, F>
2709where
2710 I: Iterator,
2711 F: FnMut(&mut I, I::Item) -> Option<U>,
2712{
2713 type Item = U;
2714 fn next(&mut self) -> Option<U> {
2715 while let Some(x) = self.0.next() {
2716 if let Some(x) = (self.1)(&mut self.0, x) {
2717 return Some(x);
2718 }
2719 }
2720 None
2721 }
2722}
2723
2724#[expect(clippy::too_many_lines)]
2727pub fn expr_use_sites<'tcx>(
2728 tcx: TyCtxt<'tcx>,
2729 typeck: &'tcx TypeckResults<'tcx>,
2730 mut ctxt: SyntaxContext,
2731 e: &'tcx Expr<'tcx>,
2732) -> impl Iterator<Item = ExprUseSite<'tcx>> {
2733 let mut adjustments: &[_] = typeck.expr_adjustments(e);
2734 let mut is_ty_unified = false;
2735 let mut moved_before_use = false;
2736 let mut same_ctxt = true;
2737 ReplacingFilterMap(
2738 hir_parent_with_src_iter(tcx, e.hir_id),
2739 move |iter: &mut _, (parent, child_id)| {
2740 let parent_ctxt;
2741 let mut parent_adjustments: &[_] = &[];
2742 match parent {
2743 Node::Expr(parent_expr) => {
2744 parent_ctxt = parent_expr.span.ctxt();
2745 same_ctxt &= parent_ctxt == ctxt;
2746 parent_adjustments = typeck.expr_adjustments(parent_expr);
2747 match parent_expr.kind {
2748 ExprKind::Match(scrutinee, arms, _) if scrutinee.hir_id != child_id => {
2749 is_ty_unified |= arms.len() != 1;
2750 moved_before_use = true;
2751 if adjustments.is_empty() {
2752 adjustments = parent_adjustments;
2753 }
2754 return None;
2755 },
2756 ExprKind::If(cond, _, else_) if cond.hir_id != child_id => {
2757 is_ty_unified |= else_.is_some();
2758 moved_before_use = true;
2759 if adjustments.is_empty() {
2760 adjustments = parent_adjustments;
2761 }
2762 return None;
2763 },
2764 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2765 is_ty_unified = true;
2766 moved_before_use = true;
2767 *iter = hir_parent_with_src_iter(tcx, id);
2768 if adjustments.is_empty() {
2769 adjustments = parent_adjustments;
2770 }
2771 return None;
2772 },
2773 ExprKind::Block(b, _) => {
2774 is_ty_unified |= b.targeted_by_break;
2775 moved_before_use = true;
2776 if adjustments.is_empty() {
2777 adjustments = parent_adjustments;
2778 }
2779 return None;
2780 },
2781 ExprKind::DropTemps(_) | ExprKind::Type(..) => {
2782 if adjustments.is_empty() {
2783 adjustments = parent_adjustments;
2784 }
2785 return None;
2786 },
2787 _ => {},
2788 }
2789 },
2790 Node::Arm(arm) => {
2791 parent_ctxt = arm.span.ctxt();
2792 same_ctxt &= parent_ctxt == ctxt;
2793 if arm.body.hir_id == child_id {
2794 return None;
2795 }
2796 },
2797 Node::Block(b) => {
2798 same_ctxt &= b.span.ctxt() == ctxt;
2799 return None;
2800 },
2801 Node::ConstBlock(_) => parent_ctxt = ctxt,
2802 Node::ExprField(&ExprField { span, .. }) => {
2803 parent_ctxt = span.ctxt();
2804 same_ctxt &= parent_ctxt == ctxt;
2805 },
2806 Node::AnonConst(&AnonConst { span, .. })
2807 | Node::ConstArg(&ConstArg { span, .. })
2808 | Node::Field(&FieldDef { span, .. })
2809 | Node::ImplItem(&ImplItem { span, .. })
2810 | Node::Item(&Item { span, .. })
2811 | Node::LetStmt(&LetStmt { span, .. })
2812 | Node::Stmt(&Stmt { span, .. })
2813 | Node::TraitItem(&TraitItem { span, .. })
2814 | Node::Variant(&Variant { span, .. }) => {
2815 parent_ctxt = span.ctxt();
2816 same_ctxt &= parent_ctxt == ctxt;
2817 *iter = hir_parent_with_src_iter(tcx, CRATE_HIR_ID);
2818 },
2819 Node::AssocItemConstraint(_)
2820 | Node::ConstArgExprField(_)
2821 | Node::Crate(_)
2822 | Node::Ctor(_)
2823 | Node::Err(_)
2824 | Node::ForeignItem(_)
2825 | Node::GenericParam(_)
2826 | Node::Infer(_)
2827 | Node::Lifetime(_)
2828 | Node::OpaqueTy(_)
2829 | Node::Param(_)
2830 | Node::Pat(_)
2831 | Node::PatExpr(_)
2832 | Node::PatField(_)
2833 | Node::PathSegment(_)
2834 | Node::PreciseCapturingNonLifetimeArg(_)
2835 | Node::Synthetic
2836 | Node::TraitRef(_)
2837 | Node::Ty(_)
2838 | Node::TyPat(_)
2839 | Node::WherePredicate(_) => {
2840 debug_assert!(false, "found {parent:?} which is after the final use node");
2843 return None;
2844 },
2845 }
2846
2847 ctxt = parent_ctxt;
2848 Some(ExprUseSite {
2849 node: parent,
2850 child_id,
2851 adjustments: mem::replace(&mut adjustments, parent_adjustments),
2852 is_ty_unified: mem::replace(&mut is_ty_unified, false),
2853 moved_before_use: mem::replace(&mut moved_before_use, false),
2854 same_ctxt: mem::replace(&mut same_ctxt, true),
2855 })
2856 },
2857 )
2858}
2859
2860pub fn get_expr_use_site<'tcx>(
2861 tcx: TyCtxt<'tcx>,
2862 typeck: &'tcx TypeckResults<'tcx>,
2863 ctxt: SyntaxContext,
2864 e: &'tcx Expr<'tcx>,
2865) -> ExprUseSite<'tcx> {
2866 expr_use_sites(tcx, typeck, ctxt, e).next().unwrap_or_else(|| {
2869 debug_assert!(false, "failed to find a use site for expr {e:?}");
2870 ExprUseSite {
2871 node: Node::Synthetic, child_id: CRATE_HIR_ID,
2873 adjustments: &[],
2874 is_ty_unified: false,
2875 moved_before_use: false,
2876 same_ctxt: false,
2877 }
2878 })
2879}
2880
2881pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2883 let mut pos = 0;
2884 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2885 let end = pos + t.len;
2886 let range = pos as usize..end as usize;
2887 let inner = InnerSpan::new(range.start, range.end);
2888 pos = end;
2889 (t.kind, s.get(range).unwrap_or_default(), inner)
2890 })
2891}
2892
2893pub fn span_contains_comment(cx: &impl source::HasSession, span: Span) -> bool {
2896 span.check_source_text(cx, |snippet| {
2897 tokenize(snippet, FrontmatterAllowed::No).any(|token| {
2898 matches!(
2899 token.kind,
2900 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2901 )
2902 })
2903 })
2904}
2905
2906pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2911 span.check_source_text(cx, |snippet| {
2912 tokenize_with_text(snippet).any(|(token, _, _)| match token {
2913 TokenKind::Whitespace => false,
2914 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2915 _ => true,
2916 })
2917 })
2918}
2919pub fn span_extract_comment(cx: &impl source::HasSession, span: Span) -> String {
2923 span_extract_comments(cx, span).join("\n")
2924}
2925
2926pub fn span_extract_comments(cx: &impl source::HasSession, span: Span) -> Vec<String> {
2930 span.with_source_text(cx, |snippet| {
2931 tokenize_with_text(snippet)
2932 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2933 .map(|(_, s, _)| s.to_string())
2934 .collect::<Vec<_>>()
2935 })
2936 .unwrap_or_default()
2937}
2938
2939pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2940 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2941}
2942
2943pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2968 cx: &LateContext<'_>,
2969 pat: &'a Pat<'hir>,
2970 else_body: &Expr<'_>,
2971) -> Option<&'a Pat<'hir>> {
2972 if let Some([inner_pat]) = as_some_pattern(cx, pat)
2973 && !is_refutable(cx, inner_pat)
2974 && let else_body = peel_blocks(else_body)
2975 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2976 && let ExprKind::Path(ret_path) = ret_val.kind
2977 && cx
2978 .qpath_res(&ret_path, ret_val.hir_id)
2979 .ctor_parent(cx)
2980 .is_lang_item(cx, OptionNone)
2981 {
2982 Some(inner_pat)
2983 } else {
2984 None
2985 }
2986}
2987
2988macro_rules! op_utils {
2989 ($($name:ident $assign:ident)*) => {
2990 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2992
2993 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2995
2996 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2998 match kind {
2999 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
3000 _ => None,
3001 }
3002 }
3003 };
3004}
3005
3006op_utils! {
3007 Add AddAssign
3008 Sub SubAssign
3009 Mul MulAssign
3010 Div DivAssign
3011 Rem RemAssign
3012 BitXor BitXorAssign
3013 BitAnd BitAndAssign
3014 BitOr BitOrAssign
3015 Shl ShlAssign
3016 Shr ShrAssign
3017}
3018
3019pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
3022 match *pat {
3023 PatKind::Wild => true,
3024 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
3025 !visitors::is_local_used(cx, body, id)
3026 },
3027 _ => false,
3028 }
3029}
3030
3031#[derive(Clone, Copy)]
3032pub enum RequiresSemi {
3033 Yes,
3034 No,
3035}
3036impl RequiresSemi {
3037 pub fn requires_semi(self) -> bool {
3038 matches!(self, Self::Yes)
3039 }
3040}
3041
3042#[expect(clippy::too_many_lines)]
3045pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
3046 struct BreakTarget {
3047 id: HirId,
3048 unused: bool,
3049 }
3050
3051 struct V<'cx, 'tcx> {
3052 cx: &'cx LateContext<'tcx>,
3053 break_targets: Vec<BreakTarget>,
3054 break_targets_for_result_ty: u32,
3055 in_final_expr: bool,
3056 requires_semi: bool,
3057 is_never: bool,
3058 }
3059
3060 impl V<'_, '_> {
3061 fn push_break_target(&mut self, id: HirId) {
3062 self.break_targets.push(BreakTarget { id, unused: true });
3063 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3064 }
3065 }
3066
3067 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3068 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3069 if self.is_never && self.break_targets.is_empty() {
3086 if self.in_final_expr && !self.requires_semi {
3087 match e.kind {
3090 ExprKind::DropTemps(e) => self.visit_expr(e),
3091 ExprKind::If(_, then, Some(else_)) => {
3092 self.visit_expr(then);
3093 self.visit_expr(else_);
3094 },
3095 ExprKind::Match(_, arms, _) => {
3096 for arm in arms {
3097 self.visit_expr(arm.body);
3098 }
3099 },
3100 ExprKind::Loop(b, ..) => {
3101 self.push_break_target(e.hir_id);
3102 self.in_final_expr = false;
3103 self.visit_block(b);
3104 self.break_targets.pop();
3105 },
3106 ExprKind::Block(b, _) => {
3107 if b.targeted_by_break {
3108 self.push_break_target(b.hir_id);
3109 self.visit_block(b);
3110 self.break_targets.pop();
3111 } else {
3112 self.visit_block(b);
3113 }
3114 },
3115 _ => {
3116 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3117 },
3118 }
3119 }
3120 return;
3121 }
3122 match e.kind {
3123 ExprKind::DropTemps(e) => self.visit_expr(e),
3124 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3125 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3126 self.in_final_expr = false;
3127 self.visit_expr(e);
3128 self.is_never = true;
3129 },
3130 ExprKind::Break(dest, e) => {
3131 if let Some(e) = e {
3132 self.in_final_expr = false;
3133 self.visit_expr(e);
3134 }
3135 if let Ok(id) = dest.target_id
3136 && let Some((i, target)) = self
3137 .break_targets
3138 .iter_mut()
3139 .enumerate()
3140 .find(|(_, target)| target.id == id)
3141 {
3142 target.unused &= self.is_never;
3143 if i < self.break_targets_for_result_ty as usize {
3144 self.requires_semi = true;
3145 }
3146 }
3147 self.is_never = true;
3148 },
3149 ExprKind::If(cond, then, else_) => {
3150 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3151 self.visit_expr(cond);
3152 self.in_final_expr = in_final_expr;
3153
3154 if self.is_never {
3155 self.visit_expr(then);
3156 if let Some(else_) = else_ {
3157 self.visit_expr(else_);
3158 }
3159 } else {
3160 self.visit_expr(then);
3161 let is_never = mem::replace(&mut self.is_never, false);
3162 if let Some(else_) = else_ {
3163 self.visit_expr(else_);
3164 self.is_never &= is_never;
3165 }
3166 }
3167 },
3168 ExprKind::Match(scrutinee, arms, _) => {
3169 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3170 self.visit_expr(scrutinee);
3171 self.in_final_expr = in_final_expr;
3172
3173 if self.is_never {
3174 for arm in arms {
3175 self.visit_arm(arm);
3176 }
3177 } else {
3178 let mut is_never = true;
3179 for arm in arms {
3180 self.is_never = false;
3181 if let Some(guard) = arm.guard {
3182 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3183 self.visit_expr(guard);
3184 self.in_final_expr = in_final_expr;
3185 self.is_never = false;
3187 }
3188 self.visit_expr(arm.body);
3189 is_never &= self.is_never;
3190 }
3191 self.is_never = is_never;
3192 }
3193 },
3194 ExprKind::Loop(b, _, _, _) => {
3195 self.push_break_target(e.hir_id);
3196 self.in_final_expr = false;
3197 self.visit_block(b);
3198 self.is_never = self.break_targets.pop().unwrap().unused;
3199 },
3200 ExprKind::Block(b, _) => {
3201 if b.targeted_by_break {
3202 self.push_break_target(b.hir_id);
3203 self.visit_block(b);
3204 self.is_never &= self.break_targets.pop().unwrap().unused;
3205 } else {
3206 self.visit_block(b);
3207 }
3208 },
3209 _ => {
3210 self.in_final_expr = false;
3211 walk_expr(self, e);
3212 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3213 },
3214 }
3215 }
3216
3217 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3218 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3219 for s in b.stmts {
3220 self.visit_stmt(s);
3221 }
3222 self.in_final_expr = in_final_expr;
3223 if let Some(e) = b.expr {
3224 self.visit_expr(e);
3225 }
3226 }
3227
3228 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3229 if let Some(e) = l.init {
3230 self.visit_expr(e);
3231 }
3232 if let Some(else_) = l.els {
3233 let is_never = self.is_never;
3234 self.visit_block(else_);
3235 self.is_never = is_never;
3236 }
3237 }
3238
3239 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3240 if let Some(guard) = arm.guard {
3241 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3242 self.visit_expr(guard);
3243 self.in_final_expr = in_final_expr;
3244 }
3245 self.visit_expr(arm.body);
3246 }
3247 }
3248
3249 if cx.typeck_results().expr_ty(e).is_never() {
3250 Some(RequiresSemi::No)
3251 } else if let ExprKind::Block(b, _) = e.kind
3252 && !b.targeted_by_break
3253 && b.expr.is_none()
3254 {
3255 None
3257 } else {
3258 let mut v = V {
3259 cx,
3260 break_targets: Vec::new(),
3261 break_targets_for_result_ty: 0,
3262 in_final_expr: true,
3263 requires_semi: false,
3264 is_never: false,
3265 };
3266 v.visit_expr(e);
3267 v.is_never
3268 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3269 RequiresSemi::Yes
3270 } else {
3271 RequiresSemi::No
3272 })
3273 }
3274}
3275
3276pub fn get_path_from_caller_to_method_type<'tcx>(
3282 tcx: TyCtxt<'tcx>,
3283 from: LocalDefId,
3284 method: DefId,
3285 args: GenericArgsRef<'tcx>,
3286) -> String {
3287 let assoc_item = tcx.associated_item(method);
3288 let def_id = assoc_item.container_id(tcx);
3289 match assoc_item.container {
3290 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3291 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3292 let ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
3293 get_path_to_ty(tcx, from, ty, args)
3294 },
3295 }
3296}
3297
3298fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3299 match ty.kind() {
3300 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3301 rustc_ty::Array(..)
3303 | rustc_ty::Dynamic(..)
3304 | rustc_ty::Never
3305 | rustc_ty::RawPtr(_, _)
3306 | rustc_ty::Ref(..)
3307 | rustc_ty::Slice(_)
3308 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args).skip_norm_wip()),
3309 _ => ty.to_string(),
3310 }
3311}
3312
3313fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3315 if callee.is_local() {
3317 let callee_path = tcx.def_path(callee);
3318 let caller_path = tcx.def_path(from.to_def_id());
3319 maybe_get_relative_path(&caller_path, &callee_path, 2)
3320 } else {
3321 tcx.def_path_str(callee)
3322 }
3323}
3324
3325fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3338 use itertools::EitherOrBoth::{Both, Left, Right};
3339
3340 let unique_parts = to
3342 .data
3343 .iter()
3344 .zip_longest(from.data.iter())
3345 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3346 .map(|el| match el {
3347 Both(l, r) => Both(l.data, r.data),
3348 Left(l) => Left(l.data),
3349 Right(r) => Right(r.data),
3350 });
3351
3352 let mut go_up_by = 0;
3354 let mut path = Vec::new();
3355 for el in unique_parts {
3356 match el {
3357 Both(l, r) => {
3358 if let DefPathData::TypeNs(sym) = l {
3368 path.push(sym);
3369 }
3370 if let DefPathData::TypeNs(_) = r {
3371 go_up_by += 1;
3372 }
3373 },
3374 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3379 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3384 _ => {},
3385 }
3386 }
3387
3388 if go_up_by > max_super {
3389 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3391 if let DefPathData::TypeNs(sym) = el.data {
3392 Some(sym)
3393 } else {
3394 None
3395 }
3396 })))
3397 } else if go_up_by == 0 && path.is_empty() {
3398 String::from("Self")
3399 } else {
3400 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3401 }
3402}
3403
3404pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3407 matches!(
3408 cx.tcx.parent_hir_node(id),
3409 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3410 )
3411}
3412
3413pub fn is_block_like(expr: &Expr<'_>) -> bool {
3416 matches!(
3417 expr.kind,
3418 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3419 )
3420}
3421
3422pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3424 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3425 match expr.kind {
3426 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3427 _ if is_block_like(expr) => is_operand,
3428 _ => false,
3429 }
3430 }
3431
3432 contains_block(expr, false)
3433}
3434
3435pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3437 if let Some(parent_expr) = get_parent_expr(cx, expr)
3438 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3439 && receiver.hir_id == expr.hir_id
3440 {
3441 return true;
3442 }
3443 false
3444}
3445
3446pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3449 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3450 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3451 && temporary_ty
3452 .walk()
3453 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3454 {
3455 ControlFlow::Break(())
3456 } else {
3457 ControlFlow::Continue(())
3458 }
3459 })
3460 .is_break()
3461}
3462
3463pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3474 let expr_ty_is_adjusted = cx
3475 .typeck_results()
3476 .expr_adjustments(expr)
3477 .iter()
3478 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3480 if expr_ty_is_adjusted {
3481 return true;
3482 }
3483
3484 match expr.kind {
3487 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3488 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip();
3489
3490 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3491 return false;
3492 }
3493
3494 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3495 let mut args_with_ty_param = {
3496 fn_sig
3497 .inputs()
3498 .skip_binder()
3499 .iter()
3500 .skip(self_arg_count)
3501 .zip(args)
3502 .filter_map(|(arg_ty, arg)| {
3503 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3504 Some(arg)
3505 } else {
3506 None
3507 }
3508 })
3509 };
3510 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3511 },
3512 ExprKind::Struct(qpath, _, _) => {
3514 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3515 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3516 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3517 return true;
3519 };
3520 v_def
3521 .fields
3522 .iter()
3523 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3524 } else {
3525 false
3526 }
3527 },
3528 ExprKind::Block(
3530 &Block {
3531 expr: Some(ret_expr), ..
3532 },
3533 _,
3534 )
3535 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3536
3537 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3539 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3541 ExprKind::If(_, then, maybe_else) => {
3543 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3544 },
3545 ExprKind::Match(_, arms, _) => arms
3546 .iter()
3547 .map(|arm| arm.body)
3548 .any(|body| expr_requires_coercion(cx, body)),
3549 _ => false,
3550 }
3551}
3552
3553pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3556 if let Some(hir_id) = expr.res_local_id()
3557 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3558 {
3559 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3560 } else if let ExprKind::Path(p) = &expr.kind
3561 && let Some(mutability) = cx
3562 .qpath_res(p, expr.hir_id)
3563 .opt_def_id()
3564 .and_then(|id| cx.tcx.static_mutability(id))
3565 {
3566 mutability == Mutability::Mut
3567 } else if let ExprKind::Field(parent, _) = expr.kind {
3568 is_mutable(cx, parent)
3569 } else {
3570 true
3571 }
3572}
3573
3574pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3577 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3578 return hir_ty;
3579 };
3580 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3581 && let Some(segment) = path.segments.last()
3582 && segment.ident.name == sym::Option
3583 && let Res::Def(DefKind::Enum, def_id) = segment.res
3584 && def_id == option_def_id
3585 && let [GenericArg::Type(arg_ty)] = segment.args().args
3586 {
3587 hir_ty = arg_ty.as_unambig_ty();
3588 }
3589 hir_ty
3590}
3591
3592pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3595 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3596 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3597 && let ctxt = expr.span.ctxt()
3598 && for_each_expr_without_closures(into_future_arg, |e| {
3599 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3600 })
3601 .is_none()
3602 {
3603 Some(into_future_arg)
3604 } else {
3605 None
3606 }
3607}
3608
3609pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3611 if let ExprKind::Call(fn_expr, []) = &expr.kind
3612 && let ExprKind::Path(qpath) = &fn_expr.kind
3613 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3614 {
3615 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3616 } else {
3617 false
3618 }
3619}
3620
3621pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3638 let enclosing_body_owner = cx
3639 .tcx
3640 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3641 let mut prev_id = expr.hir_id;
3642 let mut skip_until_id = None;
3643 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3644 if hir_id == enclosing_body_owner {
3645 return true;
3646 }
3647 if let Some(id) = skip_until_id {
3648 prev_id = hir_id;
3649 if id == hir_id {
3650 skip_until_id = None;
3651 }
3652 continue;
3653 }
3654 match node {
3655 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3656 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3657 Node::Expr(expr) => match expr.kind {
3658 ExprKind::Ret(_) => return true,
3659 ExprKind::If(_, then, opt_else)
3660 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3661 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3662 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3663 ExprKind::Break(
3664 Destination {
3665 target_id: Ok(target_id),
3666 ..
3667 },
3668 _,
3669 ) => skip_until_id = Some(target_id),
3670 _ => break,
3671 },
3672 _ => break,
3673 }
3674 prev_id = hir_id;
3675 }
3676
3677 false
3680}
3681
3682pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3685 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3686 matches!(
3687 adj.kind,
3688 Adjust::Deref(DerefAdjustKind::Overloaded(_))
3689 | Adjust::Pointer(PointerCoercion::Unsize)
3690 | Adjust::NeverToAny
3691 )
3692 })
3693}
3694
3695pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3697 matches!(
3698 expr.kind,
3699 ExprKind::Closure(Closure {
3700 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3701 CoroutineDesugaring::Async,
3702 CoroutineSource::Block
3703 )),
3704 ..
3705 })
3706 )
3707}
3708
3709pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3711 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3712}
3713
3714#[inline]
3717pub fn hir_parent_with_src_iter(tcx: TyCtxt<'_>, mut id: HirId) -> impl Iterator<Item = (Node<'_>, HirId)> {
3718 tcx.hir_parent_id_iter(id)
3719 .map(move |parent| (tcx.hir_node(parent), mem::replace(&mut id, parent)))
3720}