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