1#![feature(box_patterns)]
2#![feature(macro_metavar_expr)]
3#![feature(rustc_private)]
4#![feature(unwrap_infallible)]
5#![recursion_limit = "512"]
6#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
7#![warn(
8 trivial_casts,
9 trivial_numeric_casts,
10 rust_2018_idioms,
11 unused_lifetimes,
12 unused_qualifications,
13 rustc::internal
14)]
15
16extern crate rustc_abi;
19extern crate rustc_ast;
20extern crate rustc_attr_parsing;
21extern crate rustc_const_eval;
22extern crate rustc_data_structures;
23#[expect(
24 unused_extern_crates,
25 reason = "The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate."
26)]
27extern crate rustc_driver;
28extern crate rustc_errors;
29extern crate rustc_hir;
30extern crate rustc_hir_analysis;
31extern crate rustc_hir_typeck;
32extern crate rustc_index;
33extern crate rustc_infer;
34extern crate rustc_lexer;
35extern crate rustc_lint;
36extern crate rustc_middle;
37extern crate rustc_mir_dataflow;
38extern crate rustc_session;
39extern crate rustc_span;
40extern crate rustc_trait_selection;
41
42pub mod ast_utils;
43#[deny(missing_docs)]
44pub mod attrs;
45mod check_proc_macro;
46pub mod comparisons;
47pub mod consts;
48pub mod diagnostics;
49pub mod eager_or_lazy;
50pub mod higher;
51mod hir_utils;
52pub mod macros;
53pub mod mir;
54pub mod msrvs;
55pub mod numeric_literal;
56pub mod paths;
57pub mod qualify_min_const_fn;
58pub mod res;
59pub mod source;
60pub mod str_utils;
61pub mod sugg;
62pub mod sym;
63pub mod ty;
64pub mod usage;
65pub mod visitors;
66
67pub use self::attrs::*;
68pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
69pub use self::hir_utils::{
70 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
71 hash_stmt, is_bool, over,
72};
73
74use core::mem;
75use core::ops::ControlFlow;
76use std::collections::hash_map::Entry;
77use std::iter::{once, repeat_n, zip};
78use std::sync::{Mutex, MutexGuard, OnceLock};
79
80use itertools::Itertools;
81use rustc_abi::Integer;
82use rustc_ast::ast::{self, LitKind, RangeLimits};
83use rustc_ast::{LitIntType, join_path_syms};
84use rustc_data_structures::fx::FxHashMap;
85use rustc_data_structures::indexmap;
86use rustc_data_structures::packed::Pu128;
87use rustc_data_structures::unhash::UnindexMap;
88use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
89use rustc_hir::attrs::CfgEntry;
90use rustc_hir::def::{DefKind, Res};
91use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
92use rustc_hir::definitions::{DefPath, DefPathData};
93use rustc_hir::hir_id::{HirIdMap, HirIdSet};
94use rustc_hir::intravisit::{Visitor, walk_expr};
95use rustc_hir::{
96 self as hir, AnonConst, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, CRATE_HIR_ID, Closure, ConstArg,
97 ConstArgKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind,
98 FieldDef, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem,
99 LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path,
100 PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, Variant, def,
101 find_attr,
102};
103use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
104use rustc_lint::{LateContext, Level, Lint, LintContext};
105use rustc_middle::hir::nested_filter;
106use rustc_middle::hir::place::PlaceBase;
107use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
108use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion};
109use rustc_middle::ty::layout::IntegerExt;
110use rustc_middle::ty::{
111 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
112 TypeFlags, TypeVisitableExt, TypeckResults, UintTy, UpvarCapture,
113};
114use rustc_span::hygiene::{ExpnKind, MacroKind};
115use rustc_span::source_map::SourceMap;
116use rustc_span::symbol::{Ident, Symbol, kw};
117use rustc_span::{InnerSpan, Span, SyntaxContext};
118use source::{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, limits, .. }) = Range::hir(cx, expr) {
1334 start.is_none_or(|start| is_integer_literal(start, 0))
1335 && end.is_none_or(|end| {
1336 if 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<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2352
2353fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2356 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2357 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2358 let value = map.entry(module);
2359 match value {
2360 Entry::Occupied(entry) => f(entry.get()),
2361 Entry::Vacant(entry) => {
2362 let mut names = Vec::new();
2363 for id in tcx.hir_module_free_items(module) {
2364 if matches!(tcx.def_kind(id.owner_id), DefKind::Const { .. })
2365 && let item = tcx.hir_item(id)
2366 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2367 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2368 && let Res::Def(DefKind::Struct, _) = path.res
2370 && find_attr!(tcx, item.hir_id(), RustcTestMarker(..))
2371 {
2372 names.push(ident.name);
2373 }
2374 }
2375 names.sort_unstable();
2376 f(entry.insert(names))
2377 },
2378 }
2379}
2380
2381pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2385 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2386 let node = tcx.hir_node(id);
2387 once((id, node))
2388 .chain(tcx.hir_parent_iter(id))
2389 .any(|(_id, node)| {
2392 if let Node::Item(item) = node
2393 && let ItemKind::Fn { ident, .. } = item.kind
2394 {
2395 return names.binary_search(&ident.name).is_ok();
2398 }
2399 false
2400 })
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 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2416 names.binary_search(&ident.name).is_ok()
2417 })
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!("<{}>", EarlyBinder::bind(tcx, ty).instantiate(tcx, args).skip_norm_wip()),
3261 _ => ty.to_string(),
3262 }
3263}
3264
3265fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3267 if callee.is_local() {
3269 let callee_path = tcx.def_path(callee);
3270 let caller_path = tcx.def_path(from.to_def_id());
3271 maybe_get_relative_path(&caller_path, &callee_path, 2)
3272 } else {
3273 tcx.def_path_str(callee)
3274 }
3275}
3276
3277fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3290 use itertools::EitherOrBoth::{Both, Left, Right};
3291
3292 let unique_parts = to
3294 .data
3295 .iter()
3296 .zip_longest(from.data.iter())
3297 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3298 .map(|el| match el {
3299 Both(l, r) => Both(l.data, r.data),
3300 Left(l) => Left(l.data),
3301 Right(r) => Right(r.data),
3302 });
3303
3304 let mut go_up_by = 0;
3306 let mut path = Vec::new();
3307 for el in unique_parts {
3308 match el {
3309 Both(l, r) => {
3310 if let DefPathData::TypeNs(sym) = l {
3320 path.push(sym);
3321 }
3322 if let DefPathData::TypeNs(_) = r {
3323 go_up_by += 1;
3324 }
3325 },
3326 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3331 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3336 _ => {},
3337 }
3338 }
3339
3340 if go_up_by > max_super {
3341 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3343 if let DefPathData::TypeNs(sym) = el.data {
3344 Some(sym)
3345 } else {
3346 None
3347 }
3348 })))
3349 } else if go_up_by == 0 && path.is_empty() {
3350 String::from("Self")
3351 } else {
3352 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3353 }
3354}
3355
3356pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3359 matches!(
3360 cx.tcx.parent_hir_node(id),
3361 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3362 )
3363}
3364
3365pub fn is_block_like(expr: &Expr<'_>) -> bool {
3368 matches!(
3369 expr.kind,
3370 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3371 )
3372}
3373
3374pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3376 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3377 match expr.kind {
3378 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3379 _ if is_block_like(expr) => is_operand,
3380 _ => false,
3381 }
3382 }
3383
3384 contains_block(expr, false)
3385}
3386
3387pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3389 if let Some(parent_expr) = get_parent_expr(cx, expr)
3390 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3391 && receiver.hir_id == expr.hir_id
3392 {
3393 return true;
3394 }
3395 false
3396}
3397
3398pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3401 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3402 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3403 && temporary_ty
3404 .walk()
3405 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3406 {
3407 ControlFlow::Break(())
3408 } else {
3409 ControlFlow::Continue(())
3410 }
3411 })
3412 .is_break()
3413}
3414
3415pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3426 let expr_ty_is_adjusted = cx
3427 .typeck_results()
3428 .expr_adjustments(expr)
3429 .iter()
3430 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3432 if expr_ty_is_adjusted {
3433 return true;
3434 }
3435
3436 match expr.kind {
3439 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3440 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip();
3441
3442 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3443 return false;
3444 }
3445
3446 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3447 let mut args_with_ty_param = {
3448 fn_sig
3449 .inputs()
3450 .skip_binder()
3451 .iter()
3452 .skip(self_arg_count)
3453 .zip(args)
3454 .filter_map(|(arg_ty, arg)| {
3455 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3456 Some(arg)
3457 } else {
3458 None
3459 }
3460 })
3461 };
3462 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3463 },
3464 ExprKind::Struct(qpath, _, _) => {
3466 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3467 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3468 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3469 return true;
3471 };
3472 v_def
3473 .fields
3474 .iter()
3475 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3476 } else {
3477 false
3478 }
3479 },
3480 ExprKind::Block(
3482 &Block {
3483 expr: Some(ret_expr), ..
3484 },
3485 _,
3486 )
3487 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3488
3489 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3491 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3493 ExprKind::If(_, then, maybe_else) => {
3495 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3496 },
3497 ExprKind::Match(_, arms, _) => arms
3498 .iter()
3499 .map(|arm| arm.body)
3500 .any(|body| expr_requires_coercion(cx, body)),
3501 _ => false,
3502 }
3503}
3504
3505pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3508 if let Some(hir_id) = expr.res_local_id()
3509 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3510 {
3511 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3512 } else if let ExprKind::Path(p) = &expr.kind
3513 && let Some(mutability) = cx
3514 .qpath_res(p, expr.hir_id)
3515 .opt_def_id()
3516 .and_then(|id| cx.tcx.static_mutability(id))
3517 {
3518 mutability == Mutability::Mut
3519 } else if let ExprKind::Field(parent, _) = expr.kind {
3520 is_mutable(cx, parent)
3521 } else {
3522 true
3523 }
3524}
3525
3526pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3529 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3530 return hir_ty;
3531 };
3532 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3533 && let Some(segment) = path.segments.last()
3534 && segment.ident.name == sym::Option
3535 && let Res::Def(DefKind::Enum, def_id) = segment.res
3536 && def_id == option_def_id
3537 && let [GenericArg::Type(arg_ty)] = segment.args().args
3538 {
3539 hir_ty = arg_ty.as_unambig_ty();
3540 }
3541 hir_ty
3542}
3543
3544pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3547 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3548 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3549 && let ctxt = expr.span.ctxt()
3550 && for_each_expr_without_closures(into_future_arg, |e| {
3551 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3552 })
3553 .is_none()
3554 {
3555 Some(into_future_arg)
3556 } else {
3557 None
3558 }
3559}
3560
3561pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3563 if let ExprKind::Call(fn_expr, []) = &expr.kind
3564 && let ExprKind::Path(qpath) = &fn_expr.kind
3565 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3566 {
3567 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3568 } else {
3569 false
3570 }
3571}
3572
3573pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3590 let enclosing_body_owner = cx
3591 .tcx
3592 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3593 let mut prev_id = expr.hir_id;
3594 let mut skip_until_id = None;
3595 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3596 if hir_id == enclosing_body_owner {
3597 return true;
3598 }
3599 if let Some(id) = skip_until_id {
3600 prev_id = hir_id;
3601 if id == hir_id {
3602 skip_until_id = None;
3603 }
3604 continue;
3605 }
3606 match node {
3607 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3608 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3609 Node::Expr(expr) => match expr.kind {
3610 ExprKind::Ret(_) => return true,
3611 ExprKind::If(_, then, opt_else)
3612 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3613 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3614 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3615 ExprKind::Break(
3616 Destination {
3617 target_id: Ok(target_id),
3618 ..
3619 },
3620 _,
3621 ) => skip_until_id = Some(target_id),
3622 _ => break,
3623 },
3624 _ => break,
3625 }
3626 prev_id = hir_id;
3627 }
3628
3629 false
3632}
3633
3634pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3637 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3638 matches!(
3639 adj.kind,
3640 Adjust::Deref(DerefAdjustKind::Overloaded(_))
3641 | Adjust::Pointer(PointerCoercion::Unsize)
3642 | Adjust::NeverToAny
3643 )
3644 })
3645}
3646
3647pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3649 matches!(
3650 expr.kind,
3651 ExprKind::Closure(Closure {
3652 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3653 CoroutineDesugaring::Async,
3654 CoroutineSource::Block
3655 )),
3656 ..
3657 })
3658 )
3659}
3660
3661pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3663 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3664}
3665
3666#[inline]
3669pub fn hir_parent_with_src_iter(tcx: TyCtxt<'_>, mut id: HirId) -> impl Iterator<Item = (Node<'_>, HirId)> {
3670 tcx.hir_parent_id_iter(id)
3671 .map(move |parent| (tcx.hir_node(parent), mem::replace(&mut id, parent)))
3672}