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