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