1#![feature(box_patterns)]
2#![feature(macro_metavar_expr)]
3#![feature(rustc_private)]
4#![feature(unwrap_infallible)]
5#![recursion_limit = "512"]
6#![expect(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
7#![warn(
8 rust_2018_idioms,
9 trivial_casts,
10 trivial_numeric_casts,
11 unused_lifetimes,
12 unused_qualifications,
13 rustc::internal
14)]
15
16extern crate rustc_abi;
19extern crate rustc_ast;
20extern crate rustc_attr_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, OnceLock};
79
80use itertools::Itertools as _;
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, LocalModId};
92use rustc_hir::definitions::{DefPath, DefPathData};
93use rustc_hir::intravisit::{Visitor, walk_expr};
94use rustc_hir::{
95 self as hir, AnonConst, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, CRATE_HIR_ID, Closure, ConstArg,
96 ConstArgKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind,
97 FieldDef, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, HirIdSet, Impl, ImplItem, ImplItemKind, Item,
98 ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind,
99 PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
100 Variant, def, find_attr,
101};
102use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
103use rustc_lint::{LateContext, Level, Lint, LintContext as _};
104use rustc_middle::hir::nested_filter;
105use rustc_middle::hir::place::PlaceBase;
106use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
107use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion};
108use rustc_middle::ty::layout::IntegerExt as _;
109use rustc_middle::ty::{
110 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
111 TypeFlags, TypeVisitableExt as _, TypeckResults, UintTy, UpvarCapture,
112};
113use rustc_span::hygiene::{ExpnKind, MacroKind};
114use rustc_span::source_map::SourceMap;
115use rustc_span::symbol::{Ident, Symbol, kw};
116use rustc_span::{InnerSpan, Span, SyntaxContext};
117use source::{SpanExt as _, walk_span_to_context};
118use visitors::{Visitable, for_each_unconsumed_temporary};
119
120use crate::ast_utils::unordered_over;
121use crate::higher::Range;
122use crate::msrvs::Msrv;
123use crate::res::{MaybeDef as _, MaybeResPath as _};
124use crate::source::HasSourceMap;
125use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
126use crate::visitors::for_each_expr_without_closures;
127
128pub const VEC_METHODS_SHADOWING_SLICE_METHODS: [Symbol; 3] = [sym::as_ptr, sym::is_empty, sym::len];
130
131#[macro_export]
132macro_rules! extract_msrv_attr {
133 () => {
134 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
135 let sess = rustc_lint::LintContext::sess(cx);
136 self.msrv.check_attributes(sess, attrs);
137 }
138
139 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
140 let sess = rustc_lint::LintContext::sess(cx);
141 self.msrv.check_attributes_post(sess, attrs);
142 }
143 };
144}
145
146pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
169 while let Some(init) = expr
170 .res_local_id()
171 .and_then(|id| find_binding_init(cx, id))
172 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
173 {
174 expr = init;
175 }
176 expr
177}
178
179pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
188 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
189 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
190 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
191 {
192 return local.init;
193 }
194 None
195}
196
197pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
201 for (_, node) in cx.tcx.hir_parent_iter(local) {
202 match node {
203 Node::Pat(..) | Node::PatField(..) => {},
204 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
205 _ => return true,
206 }
207 }
208
209 false
210}
211
212pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
223 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
224 cx.enclosing_body.is_some_and(|id| {
225 cx.tcx
226 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
227 .is_some()
228 })
229}
230
231pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
238 use rustc_hir::ConstContext::{Const, ConstFn, Static};
239 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
240 return false;
241 };
242 match ctx {
243 ConstFn => false,
244 Static(_)
245 | Const {
246 allow_const_fn_promotion: _,
247 } => true,
248 }
249}
250
251pub fn is_enum_variant_ctor(
253 cx: &LateContext<'_>,
254 enum_item: Symbol,
255 variant_name: Symbol,
256 ctor_call_id: DefId,
257) -> bool {
258 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
259 return false;
260 };
261
262 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
263 variants
264 .filter(|variant| variant.name == variant_name)
265 .filter_map(|variant| variant.ctor.as_ref())
266 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
267}
268
269pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
271 let did = match cx.tcx.def_kind(did) {
272 DefKind::Ctor(..) => cx.tcx.parent(did),
273 DefKind::Variant => match cx.tcx.opt_parent(did) {
275 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
276 _ => did,
277 },
278 _ => did,
279 };
280
281 cx.tcx.is_diagnostic_item(item, did)
282}
283
284pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
286 let did = match cx.tcx.def_kind(did) {
287 DefKind::Ctor(..) => cx.tcx.parent(did),
288 DefKind::Variant => match cx.tcx.opt_parent(did) {
290 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
291 _ => did,
292 },
293 _ => did,
294 };
295
296 cx.tcx.lang_items().get(item) == Some(did)
297}
298
299pub fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
301 expr.basic_res().ctor_parent(cx).is_lang_item(cx, OptionNone)
302}
303
304pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
306 if let ExprKind::Call(e, [arg]) = expr.kind
307 && e.basic_res().ctor_parent(cx).is_lang_item(cx, OptionSome)
308 {
309 Some(arg)
310 } else {
311 None
312 }
313}
314
315pub fn is_empty_block(expr: &Expr<'_>) -> bool {
317 matches!(
318 expr.kind,
319 ExprKind::Block(
320 Block {
321 stmts: [],
322 expr: None,
323 ..
324 },
325 _,
326 )
327 )
328}
329
330pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
332 matches!(
333 expr.kind,
334 ExprKind::Block(
335 Block {
336 stmts: [],
337 expr: None,
338 ..
339 },
340 _
341 ) | ExprKind::Tup([])
342 )
343}
344
345pub fn is_wild(pat: &Pat<'_>) -> bool {
347 matches!(pat.kind, PatKind::Wild)
348}
349
350pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> {
357 if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind
358 && cx
359 .qpath_res(qpath, pat.hir_id)
360 .ctor_parent(cx)
361 .is_lang_item(cx, OptionSome)
362 {
363 Some(inner)
364 } else {
365 None
366 }
367}
368
369pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
371 matches!(pat.kind,
372 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
373 if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone))
374}
375
376pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
378 is_none_pattern(cx, arm.pat)
379 && matches!(
380 peel_blocks(arm.body).kind,
381 ExprKind::Path(qpath)
382 if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)
383 )
384}
385
386pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
388 match *qpath {
389 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
390 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
391 QPath::TypeRelative(..) => false,
392 }
393}
394
395pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
397 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
398 && let ItemKind::Impl(imp) = item.kind
399 {
400 imp.of_trait.is_some()
401 } else {
402 false
403 }
404}
405
406pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
407 match *path {
408 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
409 QPath::TypeRelative(_, seg) => seg,
410 }
411}
412
413pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
414 last_path_segment(qpath)
415 .args
416 .map_or(&[][..], |a| a.args)
417 .iter()
418 .filter_map(|a| match a {
419 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
420 _ => None,
421 })
422}
423
424pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
429 match expr.kind {
430 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
431 ExprKind::Path(QPath::Resolved(
432 _,
433 Path {
434 res: Res::Local(local), ..
435 },
436 )) => Some(*local),
437 _ => None,
438 }
439}
440
441pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
457 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
458 && let ItemKind::Impl(impl_) = &item.kind
459 && let Some(of_trait) = impl_.of_trait
460 {
461 return Some(&of_trait.trait_ref);
462 }
463 None
464}
465
466fn projection_stack<'a, 'hir>(
474 mut e: &'a Expr<'hir>,
475 ctxt: SyntaxContext,
476) -> Option<(Vec<&'a Expr<'hir>>, &'a Expr<'hir>)> {
477 let mut result = vec![];
478 let root = loop {
479 match e.kind {
480 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) if e.span.ctxt() == ctxt => {
481 result.push(e);
482 e = ep;
483 },
484 ExprKind::Index(..) | ExprKind::Field(..) => return None,
485 _ => break e,
486 }
487 };
488 result.reverse();
489 Some((result, root))
490}
491
492pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
494 cx.typeck_results()
495 .expr_adjustments(e)
496 .iter()
497 .find_map(|a| match a.kind {
498 Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)),
499 Adjust::Deref(DerefAdjustKind::Builtin) => None,
500 _ => Some(None),
501 })
502 .and_then(|x| x)
503}
504
505pub fn can_mut_borrow_both(cx: &LateContext<'_>, ctxt: SyntaxContext, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
508 let Some((s1, r1)) = projection_stack(e1, ctxt) else {
509 return false;
510 };
511 let Some((s2, r2)) = projection_stack(e2, ctxt) else {
512 return false;
513 };
514 if !eq_expr_value(cx, ctxt, r1, r2) {
515 return true;
516 }
517 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
518 return false;
519 }
520
521 for (x1, x2) in zip(&s1, &s2) {
522 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
523 return false;
524 }
525
526 match (&x1.kind, &x2.kind) {
527 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
528 if i1 != i2 {
529 return true;
530 }
531 },
532 _ => return false,
533 }
534 }
535 false
536}
537
538fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
541 let std_types_symbols = &[
542 sym::Vec,
543 sym::VecDeque,
544 sym::LinkedList,
545 sym::HashMap,
546 sym::BTreeMap,
547 sym::HashSet,
548 sym::BTreeSet,
549 sym::BinaryHeap,
550 ];
551
552 if let QPath::TypeRelative(_, method) = path
553 && method.ident.name == sym::new
554 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
555 && let Some(adt) = cx
556 .tcx
557 .type_of(impl_did)
558 .instantiate_identity()
559 .skip_norm_wip()
560 .ty_adt_def()
561 {
562 return Some(adt.did()) == cx.tcx.lang_items().string()
563 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
564 }
565 false
566}
567
568pub fn is_default_equivalent_call(
570 cx: &LateContext<'_>,
571 repl_func: &Expr<'_>,
572 whole_call_expr: Option<&Expr<'_>>,
573) -> bool {
574 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
575 && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx)
576 && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default)
577 || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath))
578 {
579 return true;
580 }
581
582 let Some(e) = whole_call_expr else { return false };
585 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
586 return false;
587 };
588 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
589 return false;
590 };
591 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
592 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
593 cx.tcx.lifetimes.re_erased.into()
594 } else if param.index == 0 && param.name == kw::SelfUpper {
595 ty.into()
596 } else {
597 param.to_error(cx.tcx)
598 }
599 });
600 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
601
602 let Ok(Some(instance)) = instance else { return false };
603 if let rustc_ty::InstanceKind::Item(def) = instance.def
604 && !cx.tcx.is_mir_available(def)
605 {
606 return false;
607 }
608 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
609 return false;
610 };
611 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
612 return false;
613 };
614
615 let body = cx.tcx.instance_mir(instance.def);
621 for block_data in body.basic_blocks.iter() {
622 if block_data.statements.len() == 1
623 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
624 && assign.0.local == RETURN_PLACE
625 && let Rvalue::Aggregate(kind, _places) = &assign.1
626 && let AggregateKind::Adt(did, variant_index, _, _, _) = **kind
627 && let def = cx.tcx.adt_def(did)
628 && let variant = &def.variant(variant_index)
629 && variant.fields.is_empty()
630 && let Some((_, did)) = variant.ctor
631 && did == repl_def_id
632 {
633 return true;
634 } else if block_data.statements.is_empty()
635 && let Some(term) = &block_data.terminator
636 {
637 match &term.kind {
638 TerminatorKind::Call {
639 func: Operand::Constant(c),
640 ..
641 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
642 && *did == repl_def_id =>
643 {
644 return true;
645 },
646 TerminatorKind::TailCall {
647 func: Operand::Constant(c),
648 ..
649 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
650 && *did == repl_def_id =>
651 {
652 return true;
653 },
654 _ => {},
655 }
656 }
657 }
658 false
659}
660
661pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
665 match &e.kind {
666 ExprKind::Lit(lit) => match lit.node {
667 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
668 LitKind::Str(s, _) => s.is_empty(),
669 _ => false,
670 },
671 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
672 ExprKind::Repeat(x, len) => {
673 if let ConstArgKind::Anon(anon_const) = len.kind
674 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
675 && let LitKind::Int(v, _) = const_lit.node
676 && v <= 32
677 && is_default_equivalent(cx, x)
678 {
679 true
680 } else {
681 false
682 }
683 },
684 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
685 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
686 ExprKind::Path(qpath) => cx
687 .qpath_res(qpath, e.hir_id)
688 .ctor_parent(cx)
689 .is_lang_item(cx, OptionNone),
690 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
691 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
692 _ => false,
693 }
694}
695
696fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
697 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
698 && seg.ident.name == sym::from
699 {
700 match arg.kind {
701 ExprKind::Lit(hir::Lit {
702 node: LitKind::Str(sym, _),
703 ..
704 }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String),
705 ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec),
706 ExprKind::Repeat(_, len) => {
707 if let ConstArgKind::Anon(anon_const) = len.kind
708 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
709 && let LitKind::Int(v, _) = const_lit.node
710 {
711 return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec);
712 }
713 },
714 _ => (),
715 }
716 }
717 false
718}
719
720pub fn can_move_expr_to_closure_no_visit<'tcx>(
752 cx: &LateContext<'tcx>,
753 expr: &'tcx Expr<'_>,
754 loop_ids: &[HirId],
755 ignore_locals: &HirIdSet,
756) -> bool {
757 match expr.kind {
758 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
759 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
760 if loop_ids.contains(&id) =>
761 {
762 true
763 },
764 ExprKind::Break(..)
765 | ExprKind::Continue(_)
766 | ExprKind::Ret(_)
767 | ExprKind::Yield(..)
768 | ExprKind::InlineAsm(_) => false,
769 ExprKind::Field(
772 &Expr {
773 hir_id,
774 kind:
775 ExprKind::Path(QPath::Resolved(
776 _,
777 Path {
778 res: Res::Local(local_id),
779 ..
780 },
781 )),
782 ..
783 },
784 _,
785 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
786 false
788 },
789 _ => true,
790 }
791}
792
793#[derive(Debug, Clone, Copy, PartialEq, Eq)]
795pub enum CaptureKind {
796 Value,
797 Use,
798 Ref(Mutability),
799}
800impl CaptureKind {
801 pub fn is_imm_ref(self) -> bool {
802 self == Self::Ref(Mutability::Not)
803 }
804}
805impl std::ops::BitOr for CaptureKind {
806 type Output = Self;
807 fn bitor(self, rhs: Self) -> Self::Output {
808 match (self, rhs) {
809 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
810 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
811 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
812 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
813 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
814 }
815 }
816}
817impl std::ops::BitOrAssign for CaptureKind {
818 fn bitor_assign(&mut self, rhs: Self) {
819 *self = *self | rhs;
820 }
821}
822
823pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
829 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
830 let mut capture = CaptureKind::Ref(Mutability::Not);
831 pat.each_binding_or_first(&mut |_, id, span, _| match cx
832 .typeck_results()
833 .extract_binding_mode(cx.sess(), id, span)
834 .0
835 {
836 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
837 capture = CaptureKind::Value;
838 },
839 ByRef::Yes(_, Mutability::Mut) if capture != CaptureKind::Value => {
840 capture = CaptureKind::Ref(Mutability::Mut);
841 },
842 _ => (),
843 });
844 capture
845 }
846
847 debug_assert!(matches!(
848 e.kind,
849 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
850 ));
851
852 let mut capture = CaptureKind::Value;
853 let mut capture_expr_ty = e;
854
855 for (parent, child_id) in hir_parent_with_src_iter(cx.tcx, e.hir_id) {
856 if let [
857 Adjustment {
858 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
859 target,
860 },
861 ref adjust @ ..,
862 ] = *cx
863 .typeck_results()
864 .adjustments()
865 .get(child_id)
866 .map_or(&[][..], |x| &**x)
867 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
868 *adjust.last().map_or(target, |a| a.target).kind()
869 {
870 return CaptureKind::Ref(mutability);
871 }
872
873 match parent {
874 Node::Expr(e) => match e.kind {
875 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
876 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
877 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
878 return CaptureKind::Ref(Mutability::Mut);
879 },
880 ExprKind::Field(..) => {
881 if capture == CaptureKind::Value {
882 capture_expr_ty = e;
883 }
884 },
885 ExprKind::Let(let_expr) => {
886 let mutability = match pat_capture_kind(cx, let_expr.pat) {
887 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
888 CaptureKind::Ref(m) => m,
889 };
890 return CaptureKind::Ref(mutability);
891 },
892 ExprKind::Match(_, arms, _) => {
893 let mut mutability = Mutability::Not;
894 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
895 match capture {
896 CaptureKind::Value | CaptureKind::Use => break,
897 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
898 CaptureKind::Ref(Mutability::Not) => (),
899 }
900 }
901 return CaptureKind::Ref(mutability);
902 },
903 _ => break,
904 },
905 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
906 CaptureKind::Value | CaptureKind::Use => break,
907 capture @ CaptureKind::Ref(_) => return capture,
908 },
909 _ => break,
910 }
911 }
912
913 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
914 CaptureKind::Ref(Mutability::Not)
916 } else {
917 capture
918 }
919}
920
921pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
924 struct V<'cx, 'tcx> {
925 cx: &'cx LateContext<'tcx>,
926 loops: Vec<HirId>,
928 locals: HirIdSet,
930 allow_closure: bool,
932 captures: HirIdMap<CaptureKind>,
935 }
936 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
937 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
938 if !self.allow_closure {
939 return;
940 }
941
942 match e.kind {
943 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
944 if !self.locals.contains(&l) {
945 let cap = capture_local_usage(self.cx, e);
946 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
947 }
948 },
949 ExprKind::Closure(closure) => {
950 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
951 let local_id = match capture.place.base {
952 PlaceBase::Local(id) => id,
953 PlaceBase::Upvar(var) => var.var_path.hir_id,
954 _ => continue,
955 };
956 if !self.locals.contains(&local_id) {
957 let capture = match capture.info.capture_kind {
958 UpvarCapture::ByValue => CaptureKind::Value,
959 UpvarCapture::ByUse => CaptureKind::Use,
960 UpvarCapture::ByRef(kind) => match kind {
961 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
962 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
963 CaptureKind::Ref(Mutability::Mut)
964 },
965 },
966 };
967 self.captures
968 .entry(local_id)
969 .and_modify(|e| *e |= capture)
970 .or_insert(capture);
971 }
972 }
973 },
974 ExprKind::Loop(b, ..) => {
975 self.loops.push(e.hir_id);
976 self.visit_block(b);
977 self.loops.pop();
978 },
979 _ => {
980 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
981 walk_expr(self, e);
982 },
983 }
984 }
985
986 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
987 p.each_binding_or_first(&mut |_, id, _, _| {
988 self.locals.insert(id);
989 });
990 }
991 }
992
993 let mut v = V {
994 cx,
995 loops: Vec::new(),
996 locals: HirIdSet::default(),
997 allow_closure: true,
998 captures: HirIdMap::default(),
999 };
1000 v.visit_expr(expr);
1001 v.allow_closure.then_some(v.captures)
1002}
1003
1004pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1006
1007pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1010 let mut method_names = Vec::with_capacity(max_depth);
1011 let mut arg_lists = Vec::with_capacity(max_depth);
1012 let mut spans = Vec::with_capacity(max_depth);
1013
1014 let mut current = expr;
1015 for _ in 0..max_depth {
1016 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1017 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1018 break;
1019 }
1020 method_names.push(path.ident.name);
1021 arg_lists.push((*receiver, &**args));
1022 spans.push(path.ident.span);
1023 current = receiver;
1024 } else {
1025 break;
1026 }
1027 }
1028
1029 (method_names, arg_lists, spans)
1030}
1031
1032pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1039 let mut current = expr;
1040 let mut matched = Vec::with_capacity(methods.len());
1041 for method_name in methods.iter().rev() {
1042 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1044 if path.ident.name == *method_name {
1045 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1046 return None;
1047 }
1048 matched.push((receiver, args)); current = receiver; } else {
1051 return None;
1052 }
1053 } else {
1054 return None;
1055 }
1056 }
1057 matched.reverse();
1059 Some(matched)
1060}
1061
1062pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1064 cx.tcx
1065 .entry_fn(())
1066 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1067}
1068
1069pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1071 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1072 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1073}
1074
1075pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1077 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1078 match cx.tcx.hir_node_by_def_id(parent_id) {
1079 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1080 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1081 _ => None,
1082 }
1083}
1084
1085pub struct ContainsName<'a, 'tcx> {
1086 pub cx: &'a LateContext<'tcx>,
1087 pub name: Symbol,
1088}
1089
1090impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1091 type Result = ControlFlow<()>;
1092 type NestedFilter = nested_filter::OnlyBodies;
1093
1094 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1095 if self.name == name {
1096 ControlFlow::Break(())
1097 } else {
1098 ControlFlow::Continue(())
1099 }
1100 }
1101
1102 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1103 self.cx.tcx
1104 }
1105}
1106
1107pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1109 let mut cn = ContainsName { cx, name };
1110 cn.visit_expr(expr).is_break()
1111}
1112
1113pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1115 for_each_expr_without_closures(expr, |e| {
1116 if matches!(e.kind, ExprKind::Ret(..)) {
1117 ControlFlow::Break(())
1118 } else {
1119 ControlFlow::Continue(())
1120 }
1121 })
1122 .is_some()
1123}
1124
1125pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1127 get_parent_expr_for_hir(cx, e.hir_id)
1128}
1129
1130pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1133 match cx.tcx.parent_hir_node(hir_id) {
1134 Node::Expr(parent) => Some(parent),
1135 _ => None,
1136 }
1137}
1138
1139pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1141 let enclosing_node = cx
1142 .tcx
1143 .hir_get_enclosing_scope(hir_id)
1144 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1145 enclosing_node.and_then(|node| match node {
1146 Node::Block(block) => Some(block),
1147 Node::Item(&Item {
1148 kind: ItemKind::Fn { body: eid, .. },
1149 ..
1150 })
1151 | Node::ImplItem(&ImplItem {
1152 kind: ImplItemKind::Fn(_, eid),
1153 ..
1154 })
1155 | Node::TraitItem(&TraitItem {
1156 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1157 ..
1158 }) => match cx.tcx.hir_body(eid).value.kind {
1159 ExprKind::Block(block, _) => Some(block),
1160 _ => None,
1161 },
1162 _ => None,
1163 })
1164}
1165
1166pub fn get_enclosing_closure<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Closure<'tcx>> {
1168 cx.tcx.hir_parent_iter(hir_id).find_map(|(_, node)| {
1169 if let Node::Expr(expr) = node
1170 && let ExprKind::Closure(closure) = expr.kind
1171 {
1172 Some(closure)
1173 } else {
1174 None
1175 }
1176 })
1177}
1178
1179pub fn is_upvar_in_closure(cx: &LateContext<'_>, closure: &Closure<'_>, local_id: HirId) -> bool {
1181 cx.typeck_results()
1182 .closure_min_captures
1183 .get(&closure.def_id)
1184 .is_some_and(|x| x.contains_key(&local_id))
1185}
1186
1187pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1189 cx: &LateContext<'tcx>,
1190 expr: &Expr<'_>,
1191) -> Option<&'tcx Expr<'tcx>> {
1192 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1193 match node {
1194 Node::Expr(e) => match e.kind {
1195 ExprKind::Closure { .. }
1196 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1197 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1198
1199 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1201 _ => (),
1202 },
1203 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1204 _ => break,
1205 }
1206 }
1207 None
1208}
1209
1210pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1212 match tcx.hir_parent_iter(id).next() {
1213 Some((
1214 _,
1215 Node::Item(Item {
1216 kind: ItemKind::Impl(imp),
1217 ..
1218 }),
1219 )) => Some(imp),
1220 _ => None,
1221 }
1222}
1223
1224pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1235 while let ExprKind::Block(
1236 Block {
1237 stmts: [],
1238 expr: Some(inner),
1239 rules: BlockCheckMode::DefaultBlock,
1240 ..
1241 },
1242 _,
1243 ) = expr.kind
1244 {
1245 expr = inner;
1246 }
1247 expr
1248}
1249
1250pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1261 while let ExprKind::Block(
1262 Block {
1263 stmts: [],
1264 expr: Some(inner),
1265 rules: BlockCheckMode::DefaultBlock,
1266 ..
1267 }
1268 | Block {
1269 stmts:
1270 [
1271 Stmt {
1272 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1273 ..
1274 },
1275 ],
1276 expr: None,
1277 rules: BlockCheckMode::DefaultBlock,
1278 ..
1279 },
1280 _,
1281 ) = expr.kind
1282 {
1283 expr = inner;
1284 }
1285 expr
1286}
1287
1288pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1290 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1291 match iter.next() {
1292 Some((
1293 _,
1294 Node::Expr(Expr {
1295 kind: ExprKind::If(_, _, Some(else_expr)),
1296 ..
1297 }),
1298 )) => else_expr.hir_id == expr.hir_id,
1299 _ => false,
1300 }
1301}
1302
1303pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1306 hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {
1307 matches!(
1308 node,
1309 Node::LetStmt(LetStmt {
1310 init: Some(init),
1311 els: Some(els),
1312 ..
1313 })
1314 if init.hir_id == child_id || els.hir_id == child_id
1315 )
1316 })
1317}
1318
1319pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1321 hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {
1322 matches!(
1323 node,
1324 Node::LetStmt(LetStmt { els: Some(els), .. })
1325 if els.hir_id == child_id
1326 )
1327 })
1328}
1329
1330pub fn is_full_collection_range(cx: &LateContext<'_>, container: Option<HirId>, expr: &Expr<'_>) -> bool {
1332 if let Some(Range { start, end, ty, .. }) = Range::hir(cx, expr) {
1333 start.is_none_or(|start| is_integer_literal(start, 0))
1334 && end.is_none_or(|end| {
1335 if ty.limits() == RangeLimits::HalfOpen
1336 && let Some(container) = container
1337 && let ExprKind::MethodCall(seg, recv, [], _) = end.kind
1338 {
1339 seg.ident.name == sym::len && recv.res_local_id() == Some(container)
1340 } else {
1341 false
1342 }
1343 })
1344 } else {
1345 false
1346 }
1347}
1348
1349pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1351 if let ExprKind::Lit(spanned) = expr.kind
1352 && let LitKind::Int(v, _) = spanned.node
1353 {
1354 return v == value;
1355 }
1356 false
1357}
1358
1359pub fn is_integer_literal_untyped(expr: &Expr<'_>) -> bool {
1361 if let ExprKind::Lit(spanned) = expr.kind
1362 && let LitKind::Int(_, suffix) = spanned.node
1363 {
1364 return suffix == LitIntType::Unsuffixed;
1365 }
1366
1367 false
1368}
1369
1370pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1372 if let ExprKind::Lit(spanned) = expr.kind
1373 && let LitKind::Float(v, _) = spanned.node
1374 {
1375 v.as_str().parse() == Ok(value)
1376 } else {
1377 false
1378 }
1379}
1380
1381pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1389 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1390}
1391
1392#[must_use]
1396pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1397 loop {
1398 if span.from_expansion() {
1399 let data = span.ctxt().outer_expn_data();
1400 let new_span = data.call_site;
1401
1402 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1403 && mac_name == name
1404 {
1405 return Some(new_span);
1406 }
1407
1408 span = new_span;
1409 } else {
1410 return None;
1411 }
1412 }
1413}
1414
1415#[must_use]
1426pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1427 if span.from_expansion() {
1428 let data = span.ctxt().outer_expn_data();
1429 let new_span = data.call_site;
1430
1431 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1432 && mac_name == name
1433 {
1434 return Some(new_span);
1435 }
1436 }
1437
1438 None
1439}
1440
1441pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1443 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().skip_norm_wip().output();
1444 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1445}
1446
1447pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1449 let arg = cx
1450 .tcx
1451 .fn_sig(fn_def_id)
1452 .instantiate_identity()
1453 .skip_norm_wip()
1454 .input(nth);
1455 cx.tcx.instantiate_bound_regions_with_erased(arg)
1456}
1457
1458pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1460 if let ExprKind::Call(fun, _) = expr.kind
1461 && let ExprKind::Path(ref qp) = fun.kind
1462 {
1463 let res = cx.qpath_res(qp, fun.hir_id);
1464 return match res {
1465 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1466 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1467 _ => false,
1468 };
1469 }
1470 false
1471}
1472
1473pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1476 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1477 !matches!(
1478 cx.qpath_res(qpath, id),
1479 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1480 )
1481 }
1482
1483 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1484 i.into_iter().any(|pat| is_refutable(cx, pat))
1485 }
1486
1487 match pat.kind {
1488 PatKind::Missing => unreachable!(),
1489 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1491 PatKind::Box(pat) | PatKind::Ref(pat, _, _) => is_refutable(cx, pat),
1492 PatKind::Expr(PatExpr {
1493 kind: PatExprKind::Path(qpath),
1494 hir_id,
1495 ..
1496 }) => is_qpath_refutable(cx, qpath, *hir_id),
1497 PatKind::Or(pats) => {
1498 are_refutable(cx, pats)
1500 },
1501 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1502 PatKind::Struct(ref qpath, fields, _) => {
1503 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1504 },
1505 PatKind::TupleStruct(ref qpath, pats, _) => {
1506 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1507 },
1508 PatKind::Slice(head, middle, tail) => {
1509 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1510 rustc_ty::Slice(..) => {
1511 !head.is_empty() || middle.is_none() || !tail.is_empty()
1513 },
1514 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1515 _ => {
1516 true
1518 },
1519 }
1520 },
1521 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1522 }
1523}
1524
1525pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1528 if let PatKind::Or(pats) = pat.kind {
1529 pats.iter().for_each(f);
1530 } else {
1531 f(pat);
1532 }
1533}
1534
1535pub fn is_self(slf: &Param<'_>) -> bool {
1536 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1537 name.name == kw::SelfLower
1538 } else {
1539 false
1540 }
1541}
1542
1543pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1544 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1545 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1546 {
1547 return true;
1548 }
1549 false
1550}
1551
1552pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1553 (0..decl.inputs.len()).map(move |i| &body.params[i])
1554}
1555
1556pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1559 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1560 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1561 && ddpos.as_opt_usize().is_none()
1562 && cx
1563 .qpath_res(path, arm.pat.hir_id)
1564 .ctor_parent(cx)
1565 .is_lang_item(cx, ResultOk)
1566 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1567 && arm.body.res_local_id() == Some(hir_id)
1568 {
1569 return true;
1570 }
1571 false
1572 }
1573
1574 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1575 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1576 cx.qpath_res(path, arm.pat.hir_id)
1577 .ctor_parent(cx)
1578 .is_lang_item(cx, ResultErr)
1579 } else {
1580 false
1581 }
1582 }
1583
1584 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1585 if let MatchSource::TryDesugar(_) = *source {
1587 return Some(expr);
1588 }
1589
1590 if arms.len() == 2
1591 && arms[0].guard.is_none()
1592 && arms[1].guard.is_none()
1593 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1594 {
1595 return Some(expr);
1596 }
1597 }
1598
1599 None
1600}
1601
1602pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1612 let mut suppress_lint = false;
1613
1614 for id in ids {
1615 let level_spec = cx.tcx.lint_level_spec_at_node(lint, id);
1616 if let Some(expectation) = level_spec.lint_id() {
1617 cx.fulfill_expectation(expectation);
1618 }
1619
1620 match level_spec.level() {
1621 Level::Allow | Level::Expect => suppress_lint = true,
1622 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1623 }
1624 }
1625
1626 suppress_lint
1627}
1628
1629pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1637 cx.tcx.lint_level_spec_at_node(lint, id).is_allow()
1638}
1639
1640pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1641 while let PatKind::Ref(subpat, _, _) = pat.kind {
1642 pat = subpat;
1643 }
1644 pat
1645}
1646
1647pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1648 Integer::from_int_ty(&tcx, ity).size().bits()
1649}
1650
1651#[expect(clippy::cast_possible_wrap)]
1652pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1654 let amt = 128 - int_bits(tcx, ity);
1655 ((u as i128) << amt) >> amt
1656}
1657
1658#[expect(clippy::cast_sign_loss)]
1659pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1661 let amt = 128 - int_bits(tcx, ity);
1662 ((u as u128) << amt) >> amt
1663}
1664
1665pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1667 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1668 let amt = 128 - bits;
1669 (u << amt) >> amt
1670}
1671
1672pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1673 attrs.iter().any(|attr| attr.has_name(symbol))
1674}
1675
1676pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1677 find_attr!(cx.tcx, hir_id, Repr { .. })
1678}
1679
1680pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1681 let mut prev_enclosing_node = None;
1682 let mut enclosing_node = node;
1683 while Some(enclosing_node) != prev_enclosing_node {
1684 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1685 return true;
1686 }
1687 prev_enclosing_node = Some(enclosing_node);
1688 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1689 }
1690
1691 false
1692}
1693
1694pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1697 tcx.hir_parent_owner_iter(id)
1698 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1699 .any(|(id, _)| find_attr!(tcx, id.def_id, AutomaticallyDerived))
1700}
1701
1702pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1704 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1707}
1708
1709pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1714 let mut conds = Vec::new();
1715 let mut blocks: Vec<&Block<'_>> = Vec::new();
1716
1717 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1718 conds.push(cond);
1719 if let ExprKind::Block(block, _) = then.kind {
1720 blocks.push(block);
1721 } else {
1722 panic!("ExprKind::If node is not an ExprKind::Block");
1723 }
1724
1725 if let Some(else_expr) = r#else {
1726 expr = else_expr;
1727 } else {
1728 break;
1729 }
1730 }
1731
1732 if !blocks.is_empty()
1734 && let ExprKind::Block(block, _) = expr.kind
1735 {
1736 blocks.push(block);
1737 }
1738
1739 (conds, blocks)
1740}
1741
1742pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1744 if let ExprKind::Closure(&Closure {
1745 body,
1746 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1747 ..
1748 }) = expr.kind
1749 && let ExprKind::Block(
1750 Block {
1751 expr:
1752 Some(Expr {
1753 kind: ExprKind::DropTemps(inner_expr),
1754 ..
1755 }),
1756 ..
1757 },
1758 _,
1759 ) = tcx.hir_body(body).value.kind
1760 {
1761 Some(inner_expr)
1762 } else {
1763 None
1764 }
1765}
1766
1767pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1769 get_async_closure_expr(tcx, body.value)
1770}
1771
1772pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1774 let did = match expr.kind {
1775 ExprKind::Call(path, _) => {
1776 if let ExprKind::Path(ref qpath) = path.kind
1777 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1778 {
1779 Some(did)
1780 } else {
1781 None
1782 }
1783 },
1784 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1785 _ => None,
1786 };
1787
1788 did.is_some_and(|did| find_attr!(cx.tcx, did, MustUse { .. }))
1789}
1790
1791fn is_body_identity_function<'hir>(cx: &LateContext<'_>, func: &Body<'hir>) -> bool {
1805 let [param] = func.params else {
1806 return false;
1807 };
1808
1809 let mut param_pat = param.pat;
1810
1811 let mut advance_param_pat_over_stmts = |stmts: &[Stmt<'hir>]| {
1818 for stmt in stmts {
1819 if let StmtKind::Let(local) = stmt.kind
1820 && let Some(init) = local.init
1821 && is_expr_identity_of_pat(cx, param_pat, init, true)
1822 {
1823 param_pat = local.pat;
1824 } else {
1825 return false;
1826 }
1827 }
1828
1829 true
1830 };
1831
1832 let mut expr = func.value;
1833 loop {
1834 match expr.kind {
1835 ExprKind::Block(
1836 &Block {
1837 stmts: [],
1838 expr: Some(e),
1839 ..
1840 },
1841 _,
1842 )
1843 | ExprKind::Ret(Some(e)) => expr = e,
1844 ExprKind::Block(
1845 &Block {
1846 stmts: [stmt],
1847 expr: None,
1848 ..
1849 },
1850 _,
1851 ) => {
1852 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1853 && let ExprKind::Ret(Some(ret_val)) = e.kind
1854 {
1855 expr = ret_val;
1856 } else {
1857 return false;
1858 }
1859 },
1860 ExprKind::Block(
1861 &Block {
1862 stmts, expr: Some(e), ..
1863 },
1864 _,
1865 ) => {
1866 if !advance_param_pat_over_stmts(stmts) {
1867 return false;
1868 }
1869
1870 expr = e;
1871 },
1872 ExprKind::Block(&Block { stmts, expr: None, .. }, _) => {
1873 if let Some((last_stmt, stmts)) = stmts.split_last()
1874 && advance_param_pat_over_stmts(stmts)
1875 && let StmtKind::Semi(e) | StmtKind::Expr(e) = last_stmt.kind
1876 && let ExprKind::Ret(Some(ret_val)) = e.kind
1877 {
1878 expr = ret_val;
1879 } else {
1880 return false;
1881 }
1882 },
1883 _ => return is_expr_identity_of_pat(cx, param_pat, expr, true),
1884 }
1885 }
1886}
1887
1888pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1898 if cx
1899 .typeck_results()
1900 .pat_binding_modes()
1901 .get(pat.hir_id)
1902 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..)))
1903 {
1904 return false;
1908 }
1909
1910 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1912
1913 match (pat.kind, expr.kind) {
1914 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1915 expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1916 },
1917 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1918 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1919 },
1920 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1921 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1922 {
1923 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1924 },
1925 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1926 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1927 },
1928 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1929 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1930 {
1931 if let ExprKind::Path(ident) = &ident.kind
1933 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1934 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
1936 {
1937 true
1938 } else {
1939 false
1940 }
1941 },
1942 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
1943 if field_pats.len() == fields.len() =>
1944 {
1945 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1947 && unordered_over(field_pats, fields, |field_pat, field| {
1949 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
1950 })
1951 },
1952 _ => false,
1953 }
1954}
1955
1956pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1961 match expr.kind {
1962 ExprKind::Closure(&Closure { body, fn_decl, .. })
1963 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1964 {
1965 is_body_identity_function(cx, cx.tcx.hir_body(body))
1966 },
1967 ExprKind::Path(QPath::Resolved(_, path))
1968 if path.segments.iter().all(|seg| seg.infer_args)
1969 && let Some(did) = path.res.opt_def_id() =>
1970 {
1971 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1972 },
1973 _ => false,
1974 }
1975}
1976
1977pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1986 match expr.kind {
1987 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1988 _ => expr.basic_res().is_diag_item(cx, sym::convert_identity),
1989 }
1990}
1991
1992pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
1995 for (node, child_id) in hir_parent_with_src_iter(tcx, expr.hir_id) {
1996 match node {
1997 Node::Block(_) => {},
1998 Node::Arm(arm) if arm.body.hir_id == child_id => {},
1999 Node::Expr(expr) => match expr.kind {
2000 ExprKind::Block(..) | ExprKind::DropTemps(_) => {},
2001 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => {},
2002 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => return None,
2003 _ => return Some((Node::Expr(expr), child_id)),
2004 },
2005 node => return Some((node, child_id)),
2006 }
2007 }
2008 None
2009}
2010
2011pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2013 !matches!(
2014 get_expr_use_or_unification_node(tcx, expr),
2015 None | Some((
2016 Node::Stmt(Stmt {
2017 kind: StmtKind::Expr(_)
2018 | StmtKind::Semi(_)
2019 | StmtKind::Let(LetStmt {
2020 pat: Pat {
2021 kind: PatKind::Wild,
2022 ..
2023 },
2024 ..
2025 }),
2026 ..
2027 }),
2028 _
2029 ))
2030 )
2031}
2032
2033pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2035 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2036}
2037
2038pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2042 !expr.is_place_expr(|base| {
2043 cx.typeck_results()
2044 .adjustments()
2045 .get(base.hir_id)
2046 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2047 })
2048}
2049
2050pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2051 if is_no_core_crate(cx) {
2052 None
2053 } else if is_no_std_crate(cx) {
2054 Some("core")
2055 } else {
2056 Some("std")
2057 }
2058}
2059
2060pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2061 find_attr!(cx.tcx, crate, NoStd)
2062}
2063
2064pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2065 find_attr!(cx.tcx, crate, NoCore)
2066}
2067
2068pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2078 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2079 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2080 } else {
2081 false
2082 }
2083}
2084
2085pub fn fn_has_unsatisfiable_clauses(cx: &LateContext<'_>, did: DefId) -> bool {
2095 use rustc_trait_selection::traits;
2096 let clauses = cx
2097 .tcx
2098 .clauses_of(did)
2099 .clauses
2100 .iter()
2101 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2102 traits::impossible_clauses(cx.tcx, traits::elaborate(cx.tcx, clauses).collect::<Vec<_>>())
2103}
2104
2105pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2107 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2108}
2109
2110pub fn fn_def_id_with_node_args<'tcx>(
2113 cx: &LateContext<'tcx>,
2114 expr: &Expr<'_>,
2115) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2116 let typeck = cx.typeck_results();
2117 match &expr.kind {
2118 ExprKind::MethodCall(..) => Some((
2119 typeck.type_dependent_def_id(expr.hir_id)?,
2120 typeck.node_args(expr.hir_id),
2121 )),
2122 ExprKind::Call(
2123 Expr {
2124 kind: ExprKind::Path(qpath),
2125 hir_id: path_hir_id,
2126 ..
2127 },
2128 ..,
2129 ) => {
2130 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2133 typeck.qpath_res(qpath, *path_hir_id)
2134 {
2135 Some((id, typeck.node_args(*path_hir_id)))
2136 } else {
2137 None
2138 }
2139 },
2140 _ => None,
2141 }
2142}
2143
2144pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2149 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2150 let expr_kind = expr_type.kind();
2151 let is_primitive = match expr_kind {
2152 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2153 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2154 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2155 is_recursively_primitive_type(*element_type)
2156 } else {
2157 unreachable!()
2158 }
2159 },
2160 _ => false,
2161 };
2162
2163 if is_primitive {
2164 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2167 rustc_ty::Slice(..) => return Some("slice".into()),
2168 rustc_ty::Array(..) => return Some("array".into()),
2169 rustc_ty::Tuple(..) => return Some("tuple".into()),
2170 _ => {
2171 let refs_peeled = expr_type.peel_refs();
2174 return Some(refs_peeled.walk().last().unwrap().to_string());
2175 },
2176 }
2177 }
2178 None
2179}
2180
2181pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2189where
2190 Hash: FnMut(&T) -> u64,
2191 Eq: FnMut(&T, &T) -> bool,
2192{
2193 match exprs {
2194 [a, b] if eq(a, b) => return vec![vec![a, b]],
2195 _ if exprs.len() <= 2 => return vec![],
2196 _ => {},
2197 }
2198
2199 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2200
2201 for expr in exprs {
2202 match buckets.entry(hash(expr)) {
2203 indexmap::map::Entry::Occupied(mut o) => {
2204 let bucket = o.get_mut();
2205 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2206 Some(group) => group.push(expr),
2207 None => bucket.push(vec![expr]),
2208 }
2209 },
2210 indexmap::map::Entry::Vacant(v) => {
2211 v.insert(vec![vec![expr]]);
2212 },
2213 }
2214 }
2215
2216 buckets
2217 .into_values()
2218 .flatten()
2219 .filter(|group| group.len() > 1)
2220 .collect()
2221}
2222
2223pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2226 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2227 if let PatKind::Ref(pat, _, _) = pat.kind {
2228 peel(pat, count + 1)
2229 } else {
2230 (pat, count)
2231 }
2232 }
2233 peel(pat, 0)
2234}
2235
2236pub fn peel_hir_expr_while<'tcx>(
2238 mut expr: &'tcx Expr<'tcx>,
2239 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2240) -> &'tcx Expr<'tcx> {
2241 while let Some(e) = f(expr) {
2242 expr = e;
2243 }
2244 expr
2245}
2246
2247pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2250 let mut remaining = count;
2251 let e = peel_hir_expr_while(expr, |e| match e.kind {
2252 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2253 remaining -= 1;
2254 Some(e)
2255 },
2256 _ => None,
2257 });
2258 (e, count - remaining)
2259}
2260
2261pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2264 let mut count: usize = 0;
2265 let mut curr_expr = expr;
2266 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2267 count = count.wrapping_add(1);
2268 curr_expr = local_expr;
2269 }
2270 (curr_expr, count)
2271}
2272
2273pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2276 let mut count = 0;
2277 let e = peel_hir_expr_while(expr, |e| match e.kind {
2278 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2279 count += 1;
2280 Some(e)
2281 },
2282 _ => None,
2283 });
2284 (e, count)
2285}
2286
2287pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2290 let mut count = 0;
2291 loop {
2292 match &ty.kind {
2293 TyKind::Ref(_, ref_ty) => {
2294 ty = ref_ty.ty;
2295 count += 1;
2296 },
2297 _ => break (ty, count),
2298 }
2299 }
2300}
2301
2302pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
2304 match &ty.kind {
2305 TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty),
2306 _ => ty,
2307 }
2308}
2309
2310pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2313 loop {
2314 match expr.kind {
2315 ExprKind::AddrOf(_, _, e) => expr = e,
2316 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2317 _ => break,
2318 }
2319 }
2320 expr
2321}
2322
2323pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2326 let mut operators = Vec::new();
2327 peel_hir_expr_while(expr, |expr| match expr.kind {
2328 ExprKind::AddrOf(_, _, e) => {
2329 operators.push(expr);
2330 Some(e)
2331 },
2332 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2333 operators.push(expr);
2334 Some(e)
2335 },
2336 _ => None,
2337 });
2338 operators
2339}
2340
2341pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2342 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2343 && let Res::Def(_, def_id) = path.res
2344 {
2345 return find_attr!(cx.tcx, def_id, CfgTrace(..) | CfgAttrTrace(..));
2346 }
2347 false
2348}
2349
2350static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModId, Vec<Symbol>>>> = OnceLock::new();
2351
2352fn test_item_names(tcx: TyCtxt<'_>, module: LocalModId) -> Vec<Symbol> {
2355 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2356 let mut map = cache.lock().unwrap();
2357 match map.entry(module) {
2358 Entry::Occupied(entry) => entry.get().clone(),
2359 Entry::Vacant(entry) => {
2360 let mut names = Vec::new();
2361 for id in tcx.hir_module_free_items(module) {
2362 if matches!(tcx.def_kind(id.owner_id), DefKind::Const { .. })
2363 && let item = tcx.hir_item(id)
2364 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2365 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2366 && let Res::Def(DefKind::Struct, _) = path.res
2368 && find_attr!(tcx, item.hir_id(), RustcTestMarker(..))
2369 {
2370 names.push(ident.name);
2371 }
2372 }
2373 names.sort_unstable();
2374 entry.insert(names).clone()
2375 },
2376 }
2377}
2378
2379pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2383 let names = test_item_names(tcx, tcx.parent_module(id));
2384 if names.is_empty() {
2386 return false;
2387 }
2388 once((id, tcx.hir_node(id)))
2389 .chain(tcx.hir_parent_iter(id))
2390 .any(|(_id, node)| {
2393 if let Node::Item(item) = node
2394 && let ItemKind::Fn { ident, .. } = item.kind
2395 {
2396 return names.binary_search(&ident.name).is_ok();
2399 }
2400 false
2401 })
2402}
2403
2404pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2411 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2412 if let Node::Item(item) = tcx.hir_node(id)
2413 && let ItemKind::Fn { ident, .. } = item.kind
2414 {
2415 test_item_names(tcx, tcx.parent_module(id))
2416 .binary_search(&ident.name)
2417 .is_ok()
2418 } else {
2419 false
2420 }
2421}
2422
2423pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2428 if let Some(cfgs) = find_attr!(tcx, id, CfgTrace(cfgs) => cfgs)
2429 && cfgs
2430 .iter()
2431 .any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. }))
2432 {
2433 true
2434 } else {
2435 false
2436 }
2437}
2438
2439pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2441 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2442}
2443
2444pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2446 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2447}
2448
2449pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2451 find_attr!(tcx, def_id, CfgTrace(..))
2452 || find_attr!(
2453 tcx.hir_parent_id_iter(tcx.local_def_id_to_hir_id(def_id))
2454 .flat_map(|parent_id| tcx.hir_attrs(parent_id)),
2455 CfgTrace(..)
2456 )
2457}
2458
2459#[derive(Clone, Copy)]
2461pub enum DefinedTy<'tcx> {
2462 Hir(&'tcx hir::Ty<'tcx>),
2464 Mir {
2472 def_site_def_id: Option<DefId>,
2473 ty: Binder<'tcx, Ty<'tcx>>,
2474 },
2475}
2476
2477pub struct ExprUseSite<'tcx> {
2479 pub node: Node<'tcx>,
2481 pub child_id: HirId,
2483 pub adjustments: &'tcx [Adjustment<'tcx>],
2485 pub is_ty_unified: bool,
2487 pub moved_before_use: bool,
2489 pub same_ctxt: bool,
2491}
2492impl<'tcx> ExprUseSite<'tcx> {
2493 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2494 match self.node {
2495 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2496 Node::ExprField(field) => ExprUseNode::Field(field),
2497
2498 Node::Item(&Item {
2499 kind: ItemKind::Static(..) | ItemKind::Const(..),
2500 owner_id,
2501 ..
2502 })
2503 | Node::TraitItem(&TraitItem {
2504 kind: TraitItemKind::Const(..),
2505 owner_id,
2506 ..
2507 })
2508 | Node::ImplItem(&ImplItem {
2509 kind: ImplItemKind::Const(..),
2510 owner_id,
2511 ..
2512 }) => ExprUseNode::ConstStatic(owner_id),
2513
2514 Node::Item(&Item {
2515 kind: ItemKind::Fn { .. },
2516 owner_id,
2517 ..
2518 })
2519 | Node::TraitItem(&TraitItem {
2520 kind: TraitItemKind::Fn(..),
2521 owner_id,
2522 ..
2523 })
2524 | Node::ImplItem(&ImplItem {
2525 kind: ImplItemKind::Fn(..),
2526 owner_id,
2527 ..
2528 }) => ExprUseNode::Return(owner_id),
2529
2530 Node::Expr(use_expr) => match use_expr.kind {
2531 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2532 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2533 }),
2534
2535 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2536 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2537 Some(i) => ExprUseNode::FnArg(func, i),
2538 None => ExprUseNode::Callee,
2539 },
2540 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2541 use_expr.hir_id,
2542 name.args,
2543 args.iter()
2544 .position(|arg| arg.hir_id == self.child_id)
2545 .map_or(0, |i| i + 1),
2546 ),
2547 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2548 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2549 _ => ExprUseNode::Other,
2550 },
2551 _ => ExprUseNode::Other,
2552 }
2553 }
2554}
2555
2556pub enum ExprUseNode<'tcx> {
2558 LetStmt(&'tcx LetStmt<'tcx>),
2560 ConstStatic(OwnerId),
2562 Return(OwnerId),
2564 Field(&'tcx ExprField<'tcx>),
2566 FnArg(&'tcx Expr<'tcx>, usize),
2568 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2570 Callee,
2572 FieldAccess(Ident),
2574 AddrOf(ast::BorrowKind, Mutability),
2576 Other,
2577}
2578impl<'tcx> ExprUseNode<'tcx> {
2579 pub fn is_return(&self) -> bool {
2581 matches!(self, Self::Return(_))
2582 }
2583
2584 pub fn is_recv(&self) -> bool {
2586 matches!(self, Self::MethodArg(_, _, 0))
2587 }
2588
2589 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2591 match *self {
2592 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2593 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2594 def_site_def_id: Some(id.def_id.to_def_id()),
2595 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity().skip_norm_wip()),
2596 }),
2597 Self::Return(id) => {
2598 if let Node::Expr(Expr {
2599 kind: ExprKind::Closure(c),
2600 ..
2601 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2602 {
2603 match c.fn_decl.output {
2604 FnRetTy::DefaultReturn(_) => None,
2605 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2606 }
2607 } else {
2608 let ty = cx.tcx.fn_sig(id).instantiate_identity().skip_norm_wip().output();
2609 Some(DefinedTy::Mir {
2610 def_site_def_id: Some(id.def_id.to_def_id()),
2611 ty,
2612 })
2613 }
2614 },
2615 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2616 Some(Expr {
2617 hir_id,
2618 kind: ExprKind::Struct(path, ..),
2619 ..
2620 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2621 .and_then(|(adt, variant)| {
2622 variant
2623 .fields
2624 .iter()
2625 .find(|f| f.name == field.ident.name)
2626 .map(|f| (adt, f))
2627 })
2628 .map(|(adt, field_def)| DefinedTy::Mir {
2629 def_site_def_id: Some(adt.did()),
2630 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity().skip_norm_wip()),
2631 }),
2632 _ => None,
2633 },
2634 Self::FnArg(callee, i) => {
2635 let sig = expr_sig(cx, callee)?;
2636 let (hir_ty, ty) = sig.input_with_hir(i)?;
2637 Some(match hir_ty {
2638 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2639 None => DefinedTy::Mir {
2640 def_site_def_id: sig.predicates_id(),
2641 ty,
2642 },
2643 })
2644 },
2645 Self::MethodArg(id, _, i) => {
2646 let id = cx.typeck_results().type_dependent_def_id(id)?;
2647 let sig = cx.tcx.fn_sig(id).skip_binder();
2648 Some(DefinedTy::Mir {
2649 def_site_def_id: Some(id),
2650 ty: sig.input(i),
2651 })
2652 },
2653 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2654 }
2655 }
2656}
2657
2658struct ReplacingFilterMap<I, F>(I, F);
2659impl<I, F, U> Iterator for ReplacingFilterMap<I, F>
2660where
2661 I: Iterator,
2662 F: FnMut(&mut I, I::Item) -> Option<U>,
2663{
2664 type Item = U;
2665 fn next(&mut self) -> Option<U> {
2666 while let Some(x) = self.0.next() {
2667 if let Some(x) = (self.1)(&mut self.0, x) {
2668 return Some(x);
2669 }
2670 }
2671 None
2672 }
2673}
2674
2675#[expect(clippy::too_many_lines)]
2678pub fn expr_use_sites<'tcx>(
2679 tcx: TyCtxt<'tcx>,
2680 typeck: &'tcx TypeckResults<'tcx>,
2681 mut ctxt: SyntaxContext,
2682 e: &'tcx Expr<'tcx>,
2683) -> impl Iterator<Item = ExprUseSite<'tcx>> {
2684 let mut adjustments: &[_] = typeck.expr_adjustments(e);
2685 let mut is_ty_unified = false;
2686 let mut moved_before_use = false;
2687 let mut same_ctxt = true;
2688 ReplacingFilterMap(
2689 hir_parent_with_src_iter(tcx, e.hir_id),
2690 move |iter: &mut _, (parent, child_id)| {
2691 let parent_ctxt;
2692 let mut parent_adjustments: &[_] = &[];
2693 match parent {
2694 Node::Expr(parent_expr) => {
2695 parent_ctxt = parent_expr.span.ctxt();
2696 same_ctxt &= parent_ctxt == ctxt;
2697 parent_adjustments = typeck.expr_adjustments(parent_expr);
2698 match parent_expr.kind {
2699 ExprKind::Match(scrutinee, arms, _) if scrutinee.hir_id != child_id => {
2700 is_ty_unified |= arms.len() != 1;
2701 moved_before_use = true;
2702 if adjustments.is_empty() {
2703 adjustments = parent_adjustments;
2704 }
2705 return None;
2706 },
2707 ExprKind::If(cond, _, else_) if cond.hir_id != child_id => {
2708 is_ty_unified |= else_.is_some();
2709 moved_before_use = true;
2710 if adjustments.is_empty() {
2711 adjustments = parent_adjustments;
2712 }
2713 return None;
2714 },
2715 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2716 is_ty_unified = true;
2717 moved_before_use = true;
2718 *iter = hir_parent_with_src_iter(tcx, id);
2719 if adjustments.is_empty() {
2720 adjustments = parent_adjustments;
2721 }
2722 return None;
2723 },
2724 ExprKind::Block(b, _) => {
2725 is_ty_unified |= b.targeted_by_break;
2726 moved_before_use = true;
2727 if adjustments.is_empty() {
2728 adjustments = parent_adjustments;
2729 }
2730 return None;
2731 },
2732 ExprKind::DropTemps(_) | ExprKind::Type(..) => {
2733 if adjustments.is_empty() {
2734 adjustments = parent_adjustments;
2735 }
2736 return None;
2737 },
2738 _ => {},
2739 }
2740 },
2741 Node::Arm(arm) => {
2742 parent_ctxt = arm.span.ctxt();
2743 same_ctxt &= parent_ctxt == ctxt;
2744 if arm.body.hir_id == child_id {
2745 return None;
2746 }
2747 },
2748 Node::Block(b) => {
2749 same_ctxt &= b.span.ctxt() == ctxt;
2750 return None;
2751 },
2752 Node::ConstBlock(_) => parent_ctxt = ctxt,
2753 Node::ExprField(&ExprField { span, .. }) => {
2754 parent_ctxt = span.ctxt();
2755 same_ctxt &= parent_ctxt == ctxt;
2756 },
2757 Node::AnonConst(&AnonConst { span, .. })
2758 | Node::ConstArg(&ConstArg { span, .. })
2759 | Node::Field(&FieldDef { span, .. })
2760 | Node::ImplItem(&ImplItem { span, .. })
2761 | Node::Item(&Item { span, .. })
2762 | Node::LetStmt(&LetStmt { span, .. })
2763 | Node::Stmt(&Stmt { span, .. })
2764 | Node::TraitItem(&TraitItem { span, .. })
2765 | Node::Variant(&Variant { span, .. }) => {
2766 parent_ctxt = span.ctxt();
2767 same_ctxt &= parent_ctxt == ctxt;
2768 *iter = hir_parent_with_src_iter(tcx, CRATE_HIR_ID);
2769 },
2770 Node::AssocItemConstraint(_)
2771 | Node::ConstArgExprField(_)
2772 | Node::Crate(_)
2773 | Node::Ctor(_)
2774 | Node::Err(_)
2775 | Node::ForeignItem(_)
2776 | Node::GenericParam(_)
2777 | Node::Infer(_)
2778 | Node::Lifetime(_)
2779 | Node::OpaqueTy(_)
2780 | Node::Param(_)
2781 | Node::Pat(_)
2782 | Node::PatExpr(_)
2783 | Node::PatField(_)
2784 | Node::PathSegment(_)
2785 | Node::PreciseCapturingNonLifetimeArg(_)
2786 | Node::Synthetic
2787 | Node::TraitRef(_)
2788 | Node::Ty(_)
2789 | Node::TyPat(_)
2790 | Node::WherePredicate(_) => {
2791 debug_assert!(false, "found {parent:?} which is after the final use node");
2794 return None;
2795 },
2796 }
2797
2798 ctxt = parent_ctxt;
2799 Some(ExprUseSite {
2800 node: parent,
2801 child_id,
2802 adjustments: mem::replace(&mut adjustments, parent_adjustments),
2803 is_ty_unified: mem::replace(&mut is_ty_unified, false),
2804 moved_before_use: mem::replace(&mut moved_before_use, false),
2805 same_ctxt: mem::replace(&mut same_ctxt, true),
2806 })
2807 },
2808 )
2809}
2810
2811pub fn get_expr_use_site<'tcx>(
2812 tcx: TyCtxt<'tcx>,
2813 typeck: &'tcx TypeckResults<'tcx>,
2814 ctxt: SyntaxContext,
2815 e: &'tcx Expr<'tcx>,
2816) -> ExprUseSite<'tcx> {
2817 expr_use_sites(tcx, typeck, ctxt, e).next().unwrap_or_else(|| {
2820 debug_assert!(false, "failed to find a use site for expr {e:?}");
2821 ExprUseSite {
2822 node: Node::Synthetic, child_id: CRATE_HIR_ID,
2824 adjustments: &[],
2825 is_ty_unified: false,
2826 moved_before_use: false,
2827 same_ctxt: false,
2828 }
2829 })
2830}
2831
2832pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2834 let mut pos = 0;
2835 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2836 let end = pos + t.len;
2837 let range = pos as usize..end as usize;
2838 let inner = InnerSpan::new(range.start, range.end);
2839 pos = end;
2840 (t.kind, s.get(range).unwrap_or_default(), inner)
2841 })
2842}
2843
2844pub fn span_contains_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool {
2847 span.check_text(sm, |snippet| {
2848 tokenize(snippet, FrontmatterAllowed::No).any(|token| {
2849 matches!(
2850 token.kind,
2851 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2852 )
2853 })
2854 })
2855}
2856
2857pub fn span_contains_non_whitespace<'sm>(sm: impl HasSourceMap<'sm>, span: Span, skip_comments: bool) -> bool {
2862 span.check_text(sm, |snippet| {
2863 tokenize_with_text(snippet).any(|(token, _, _)| match token {
2864 TokenKind::Whitespace => false,
2865 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2866 _ => true,
2867 })
2868 })
2869}
2870
2871pub fn span_extract_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> String {
2875 span_extract_comments(sm, span).join("\n")
2876}
2877
2878pub fn span_extract_comments<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Vec<String> {
2882 span.with_source_text(sm, |snippet| {
2883 tokenize_with_text(snippet)
2884 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2885 .map(|(_, s, _)| s.to_string())
2886 .collect::<Vec<_>>()
2887 })
2888 .unwrap_or_default()
2889}
2890
2891pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2892 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2893}
2894
2895pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2920 cx: &LateContext<'_>,
2921 pat: &'a Pat<'hir>,
2922 else_body: &Expr<'_>,
2923) -> Option<&'a Pat<'hir>> {
2924 if let Some([inner_pat]) = as_some_pattern(cx, pat)
2925 && !is_refutable(cx, inner_pat)
2926 && let else_body = peel_blocks(else_body)
2927 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2928 && let ExprKind::Path(ret_path) = ret_val.kind
2929 && cx
2930 .qpath_res(&ret_path, ret_val.hir_id)
2931 .ctor_parent(cx)
2932 .is_lang_item(cx, OptionNone)
2933 {
2934 Some(inner_pat)
2935 } else {
2936 None
2937 }
2938}
2939
2940macro_rules! op_utils {
2941 ($($name:ident $assign:ident)*) => {
2942 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2944
2945 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2947
2948 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2950 match kind {
2951 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2952 _ => None,
2953 }
2954 }
2955 };
2956}
2957
2958op_utils! {
2959 Add AddAssign
2960 Sub SubAssign
2961 Mul MulAssign
2962 Div DivAssign
2963 Rem RemAssign
2964 BitXor BitXorAssign
2965 BitAnd BitAndAssign
2966 BitOr BitOrAssign
2967 Shl ShlAssign
2968 Shr ShrAssign
2969}
2970
2971pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2974 match *pat {
2975 PatKind::Wild => true,
2976 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2977 !visitors::is_local_used(cx, body, id)
2978 },
2979 _ => false,
2980 }
2981}
2982
2983#[derive(Clone, Copy)]
2984pub enum RequiresSemi {
2985 Yes,
2986 No,
2987}
2988impl RequiresSemi {
2989 pub fn requires_semi(self) -> bool {
2990 matches!(self, Self::Yes)
2991 }
2992}
2993
2994#[expect(clippy::too_many_lines)]
2997pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2998 struct BreakTarget {
2999 id: HirId,
3000 unused: bool,
3001 }
3002
3003 struct V<'cx, 'tcx> {
3004 cx: &'cx LateContext<'tcx>,
3005 break_targets: Vec<BreakTarget>,
3006 break_targets_for_result_ty: u32,
3007 in_final_expr: bool,
3008 requires_semi: bool,
3009 is_never: bool,
3010 }
3011
3012 impl V<'_, '_> {
3013 fn push_break_target(&mut self, id: HirId) {
3014 self.break_targets.push(BreakTarget { id, unused: true });
3015 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3016 }
3017 }
3018
3019 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3020 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3021 if self.is_never && self.break_targets.is_empty() {
3038 if self.in_final_expr && !self.requires_semi {
3039 match e.kind {
3042 ExprKind::DropTemps(e) => self.visit_expr(e),
3043 ExprKind::If(_, then, Some(else_)) => {
3044 self.visit_expr(then);
3045 self.visit_expr(else_);
3046 },
3047 ExprKind::Match(_, arms, _) => {
3048 for arm in arms {
3049 self.visit_expr(arm.body);
3050 }
3051 },
3052 ExprKind::Loop(b, ..) => {
3053 self.push_break_target(e.hir_id);
3054 self.in_final_expr = false;
3055 self.visit_block(b);
3056 self.break_targets.pop();
3057 },
3058 ExprKind::Block(b, _) => {
3059 if b.targeted_by_break {
3060 self.push_break_target(b.hir_id);
3061 self.visit_block(b);
3062 self.break_targets.pop();
3063 } else {
3064 self.visit_block(b);
3065 }
3066 },
3067 _ => {
3068 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3069 },
3070 }
3071 }
3072 return;
3073 }
3074 match e.kind {
3075 ExprKind::DropTemps(e) => self.visit_expr(e),
3076 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3077 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3078 self.in_final_expr = false;
3079 self.visit_expr(e);
3080 self.is_never = true;
3081 },
3082 ExprKind::Break(dest, e) => {
3083 if let Some(e) = e {
3084 self.in_final_expr = false;
3085 self.visit_expr(e);
3086 }
3087 if let Ok(id) = dest.target_id
3088 && let Some((i, target)) = self
3089 .break_targets
3090 .iter_mut()
3091 .enumerate()
3092 .find(|(_, target)| target.id == id)
3093 {
3094 target.unused &= self.is_never;
3095 if i < self.break_targets_for_result_ty as usize {
3096 self.requires_semi = true;
3097 }
3098 }
3099 self.is_never = true;
3100 },
3101 ExprKind::If(cond, then, else_) => {
3102 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3103 self.visit_expr(cond);
3104 self.in_final_expr = in_final_expr;
3105
3106 if self.is_never {
3107 self.visit_expr(then);
3108 if let Some(else_) = else_ {
3109 self.visit_expr(else_);
3110 }
3111 } else {
3112 self.visit_expr(then);
3113 let is_never = mem::replace(&mut self.is_never, false);
3114 if let Some(else_) = else_ {
3115 self.visit_expr(else_);
3116 self.is_never &= is_never;
3117 }
3118 }
3119 },
3120 ExprKind::Match(scrutinee, arms, _) => {
3121 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3122 self.visit_expr(scrutinee);
3123 self.in_final_expr = in_final_expr;
3124
3125 if self.is_never {
3126 for arm in arms {
3127 self.visit_arm(arm);
3128 }
3129 } else {
3130 let mut is_never = true;
3131 for arm in arms {
3132 self.is_never = false;
3133 if let Some(guard) = arm.guard {
3134 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3135 self.visit_expr(guard);
3136 self.in_final_expr = in_final_expr;
3137 self.is_never = false;
3139 }
3140 self.visit_expr(arm.body);
3141 is_never &= self.is_never;
3142 }
3143 self.is_never = is_never;
3144 }
3145 },
3146 ExprKind::Loop(b, _, _, _) => {
3147 self.push_break_target(e.hir_id);
3148 self.in_final_expr = false;
3149 self.visit_block(b);
3150 self.is_never = self.break_targets.pop().unwrap().unused;
3151 },
3152 ExprKind::Block(b, _) => {
3153 if b.targeted_by_break {
3154 self.push_break_target(b.hir_id);
3155 self.visit_block(b);
3156 self.is_never &= self.break_targets.pop().unwrap().unused;
3157 } else {
3158 self.visit_block(b);
3159 }
3160 },
3161 _ => {
3162 self.in_final_expr = false;
3163 walk_expr(self, e);
3164 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3165 },
3166 }
3167 }
3168
3169 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3170 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3171 for s in b.stmts {
3172 self.visit_stmt(s);
3173 }
3174 self.in_final_expr = in_final_expr;
3175 if let Some(e) = b.expr {
3176 self.visit_expr(e);
3177 }
3178 }
3179
3180 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3181 if let Some(e) = l.init {
3182 self.visit_expr(e);
3183 }
3184 if let Some(else_) = l.els {
3185 let is_never = self.is_never;
3186 self.visit_block(else_);
3187 self.is_never = is_never;
3188 }
3189 }
3190
3191 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3192 if let Some(guard) = arm.guard {
3193 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3194 self.visit_expr(guard);
3195 self.in_final_expr = in_final_expr;
3196 }
3197 self.visit_expr(arm.body);
3198 }
3199 }
3200
3201 if cx.typeck_results().expr_ty(e).is_never() {
3202 Some(RequiresSemi::No)
3203 } else if let ExprKind::Block(b, _) = e.kind
3204 && !b.targeted_by_break
3205 && b.expr.is_none()
3206 {
3207 None
3209 } else {
3210 let mut v = V {
3211 cx,
3212 break_targets: Vec::new(),
3213 break_targets_for_result_ty: 0,
3214 in_final_expr: true,
3215 requires_semi: false,
3216 is_never: false,
3217 };
3218 v.visit_expr(e);
3219 v.is_never
3220 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3221 RequiresSemi::Yes
3222 } else {
3223 RequiresSemi::No
3224 })
3225 }
3226}
3227
3228pub fn get_path_from_caller_to_method_type<'tcx>(
3234 tcx: TyCtxt<'tcx>,
3235 from: LocalDefId,
3236 method: DefId,
3237 args: GenericArgsRef<'tcx>,
3238) -> String {
3239 let assoc_item = tcx.associated_item(method);
3240 let def_id = assoc_item.container_id(tcx);
3241 match assoc_item.container {
3242 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3243 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3244 let ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
3245 get_path_to_ty(tcx, from, ty, args)
3246 },
3247 }
3248}
3249
3250fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3251 match ty.kind() {
3252 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3253 rustc_ty::Array(..)
3255 | rustc_ty::Dynamic(..)
3256 | rustc_ty::Never
3257 | rustc_ty::RawPtr(_, _)
3258 | rustc_ty::Ref(..)
3259 | rustc_ty::Slice(_)
3260 | rustc_ty::Tuple(_) => format!(
3261 "<{}>",
3262 EarlyBinder::bind(tcx, ty).instantiate(tcx, args).skip_norm_wip()
3263 ),
3264 _ => ty.to_string(),
3265 }
3266}
3267
3268fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3270 if callee.is_local() {
3272 let callee_path = tcx.def_path(callee);
3273 let caller_path = tcx.def_path(from.to_def_id());
3274 maybe_get_relative_path(&caller_path, &callee_path, 2)
3275 } else {
3276 tcx.def_path_str(callee)
3277 }
3278}
3279
3280fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3293 use itertools::EitherOrBoth::{Both, Left, Right};
3294
3295 let unique_parts = to
3297 .data
3298 .iter()
3299 .zip_longest(from.data.iter())
3300 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3301 .map(|el| match el {
3302 Both(l, r) => Both(l.data, r.data),
3303 Left(l) => Left(l.data),
3304 Right(r) => Right(r.data),
3305 });
3306
3307 let mut go_up_by = 0;
3309 let mut path = Vec::new();
3310 for el in unique_parts {
3311 match el {
3312 Both(l, r) => {
3313 if let DefPathData::TypeNs(sym) = l {
3323 path.push(sym);
3324 }
3325 if let DefPathData::TypeNs(_) = r {
3326 go_up_by += 1;
3327 }
3328 },
3329 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3334 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3339 _ => {},
3340 }
3341 }
3342
3343 if go_up_by > max_super {
3344 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3346 if let DefPathData::TypeNs(sym) = el.data {
3347 Some(sym)
3348 } else {
3349 None
3350 }
3351 })))
3352 } else if go_up_by == 0 && path.is_empty() {
3353 String::from("Self")
3354 } else {
3355 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3356 }
3357}
3358
3359pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3362 matches!(
3363 cx.tcx.parent_hir_node(id),
3364 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3365 )
3366}
3367
3368pub fn is_block_like(expr: &Expr<'_>) -> bool {
3371 matches!(
3372 expr.kind,
3373 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3374 )
3375}
3376
3377pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3379 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3380 match expr.kind {
3381 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3382 _ if is_block_like(expr) => is_operand,
3383 _ => false,
3384 }
3385 }
3386
3387 contains_block(expr, false)
3388}
3389
3390pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3392 if let Some(parent_expr) = get_parent_expr(cx, expr)
3393 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3394 && receiver.hir_id == expr.hir_id
3395 {
3396 return true;
3397 }
3398 false
3399}
3400
3401pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3404 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3405 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3406 && temporary_ty
3407 .walk()
3408 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3409 {
3410 ControlFlow::Break(())
3411 } else {
3412 ControlFlow::Continue(())
3413 }
3414 })
3415 .is_break()
3416}
3417
3418pub fn leaks_droppable_temporary<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3421 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3422 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) {
3423 ControlFlow::Break(())
3424 } else {
3425 ControlFlow::Continue(())
3426 }
3427 })
3428 .is_break()
3429}
3430
3431pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3442 let expr_ty_is_adjusted = cx
3443 .typeck_results()
3444 .expr_adjustments(expr)
3445 .iter()
3446 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3448 if expr_ty_is_adjusted {
3449 return true;
3450 }
3451
3452 match expr.kind {
3455 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3456 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip();
3457
3458 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3459 return false;
3460 }
3461
3462 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3463 let mut args_with_ty_param = {
3464 fn_sig
3465 .inputs()
3466 .skip_binder()
3467 .iter()
3468 .skip(self_arg_count)
3469 .zip(args)
3470 .filter_map(|(arg_ty, arg)| {
3471 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3472 Some(arg)
3473 } else {
3474 None
3475 }
3476 })
3477 };
3478 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3479 },
3480 ExprKind::Struct(qpath, _, _) => {
3482 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3483 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3484 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3485 return true;
3487 };
3488 v_def
3489 .fields
3490 .iter()
3491 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3492 } else {
3493 false
3494 }
3495 },
3496 ExprKind::Block(
3498 &Block {
3499 expr: Some(ret_expr), ..
3500 },
3501 _,
3502 )
3503 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3504
3505 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3507 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3509 ExprKind::If(_, then, maybe_else) => {
3511 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3512 },
3513 ExprKind::Match(_, arms, _) => arms
3514 .iter()
3515 .map(|arm| arm.body)
3516 .any(|body| expr_requires_coercion(cx, body)),
3517 _ => false,
3518 }
3519}
3520
3521pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3524 if let Some(hir_id) = expr.res_local_id()
3525 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3526 {
3527 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3528 } else if let ExprKind::Path(p) = &expr.kind
3529 && let Some(mutability) = cx
3530 .qpath_res(p, expr.hir_id)
3531 .opt_def_id()
3532 .and_then(|id| cx.tcx.static_mutability(id))
3533 {
3534 mutability == Mutability::Mut
3535 } else if let ExprKind::Field(parent, _) = expr.kind {
3536 is_mutable(cx, parent)
3537 } else {
3538 true
3539 }
3540}
3541
3542pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3545 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3546 return hir_ty;
3547 };
3548 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3549 && let Some(segment) = path.segments.last()
3550 && segment.ident.name == sym::Option
3551 && let Res::Def(DefKind::Enum, def_id) = segment.res
3552 && def_id == option_def_id
3553 && let [GenericArg::Type(arg_ty)] = segment.args().args
3554 {
3555 hir_ty = arg_ty.as_unambig_ty();
3556 }
3557 hir_ty
3558}
3559
3560pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3563 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3564 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3565 && let ctxt = expr.span.ctxt()
3566 && for_each_expr_without_closures(into_future_arg, |e| {
3567 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3568 })
3569 .is_none()
3570 {
3571 Some(into_future_arg)
3572 } else {
3573 None
3574 }
3575}
3576
3577pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3579 if let ExprKind::Call(fn_expr, []) = &expr.kind
3580 && let ExprKind::Path(qpath) = &fn_expr.kind
3581 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3582 {
3583 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3584 } else {
3585 false
3586 }
3587}
3588
3589pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3606 let enclosing_body_owner = cx
3607 .tcx
3608 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3609 let mut prev_id = expr.hir_id;
3610 let mut skip_until_id = None;
3611 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3612 if hir_id == enclosing_body_owner {
3613 return true;
3614 }
3615 if let Some(id) = skip_until_id {
3616 prev_id = hir_id;
3617 if id == hir_id {
3618 skip_until_id = None;
3619 }
3620 continue;
3621 }
3622 match node {
3623 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3624 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3625 Node::Expr(expr) => match expr.kind {
3626 ExprKind::Ret(_) => return true,
3627 ExprKind::If(_, then, opt_else)
3628 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3629 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3630 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3631 ExprKind::Break(
3632 Destination {
3633 target_id: Ok(target_id),
3634 ..
3635 },
3636 _,
3637 ) => skip_until_id = Some(target_id),
3638 _ => break,
3639 },
3640 _ => break,
3641 }
3642 prev_id = hir_id;
3643 }
3644
3645 false
3648}
3649
3650pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3653 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3654 matches!(
3655 adj.kind,
3656 Adjust::Deref(DerefAdjustKind::Overloaded(_))
3657 | Adjust::Pointer(PointerCoercion::Unsize)
3658 | Adjust::NeverToAny
3659 )
3660 })
3661}
3662
3663pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3665 matches!(
3666 expr.kind,
3667 ExprKind::Closure(Closure {
3668 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3669 CoroutineDesugaring::Async,
3670 CoroutineSource::Block
3671 )),
3672 ..
3673 })
3674 )
3675}
3676
3677pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3679 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3680}
3681
3682#[inline]
3685pub fn hir_parent_with_src_iter(tcx: TyCtxt<'_>, mut id: HirId) -> impl Iterator<Item = (Node<'_>, HirId)> {
3686 tcx.hir_parent_id_iter(id)
3687 .map(move |parent| (tcx.hir_node(parent), mem::replace(&mut id, parent)))
3688}