1#![feature(deref_patterns)]
2#![feature(macro_metavar_expr)]
3#![feature(rustc_private)]
4#![feature(unwrap_infallible)]
5#![recursion_limit = "512"]
6#![expect(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
7#![warn(
8 rust_2018_idioms,
9 trivial_casts,
10 trivial_numeric_casts,
11 unused_lifetimes,
12 unused_qualifications,
13 rustc::internal
14)]
15
16extern crate rustc_abi;
19extern crate rustc_ast;
20extern crate rustc_attr_parsing;
21extern crate rustc_const_eval;
22extern crate rustc_data_structures;
23#[expect(
24 unused_extern_crates,
25 reason = "The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate."
26)]
27extern crate rustc_driver;
28extern crate rustc_errors;
29extern crate rustc_hir;
30extern crate rustc_hir_analysis;
31extern crate rustc_hir_typeck;
32extern crate rustc_index;
33extern crate rustc_infer;
34extern crate rustc_lexer;
35extern crate rustc_lint;
36extern crate rustc_middle;
37extern crate rustc_mir_dataflow;
38extern crate rustc_session;
39extern crate rustc_span;
40extern crate rustc_trait_selection;
41
42pub mod ast_utils;
43#[deny(missing_docs)]
44pub mod attrs;
45mod check_proc_macro;
46pub mod comparisons;
47pub mod consts;
48pub mod diagnostics;
49pub mod eager_or_lazy;
50pub mod higher;
51mod hir_utils;
52pub mod macros;
53pub mod mir;
54pub mod msrvs;
55pub mod numeric_literal;
56pub mod paths;
57pub mod qualify_min_const_fn;
58pub mod res;
59pub mod source;
60pub mod str_utils;
61pub mod sugg;
62pub mod sym;
63pub mod ty;
64pub mod usage;
65pub mod visitors;
66
67pub use self::attrs::*;
68pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
69pub use self::hir_utils::{
70 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
71 hash_stmt, is_bool, over,
72};
73
74use core::mem;
75use core::ops::ControlFlow;
76use std::collections::hash_map::Entry;
77use std::iter::{once, repeat_n, zip};
78use std::sync::{Mutex, OnceLock};
79
80use itertools::Itertools as _;
81use rustc_abi::Integer;
82use rustc_ast::ast::{self, LitKind, RangeLimits};
83use rustc_ast::{LitIntType, join_path_syms};
84use rustc_data_structures::fx::FxHashMap;
85use rustc_data_structures::indexmap;
86use rustc_data_structures::packed::Pu128;
87use rustc_data_structures::unhash::UnindexMap;
88use rustc_hir::attrs::CfgEntry;
89use rustc_hir::attrs::lang_items::LangItem;
90use rustc_hir::attrs::lang_items::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
91use rustc_hir::def::{DefKind, Res};
92use rustc_hir::def_id::{DefId, LocalDefId, LocalModId};
93use rustc_hir::definitions::{DefPath, DefPathData};
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, HirIdMap, HirIdSet, Impl, ImplItem, ImplItemKind, Item,
99 ItemKind, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind,
100 Path, 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 as _};
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 as _;
110use rustc_middle::ty::{
111 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
112 TypeFlags, TypeVisitableExt as _, 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 as _, 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 as _, MaybeResPath as _};
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(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(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.basic_res().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.basic_res().ctor_parent(cx).is_lang_item(cx, OptionSome)
309 {
310 Some(arg)
311 } else {
312 None
313 }
314}
315
316pub fn is_empty_block(expr: &Expr<'_>) -> bool {
318 matches!(
319 expr.kind,
320 ExprKind::Block(
321 Block {
322 stmts: [],
323 expr: None,
324 ..
325 },
326 _,
327 )
328 )
329}
330
331pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
333 matches!(
334 expr.kind,
335 ExprKind::Block(
336 Block {
337 stmts: [],
338 expr: None,
339 ..
340 },
341 _
342 ) | ExprKind::Tup([])
343 )
344}
345
346pub fn is_wild(pat: &Pat<'_>) -> bool {
348 matches!(pat.kind, PatKind::Wild)
349}
350
351pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> {
358 if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind
359 && cx
360 .qpath_res(qpath, pat.hir_id)
361 .ctor_parent(cx)
362 .is_lang_item(cx, OptionSome)
363 {
364 Some(inner)
365 } else {
366 None
367 }
368}
369
370pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
372 matches!(pat.kind,
373 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
374 if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone))
375}
376
377pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
379 is_none_pattern(cx, arm.pat)
380 && matches!(
381 peel_blocks(arm.body).kind,
382 ExprKind::Path(qpath)
383 if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)
384 )
385}
386
387pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
389 match *qpath {
390 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
391 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
392 QPath::TypeRelative(..) => false,
393 }
394}
395
396pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
398 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
399 && let ItemKind::Impl(imp) = item.kind
400 {
401 imp.of_trait.is_some()
402 } else {
403 false
404 }
405}
406
407pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
408 match *path {
409 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
410 QPath::TypeRelative(_, seg) => seg,
411 }
412}
413
414pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
415 last_path_segment(qpath)
416 .args
417 .map_or(&[][..], |a| a.args)
418 .iter()
419 .filter_map(|a| match a {
420 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
421 _ => None,
422 })
423}
424
425pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
430 match expr.kind {
431 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
432 ExprKind::Path(QPath::Resolved(
433 _,
434 Path {
435 res: Res::Local(local), ..
436 },
437 )) => Some(*local),
438 _ => None,
439 }
440}
441
442pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
458 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
459 && let ItemKind::Impl(impl_) = &item.kind
460 && let Some(of_trait) = impl_.of_trait
461 {
462 return Some(&of_trait.trait_ref);
463 }
464 None
465}
466
467fn projection_stack<'a, 'hir>(
475 mut e: &'a Expr<'hir>,
476 ctxt: SyntaxContext,
477) -> Option<(Vec<&'a Expr<'hir>>, &'a Expr<'hir>)> {
478 let mut result = vec![];
479 let root = loop {
480 match e.kind {
481 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) if e.span.ctxt() == ctxt => {
482 result.push(e);
483 e = ep;
484 },
485 ExprKind::Index(..) | ExprKind::Field(..) => return None,
486 _ => break e,
487 }
488 };
489 result.reverse();
490 Some((result, root))
491}
492
493pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
495 cx.typeck_results()
496 .expr_adjustments(e)
497 .iter()
498 .find_map(|a| match a.kind {
499 Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)),
500 Adjust::Deref(DerefAdjustKind::Builtin) => None,
501 _ => Some(None),
502 })
503 .and_then(|x| x)
504}
505
506pub fn can_mut_borrow_both(cx: &LateContext<'_>, ctxt: SyntaxContext, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
509 let Some((s1, r1)) = projection_stack(e1, ctxt) else {
510 return false;
511 };
512 let Some((s2, r2)) = projection_stack(e2, ctxt) else {
513 return false;
514 };
515 if !eq_expr_value(cx, ctxt, r1, r2) {
516 return true;
517 }
518 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
519 return false;
520 }
521
522 for (x1, x2) in zip(&s1, &s2) {
523 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
524 return false;
525 }
526
527 match (&x1.kind, &x2.kind) {
528 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
529 if i1 != i2 {
530 return true;
531 }
532 },
533 _ => return false,
534 }
535 }
536 false
537}
538
539fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
542 let std_types_symbols = &[
543 sym::Vec,
544 sym::VecDeque,
545 sym::LinkedList,
546 sym::HashMap,
547 sym::BTreeMap,
548 sym::HashSet,
549 sym::BTreeSet,
550 sym::BinaryHeap,
551 ];
552
553 if let QPath::TypeRelative(_, method) = path
554 && method.ident.name == sym::new
555 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
556 && let Some(adt) = cx
557 .tcx
558 .type_of(impl_did)
559 .instantiate_identity()
560 .skip_norm_wip()
561 .ty_adt_def()
562 {
563 return Some(adt.did()) == cx.tcx.lang_items().string()
564 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
565 }
566 false
567}
568
569pub fn is_default_equivalent_call(
571 cx: &LateContext<'_>,
572 repl_func: &Expr<'_>,
573 whole_call_expr: Option<&Expr<'_>>,
574) -> bool {
575 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
576 && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx)
577 && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default)
578 || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath))
579 {
580 return true;
581 }
582
583 let Some(e) = whole_call_expr else { return false };
586 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
587 return false;
588 };
589 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
590 return false;
591 };
592 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
593 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
594 cx.tcx.lifetimes.re_erased.into()
595 } else if param.index == 0 && param.name == kw::SelfUpper {
596 ty.into()
597 } else {
598 param.to_error(cx.tcx)
599 }
600 });
601 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
602
603 let Ok(Some(instance)) = instance else { return false };
604 if let rustc_ty::InstanceKind::Item(def) = instance.def
605 && !cx.tcx.is_mir_available(def)
606 {
607 return false;
608 }
609 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
610 return false;
611 };
612 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
613 return false;
614 };
615
616 let body = cx.tcx.instance_mir(instance.def);
622 for block_data in body.basic_blocks.iter() {
623 if block_data.statements.len() == 1
624 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
625 && assign.0.local == RETURN_PLACE
626 && let Rvalue::Aggregate(kind, _places) = &assign.1
627 && let AggregateKind::Adt(did, variant_index, _, _, _) = **kind
628 && let def = cx.tcx.adt_def(did)
629 && let variant = &def.variant(variant_index)
630 && variant.fields.is_empty()
631 && let Some((_, did)) = variant.ctor
632 && did == repl_def_id
633 {
634 return true;
635 } else if block_data.statements.is_empty()
636 && let Some(term) = &block_data.terminator
637 {
638 match &term.kind {
639 TerminatorKind::Call {
640 func: Operand::Constant(c),
641 ..
642 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
643 && *did == repl_def_id =>
644 {
645 return true;
646 },
647 TerminatorKind::TailCall {
648 func: Operand::Constant(c),
649 ..
650 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
651 && *did == repl_def_id =>
652 {
653 return true;
654 },
655 _ => {},
656 }
657 }
658 }
659 false
660}
661
662pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
666 match &e.kind {
667 ExprKind::Lit(lit) => match lit.node {
668 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
669 LitKind::Str(s, _) => s.is_empty(),
670 _ => false,
671 },
672 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
673 ExprKind::Repeat(x, len) => {
674 if let ConstArgKind::Anon(anon_const) = len.kind
675 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
676 && let LitKind::Int(v, _) = const_lit.node
677 && v <= 32
678 && is_default_equivalent(cx, x)
679 {
680 true
681 } else {
682 false
683 }
684 },
685 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
686 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
687 ExprKind::Path(qpath) => cx
688 .qpath_res(qpath, e.hir_id)
689 .ctor_parent(cx)
690 .is_lang_item(cx, OptionNone),
691 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
692 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
693 _ => false,
694 }
695}
696
697fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
698 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
699 && seg.ident.name == sym::from
700 {
701 match arg.kind {
702 ExprKind::Lit(hir::Lit {
703 node: LitKind::Str(sym, _),
704 ..
705 }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String),
706 ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec),
707 ExprKind::Repeat(_, len) => {
708 if let ConstArgKind::Anon(anon_const) = len.kind
709 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
710 && let LitKind::Int(v, _) = const_lit.node
711 {
712 return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec);
713 }
714 },
715 _ => (),
716 }
717 }
718 false
719}
720
721pub fn can_move_expr_to_closure_no_visit<'tcx>(
753 cx: &LateContext<'tcx>,
754 expr: &'tcx Expr<'_>,
755 loop_ids: &[HirId],
756 ignore_locals: &HirIdSet,
757) -> bool {
758 match expr.kind {
759 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
760 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
761 if loop_ids.contains(&id) =>
762 {
763 true
764 },
765 ExprKind::Break(..)
766 | ExprKind::Continue(_)
767 | ExprKind::Ret(_)
768 | ExprKind::Yield(..)
769 | ExprKind::InlineAsm(_) => false,
770 ExprKind::Field(
773 &Expr {
774 hir_id,
775 kind:
776 ExprKind::Path(QPath::Resolved(
777 _,
778 Path {
779 res: Res::Local(local_id),
780 ..
781 },
782 )),
783 ..
784 },
785 _,
786 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
787 false
789 },
790 _ => true,
791 }
792}
793
794#[derive(Debug, Clone, Copy, PartialEq, Eq)]
796pub enum CaptureKind {
797 Value,
798 Use,
799 Ref(Mutability),
800}
801impl CaptureKind {
802 pub fn is_imm_ref(self) -> bool {
803 self == Self::Ref(Mutability::Not)
804 }
805}
806impl std::ops::BitOr for CaptureKind {
807 type Output = Self;
808 fn bitor(self, rhs: Self) -> Self::Output {
809 match (self, rhs) {
810 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
811 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
812 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
813 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
814 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
815 }
816 }
817}
818impl std::ops::BitOrAssign for CaptureKind {
819 fn bitor_assign(&mut self, rhs: Self) {
820 *self = *self | rhs;
821 }
822}
823
824pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
830 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
831 let mut capture = CaptureKind::Ref(Mutability::Not);
832 pat.each_binding_or_first(&mut |_, id, span, _| match cx
833 .typeck_results()
834 .extract_binding_mode(cx.sess(), id, span)
835 .0
836 {
837 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
838 capture = CaptureKind::Value;
839 },
840 ByRef::Yes(_, Mutability::Mut) if capture != CaptureKind::Value => {
841 capture = CaptureKind::Ref(Mutability::Mut);
842 },
843 _ => (),
844 });
845 capture
846 }
847
848 debug_assert!(matches!(
849 e.kind,
850 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
851 ));
852
853 let mut capture = CaptureKind::Value;
854 let mut capture_expr_ty = e;
855
856 for (parent, child_id) in hir_parent_with_src_iter(cx.tcx, e.hir_id) {
857 if let [
858 Adjustment {
859 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
860 target,
861 },
862 ref adjust @ ..,
863 ] = *cx
864 .typeck_results()
865 .adjustments()
866 .get(child_id)
867 .map_or(&[][..], |x| &**x)
868 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
869 *adjust.last().map_or(target, |a| a.target).kind()
870 {
871 return CaptureKind::Ref(mutability);
872 }
873
874 match parent {
875 Node::Expr(e) => match e.kind {
876 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
877 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
878 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
879 return CaptureKind::Ref(Mutability::Mut);
880 },
881 ExprKind::Field(..) => {
882 if capture == CaptureKind::Value {
883 capture_expr_ty = e;
884 }
885 },
886 ExprKind::Let(let_expr) => {
887 let mutability = match pat_capture_kind(cx, let_expr.pat) {
888 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
889 CaptureKind::Ref(m) => m,
890 };
891 return CaptureKind::Ref(mutability);
892 },
893 ExprKind::Match(_, arms, _) => {
894 let mut mutability = Mutability::Not;
895 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
896 match capture {
897 CaptureKind::Value | CaptureKind::Use => break,
898 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
899 CaptureKind::Ref(Mutability::Not) => (),
900 }
901 }
902 return CaptureKind::Ref(mutability);
903 },
904 _ => break,
905 },
906 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
907 CaptureKind::Value | CaptureKind::Use => break,
908 capture @ CaptureKind::Ref(_) => return capture,
909 },
910 _ => break,
911 }
912 }
913
914 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
915 CaptureKind::Ref(Mutability::Not)
917 } else {
918 capture
919 }
920}
921
922pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
925 struct V<'cx, 'tcx> {
926 cx: &'cx LateContext<'tcx>,
927 loops: Vec<HirId>,
929 locals: HirIdSet,
931 allow_closure: bool,
933 captures: HirIdMap<CaptureKind>,
936 }
937 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
938 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
939 if !self.allow_closure {
940 return;
941 }
942
943 match e.kind {
944 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
945 if !self.locals.contains(&l) {
946 let cap = capture_local_usage(self.cx, e);
947 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
948 }
949 },
950 ExprKind::Closure(closure) => {
951 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
952 let local_id = match capture.place.base {
953 PlaceBase::Local(id) => id,
954 PlaceBase::Upvar(var) => var.var_path.hir_id,
955 _ => continue,
956 };
957 if !self.locals.contains(&local_id) {
958 let capture = match capture.info.capture_kind {
959 UpvarCapture::ByValue => CaptureKind::Value,
960 UpvarCapture::ByUse => CaptureKind::Use,
961 UpvarCapture::ByRef(kind) => match kind {
962 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
963 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
964 CaptureKind::Ref(Mutability::Mut)
965 },
966 },
967 };
968 self.captures
969 .entry(local_id)
970 .and_modify(|e| *e |= capture)
971 .or_insert(capture);
972 }
973 }
974 },
975 ExprKind::Loop(b, ..) => {
976 self.loops.push(e.hir_id);
977 self.visit_block(b);
978 self.loops.pop();
979 },
980 _ => {
981 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
982 walk_expr(self, e);
983 },
984 }
985 }
986
987 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
988 p.each_binding_or_first(&mut |_, id, _, _| {
989 self.locals.insert(id);
990 });
991 }
992 }
993
994 let mut v = V {
995 cx,
996 loops: Vec::new(),
997 locals: HirIdSet::default(),
998 allow_closure: true,
999 captures: HirIdMap::default(),
1000 };
1001 v.visit_expr(expr);
1002 v.allow_closure.then_some(v.captures)
1003}
1004
1005pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1007
1008pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1011 let mut method_names = Vec::with_capacity(max_depth);
1012 let mut arg_lists = Vec::with_capacity(max_depth);
1013 let mut spans = Vec::with_capacity(max_depth);
1014
1015 let mut current = expr;
1016 for _ in 0..max_depth {
1017 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1018 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1019 break;
1020 }
1021 method_names.push(path.ident.name);
1022 arg_lists.push((*receiver, &**args));
1023 spans.push(path.ident.span);
1024 current = receiver;
1025 } else {
1026 break;
1027 }
1028 }
1029
1030 (method_names, arg_lists, spans)
1031}
1032
1033pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1040 let mut current = expr;
1041 let mut matched = Vec::with_capacity(methods.len());
1042 for method_name in methods.iter().rev() {
1043 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1045 if path.ident.name == *method_name {
1046 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1047 return None;
1048 }
1049 matched.push((receiver, args)); current = receiver; } else {
1052 return None;
1053 }
1054 } else {
1055 return None;
1056 }
1057 }
1058 matched.reverse();
1060 Some(matched)
1061}
1062
1063pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1065 cx.tcx
1066 .entry_fn(())
1067 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1068}
1069
1070pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1072 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1073 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1074}
1075
1076pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1078 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1079 match cx.tcx.hir_node_by_def_id(parent_id) {
1080 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1081 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1082 _ => None,
1083 }
1084}
1085
1086pub struct ContainsName<'a, 'tcx> {
1087 pub cx: &'a LateContext<'tcx>,
1088 pub name: Symbol,
1089}
1090
1091impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1092 type Result = ControlFlow<()>;
1093 type NestedFilter = nested_filter::OnlyBodies;
1094
1095 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1096 if self.name == name {
1097 ControlFlow::Break(())
1098 } else {
1099 ControlFlow::Continue(())
1100 }
1101 }
1102
1103 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1104 self.cx.tcx
1105 }
1106}
1107
1108pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1110 let mut cn = ContainsName { cx, name };
1111 cn.visit_expr(expr).is_break()
1112}
1113
1114pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1116 for_each_expr_without_closures(expr, |e| {
1117 if matches!(e.kind, ExprKind::Ret(..)) {
1118 ControlFlow::Break(())
1119 } else {
1120 ControlFlow::Continue(())
1121 }
1122 })
1123 .is_some()
1124}
1125
1126pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1128 get_parent_expr_for_hir(cx, e.hir_id)
1129}
1130
1131pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1134 match cx.tcx.parent_hir_node(hir_id) {
1135 Node::Expr(parent) => Some(parent),
1136 _ => None,
1137 }
1138}
1139
1140pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1142 let enclosing_node = cx
1143 .tcx
1144 .hir_get_enclosing_scope(hir_id)
1145 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1146 enclosing_node.and_then(|node| match node {
1147 Node::Block(block) => Some(block),
1148 Node::Item(&Item {
1149 kind: ItemKind::Fn { body: eid, .. },
1150 ..
1151 })
1152 | Node::ImplItem(&ImplItem {
1153 kind: ImplItemKind::Fn(_, eid),
1154 ..
1155 })
1156 | Node::TraitItem(&TraitItem {
1157 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1158 ..
1159 }) => match cx.tcx.hir_body(eid).value.kind {
1160 ExprKind::Block(block, _) => Some(block),
1161 _ => None,
1162 },
1163 _ => None,
1164 })
1165}
1166
1167pub fn get_enclosing_closure<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Closure<'tcx>> {
1169 cx.tcx.hir_parent_iter(hir_id).find_map(|(_, node)| {
1170 if let Node::Expr(expr) = node
1171 && let ExprKind::Closure(closure) = expr.kind
1172 {
1173 Some(closure)
1174 } else {
1175 None
1176 }
1177 })
1178}
1179
1180pub fn is_upvar_in_closure(cx: &LateContext<'_>, closure: &Closure<'_>, local_id: HirId) -> bool {
1182 cx.typeck_results()
1183 .closure_min_captures
1184 .get(&closure.def_id)
1185 .is_some_and(|x| x.contains_key(&local_id))
1186}
1187
1188pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1190 cx: &LateContext<'tcx>,
1191 expr: &Expr<'_>,
1192) -> Option<&'tcx Expr<'tcx>> {
1193 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1194 match node {
1195 Node::Expr(e) => match e.kind {
1196 ExprKind::Closure { .. }
1197 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1198 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1199
1200 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1202 _ => (),
1203 },
1204 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1205 _ => break,
1206 }
1207 }
1208 None
1209}
1210
1211pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1213 match tcx.hir_parent_iter(id).next() {
1214 Some((
1215 _,
1216 Node::Item(Item {
1217 kind: ItemKind::Impl(imp),
1218 ..
1219 }),
1220 )) => Some(imp),
1221 _ => None,
1222 }
1223}
1224
1225pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1236 while let ExprKind::Block(
1237 Block {
1238 stmts: [],
1239 expr: Some(inner),
1240 rules: BlockCheckMode::DefaultBlock,
1241 ..
1242 },
1243 _,
1244 ) = expr.kind
1245 {
1246 expr = inner;
1247 }
1248 expr
1249}
1250
1251pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1262 while let ExprKind::Block(
1263 Block {
1264 stmts: [],
1265 expr: Some(inner),
1266 rules: BlockCheckMode::DefaultBlock,
1267 ..
1268 }
1269 | Block {
1270 stmts:
1271 [
1272 Stmt {
1273 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1274 ..
1275 },
1276 ],
1277 expr: None,
1278 rules: BlockCheckMode::DefaultBlock,
1279 ..
1280 },
1281 _,
1282 ) = expr.kind
1283 {
1284 expr = inner;
1285 }
1286 expr
1287}
1288
1289pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1291 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1292 match iter.next() {
1293 Some((
1294 _,
1295 Node::Expr(Expr {
1296 kind: ExprKind::If(_, _, Some(else_expr)),
1297 ..
1298 }),
1299 )) => else_expr.hir_id == expr.hir_id,
1300 _ => false,
1301 }
1302}
1303
1304pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1307 hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {
1308 matches!(
1309 node,
1310 Node::LetStmt(LetStmt {
1311 init: Some(init),
1312 els: Some(els),
1313 ..
1314 })
1315 if init.hir_id == child_id || els.hir_id == child_id
1316 )
1317 })
1318}
1319
1320pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1322 hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {
1323 matches!(
1324 node,
1325 Node::LetStmt(LetStmt { els: Some(els), .. })
1326 if els.hir_id == child_id
1327 )
1328 })
1329}
1330
1331pub fn is_full_collection_range(cx: &LateContext<'_>, container: Option<HirId>, expr: &Expr<'_>) -> bool {
1333 if let Some(Range { start, end, ty, .. }) = Range::hir(cx, expr) {
1334 start.is_none_or(|start| is_integer_literal(start, 0))
1335 && end.is_none_or(|end| {
1336 if ty.limits() == RangeLimits::HalfOpen
1337 && let Some(container) = container
1338 && let ExprKind::MethodCall(seg, recv, [], _) = end.kind
1339 {
1340 seg.ident.name == sym::len && recv.res_local_id() == Some(container)
1341 } else {
1342 false
1343 }
1344 })
1345 } else {
1346 false
1347 }
1348}
1349
1350pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1352 if let ExprKind::Lit(spanned) = expr.kind
1353 && let LitKind::Int(v, _) = spanned.node
1354 {
1355 return v == value;
1356 }
1357 false
1358}
1359
1360pub fn is_integer_literal_untyped(expr: &Expr<'_>) -> bool {
1362 if let ExprKind::Lit(spanned) = expr.kind
1363 && let LitKind::Int(_, suffix) = spanned.node
1364 {
1365 return suffix == LitIntType::Unsuffixed;
1366 }
1367
1368 false
1369}
1370
1371pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1373 if let ExprKind::Lit(spanned) = expr.kind
1374 && let LitKind::Float(v, _) = spanned.node
1375 {
1376 v.as_str().parse() == Ok(value)
1377 } else {
1378 false
1379 }
1380}
1381
1382pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1390 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1391}
1392
1393#[must_use]
1397pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1398 loop {
1399 if span.from_expansion() {
1400 let data = span.ctxt().outer_expn_data();
1401 let new_span = data.call_site;
1402
1403 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1404 && mac_name == name
1405 {
1406 return Some(new_span);
1407 }
1408
1409 span = new_span;
1410 } else {
1411 return None;
1412 }
1413 }
1414}
1415
1416#[must_use]
1427pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1428 if span.from_expansion() {
1429 let data = span.ctxt().outer_expn_data();
1430 let new_span = data.call_site;
1431
1432 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1433 && mac_name == name
1434 {
1435 return Some(new_span);
1436 }
1437 }
1438
1439 None
1440}
1441
1442pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1444 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().skip_norm_wip().output();
1445 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1446}
1447
1448pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1450 let arg = cx
1451 .tcx
1452 .fn_sig(fn_def_id)
1453 .instantiate_identity()
1454 .skip_norm_wip()
1455 .input(nth);
1456 cx.tcx.instantiate_bound_regions_with_erased(arg)
1457}
1458
1459pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1461 if let ExprKind::Call(fun, _) = expr.kind
1462 && let ExprKind::Path(ref qp) = fun.kind
1463 {
1464 let res = cx.qpath_res(qp, fun.hir_id);
1465 return match res {
1466 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1467 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1468 _ => false,
1469 };
1470 }
1471 false
1472}
1473
1474pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1477 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1478 !matches!(
1479 cx.qpath_res(qpath, id),
1480 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1481 )
1482 }
1483
1484 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1485 i.into_iter().any(|pat| is_refutable(cx, pat))
1486 }
1487
1488 match pat.kind {
1489 PatKind::Missing => unreachable!(),
1490 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1492 PatKind::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())
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_clauses(cx: &LateContext<'_>, did: DefId) -> bool {
2097 use rustc_trait_selection::traits;
2098 let clauses = cx
2099 .tcx
2100 .clauses_of(did)
2101 .clauses
2102 .iter()
2103 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2104 traits::impossible_clauses(cx.tcx, traits::elaborate(cx.tcx, clauses).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::Static { .. })
2365 && let item = tcx.hir_item(id)
2366 && let ItemKind::Static(_mut, ident, 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 | Node::TestBinderForall(_)
2794 | Node::TestBinderExists(_)
2795 | Node::TestBinderBoundTypeConstraint(_) => {
2796 debug_assert!(false, "found {parent:?} which is after the final use node");
2799 return None;
2800 },
2801 }
2802
2803 ctxt = parent_ctxt;
2804 Some(ExprUseSite {
2805 node: parent,
2806 child_id,
2807 adjustments: mem::replace(&mut adjustments, parent_adjustments),
2808 is_ty_unified: mem::replace(&mut is_ty_unified, false),
2809 moved_before_use: mem::replace(&mut moved_before_use, false),
2810 same_ctxt: mem::replace(&mut same_ctxt, true),
2811 })
2812 },
2813 )
2814}
2815
2816pub fn get_expr_use_site<'tcx>(
2817 tcx: TyCtxt<'tcx>,
2818 typeck: &'tcx TypeckResults<'tcx>,
2819 ctxt: SyntaxContext,
2820 e: &'tcx Expr<'tcx>,
2821) -> ExprUseSite<'tcx> {
2822 expr_use_sites(tcx, typeck, ctxt, e).next().unwrap_or_else(|| {
2825 debug_assert!(false, "failed to find a use site for expr {e:?}");
2826 ExprUseSite {
2827 node: Node::Synthetic, child_id: CRATE_HIR_ID,
2829 adjustments: &[],
2830 is_ty_unified: false,
2831 moved_before_use: false,
2832 same_ctxt: false,
2833 }
2834 })
2835}
2836
2837pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2839 let mut pos = 0;
2840 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2841 let end = pos + t.len;
2842 let range = pos as usize..end as usize;
2843 let inner = InnerSpan::new(range.start, range.end);
2844 pos = end;
2845 (t.kind, s.get(range).unwrap_or_default(), inner)
2846 })
2847}
2848
2849pub fn span_contains_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool {
2852 span.check_text(sm, |snippet| {
2853 tokenize(snippet, FrontmatterAllowed::No).any(|token| {
2854 matches!(
2855 token.kind,
2856 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2857 )
2858 })
2859 })
2860}
2861
2862pub fn span_contains_non_whitespace<'sm>(sm: impl HasSourceMap<'sm>, span: Span, skip_comments: bool) -> bool {
2867 span.check_text(sm, |snippet| {
2868 tokenize_with_text(snippet).any(|(token, _, _)| match token {
2869 TokenKind::Whitespace => false,
2870 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2871 _ => true,
2872 })
2873 })
2874}
2875
2876pub fn span_extract_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> String {
2880 span_extract_comments(sm, span).join("\n")
2881}
2882
2883pub fn span_extract_comments<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Vec<String> {
2887 span.with_source_text(sm, |snippet| {
2888 tokenize_with_text(snippet)
2889 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2890 .map(|(_, s, _)| s.to_string())
2891 .collect::<Vec<_>>()
2892 })
2893 .unwrap_or_default()
2894}
2895
2896pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2897 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2898}
2899
2900pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2925 cx: &LateContext<'_>,
2926 pat: &'a Pat<'hir>,
2927 else_body: &Expr<'_>,
2928) -> Option<&'a Pat<'hir>> {
2929 if let Some([inner_pat]) = as_some_pattern(cx, pat)
2930 && !is_refutable(cx, inner_pat)
2931 && let else_body = peel_blocks(else_body)
2932 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2933 && let ExprKind::Path(ret_path) = ret_val.kind
2934 && cx
2935 .qpath_res(&ret_path, ret_val.hir_id)
2936 .ctor_parent(cx)
2937 .is_lang_item(cx, OptionNone)
2938 {
2939 Some(inner_pat)
2940 } else {
2941 None
2942 }
2943}
2944
2945macro_rules! op_utils {
2946 ($($name:ident $assign:ident)*) => {
2947 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2949
2950 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2952
2953 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2955 match kind {
2956 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2957 _ => None,
2958 }
2959 }
2960 };
2961}
2962
2963op_utils! {
2964 Add AddAssign
2965 Sub SubAssign
2966 Mul MulAssign
2967 Div DivAssign
2968 Rem RemAssign
2969 BitXor BitXorAssign
2970 BitAnd BitAndAssign
2971 BitOr BitOrAssign
2972 Shl ShlAssign
2973 Shr ShrAssign
2974}
2975
2976pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2979 match *pat {
2980 PatKind::Wild => true,
2981 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2982 !visitors::is_local_used(cx, body, id)
2983 },
2984 _ => false,
2985 }
2986}
2987
2988#[derive(Clone, Copy)]
2989pub enum RequiresSemi {
2990 Yes,
2991 No,
2992}
2993impl RequiresSemi {
2994 pub fn requires_semi(self) -> bool {
2995 matches!(self, Self::Yes)
2996 }
2997}
2998
2999#[expect(clippy::too_many_lines)]
3002pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
3003 struct BreakTarget {
3004 id: HirId,
3005 unused: bool,
3006 }
3007
3008 struct V<'cx, 'tcx> {
3009 cx: &'cx LateContext<'tcx>,
3010 break_targets: Vec<BreakTarget>,
3011 break_targets_for_result_ty: u32,
3012 in_final_expr: bool,
3013 requires_semi: bool,
3014 is_never: bool,
3015 }
3016
3017 impl V<'_, '_> {
3018 fn push_break_target(&mut self, id: HirId) {
3019 self.break_targets.push(BreakTarget { id, unused: true });
3020 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3021 }
3022 }
3023
3024 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3025 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3026 if self.is_never && self.break_targets.is_empty() {
3043 if self.in_final_expr && !self.requires_semi {
3044 match e.kind {
3047 ExprKind::DropTemps(e) => self.visit_expr(e),
3048 ExprKind::If(_, then, Some(else_)) => {
3049 self.visit_expr(then);
3050 self.visit_expr(else_);
3051 },
3052 ExprKind::Match(_, arms, _) => {
3053 for arm in arms {
3054 self.visit_expr(arm.body);
3055 }
3056 },
3057 ExprKind::Loop(b, ..) => {
3058 self.push_break_target(e.hir_id);
3059 self.in_final_expr = false;
3060 self.visit_block(b);
3061 self.break_targets.pop();
3062 },
3063 ExprKind::Block(b, _) => {
3064 if b.targeted_by_break {
3065 self.push_break_target(b.hir_id);
3066 self.visit_block(b);
3067 self.break_targets.pop();
3068 } else {
3069 self.visit_block(b);
3070 }
3071 },
3072 _ => {
3073 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3074 },
3075 }
3076 }
3077 return;
3078 }
3079 match e.kind {
3080 ExprKind::DropTemps(e) => self.visit_expr(e),
3081 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3082 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3083 self.in_final_expr = false;
3084 self.visit_expr(e);
3085 self.is_never = true;
3086 },
3087 ExprKind::Break(dest, e) => {
3088 if let Some(e) = e {
3089 self.in_final_expr = false;
3090 self.visit_expr(e);
3091 }
3092 if let Ok(id) = dest.target_id
3093 && let Some((i, target)) = self
3094 .break_targets
3095 .iter_mut()
3096 .enumerate()
3097 .find(|(_, target)| target.id == id)
3098 {
3099 target.unused &= self.is_never;
3100 if i < self.break_targets_for_result_ty as usize {
3101 self.requires_semi = true;
3102 }
3103 }
3104 self.is_never = true;
3105 },
3106 ExprKind::If(cond, then, else_) => {
3107 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3108 self.visit_expr(cond);
3109 self.in_final_expr = in_final_expr;
3110
3111 if self.is_never {
3112 self.visit_expr(then);
3113 if let Some(else_) = else_ {
3114 self.visit_expr(else_);
3115 }
3116 } else {
3117 self.visit_expr(then);
3118 let is_never = mem::replace(&mut self.is_never, false);
3119 if let Some(else_) = else_ {
3120 self.visit_expr(else_);
3121 self.is_never &= is_never;
3122 }
3123 }
3124 },
3125 ExprKind::Match(scrutinee, arms, _) => {
3126 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3127 self.visit_expr(scrutinee);
3128 self.in_final_expr = in_final_expr;
3129
3130 if self.is_never {
3131 for arm in arms {
3132 self.visit_arm(arm);
3133 }
3134 } else {
3135 let mut is_never = true;
3136 for arm in arms {
3137 self.is_never = false;
3138 if let Some(guard) = arm.guard {
3139 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3140 self.visit_expr(guard);
3141 self.in_final_expr = in_final_expr;
3142 self.is_never = false;
3145 }
3146 self.visit_expr(arm.body);
3147 is_never &= self.is_never;
3148 }
3149 self.is_never = is_never;
3150 }
3151 },
3152 ExprKind::Loop(b, _, _, _) => {
3153 self.push_break_target(e.hir_id);
3154 self.in_final_expr = false;
3155 self.visit_block(b);
3156 self.is_never = self.break_targets.pop().unwrap().unused;
3157 },
3158 ExprKind::Block(b, _) => {
3159 if b.targeted_by_break {
3160 self.push_break_target(b.hir_id);
3161 self.visit_block(b);
3162 self.is_never &= self.break_targets.pop().unwrap().unused;
3163 } else {
3164 self.visit_block(b);
3165 }
3166 },
3167 _ => {
3168 self.in_final_expr = false;
3169 walk_expr(self, e);
3170 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3171 },
3172 }
3173 }
3174
3175 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3176 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3177 for s in b.stmts {
3178 self.visit_stmt(s);
3179 }
3180 self.in_final_expr = in_final_expr;
3181 if let Some(e) = b.expr {
3182 self.visit_expr(e);
3183 }
3184 }
3185
3186 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3187 if let Some(e) = l.init {
3188 self.visit_expr(e);
3189 }
3190 if let Some(else_) = l.els {
3191 let is_never = self.is_never;
3192 self.visit_block(else_);
3193 self.is_never = is_never;
3194 }
3195 }
3196
3197 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3198 if let Some(guard) = arm.guard {
3199 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3200 self.visit_expr(guard);
3201 self.in_final_expr = in_final_expr;
3202 }
3203 self.visit_expr(arm.body);
3204 }
3205 }
3206
3207 if cx.typeck_results().expr_ty(e).is_never() {
3208 Some(RequiresSemi::No)
3209 } else if let ExprKind::Block(b, _) = e.kind
3210 && !b.targeted_by_break
3211 && b.expr.is_none()
3212 {
3213 None
3215 } else {
3216 let mut v = V {
3217 cx,
3218 break_targets: Vec::new(),
3219 break_targets_for_result_ty: 0,
3220 in_final_expr: true,
3221 requires_semi: false,
3222 is_never: false,
3223 };
3224 v.visit_expr(e);
3225 v.is_never
3226 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3227 RequiresSemi::Yes
3228 } else {
3229 RequiresSemi::No
3230 })
3231 }
3232}
3233
3234pub fn get_path_from_caller_to_method_type<'tcx>(
3240 tcx: TyCtxt<'tcx>,
3241 from: LocalDefId,
3242 method: DefId,
3243 args: GenericArgsRef<'tcx>,
3244) -> String {
3245 let assoc_item = tcx.associated_item(method);
3246 let def_id = assoc_item.container_id(tcx);
3247 match assoc_item.container {
3248 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3249 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3250 let ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
3251 get_path_to_ty(tcx, from, ty, args)
3252 },
3253 }
3254}
3255
3256fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3257 match ty.kind() {
3258 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3259 rustc_ty::Array(..)
3261 | rustc_ty::Dynamic(..)
3262 | rustc_ty::Never
3263 | rustc_ty::RawPtr(_, _)
3264 | rustc_ty::Ref(..)
3265 | rustc_ty::Slice(_)
3266 | rustc_ty::Tuple(_) => format!(
3267 "<{}>",
3268 EarlyBinder::bind(tcx, ty).instantiate(tcx, args).skip_norm_wip()
3269 ),
3270 _ => ty.to_string(),
3271 }
3272}
3273
3274fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3276 if callee.is_local() {
3278 let callee_path = tcx.def_path(callee);
3279 let caller_path = tcx.def_path(from.to_def_id());
3280 maybe_get_relative_path(&caller_path, &callee_path, 2)
3281 } else {
3282 tcx.def_path_str(callee)
3283 }
3284}
3285
3286fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3299 use itertools::EitherOrBoth::{Both, Left, Right};
3300
3301 let unique_parts = to
3303 .data
3304 .iter()
3305 .zip_longest(from.data.iter())
3306 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3307 .map(|el| match el {
3308 Both(l, r) => Both(l.data, r.data),
3309 Left(l) => Left(l.data),
3310 Right(r) => Right(r.data),
3311 });
3312
3313 let mut go_up_by = 0;
3315 let mut path = Vec::new();
3316 for el in unique_parts {
3317 match el {
3318 Both(l, r) => {
3319 if let DefPathData::TypeNs(sym) = l {
3329 path.push(sym);
3330 }
3331 if let DefPathData::TypeNs(_) = r {
3332 go_up_by += 1;
3333 }
3334 },
3335 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3340 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3345 _ => {},
3346 }
3347 }
3348
3349 if go_up_by > max_super {
3350 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3352 if let DefPathData::TypeNs(sym) = el.data {
3353 Some(sym)
3354 } else {
3355 None
3356 }
3357 })))
3358 } else if go_up_by == 0 && path.is_empty() {
3359 String::from("Self")
3360 } else {
3361 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3362 }
3363}
3364
3365pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3368 matches!(
3369 cx.tcx.parent_hir_node(id),
3370 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3371 )
3372}
3373
3374pub fn is_block_like(expr: &Expr<'_>) -> bool {
3377 matches!(
3378 expr.kind,
3379 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3380 )
3381}
3382
3383pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3385 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3386 match expr.kind {
3387 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3388 _ if is_block_like(expr) => is_operand,
3389 _ => false,
3390 }
3391 }
3392
3393 contains_block(expr, false)
3394}
3395
3396pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3398 if let Some(parent_expr) = get_parent_expr(cx, expr)
3399 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3400 && receiver.hir_id == expr.hir_id
3401 {
3402 return true;
3403 }
3404 false
3405}
3406
3407pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3410 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3411 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3412 && temporary_ty
3413 .walk()
3414 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3415 {
3416 ControlFlow::Break(())
3417 } else {
3418 ControlFlow::Continue(())
3419 }
3420 })
3421 .is_break()
3422}
3423
3424pub fn leaks_droppable_temporary<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3427 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3428 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) {
3429 ControlFlow::Break(())
3430 } else {
3431 ControlFlow::Continue(())
3432 }
3433 })
3434 .is_break()
3435}
3436
3437pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3448 let expr_ty_is_adjusted = cx
3449 .typeck_results()
3450 .expr_adjustments(expr)
3451 .iter()
3452 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3454 if expr_ty_is_adjusted {
3455 return true;
3456 }
3457
3458 match expr.kind {
3461 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3462 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip();
3463
3464 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3465 return false;
3466 }
3467
3468 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3469 let mut args_with_ty_param = {
3470 fn_sig
3471 .inputs()
3472 .skip_binder()
3473 .iter()
3474 .skip(self_arg_count)
3475 .zip(args)
3476 .filter_map(|(arg_ty, arg)| {
3477 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3478 Some(arg)
3479 } else {
3480 None
3481 }
3482 })
3483 };
3484 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3485 },
3486 ExprKind::Struct(qpath, _, _) => {
3488 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3489 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3490 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3491 return true;
3493 };
3494 v_def
3495 .fields
3496 .iter()
3497 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3498 } else {
3499 false
3500 }
3501 },
3502 ExprKind::Block(
3504 &Block {
3505 expr: Some(ret_expr), ..
3506 },
3507 _,
3508 )
3509 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3510
3511 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3513 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3515 ExprKind::If(_, then, maybe_else) => {
3517 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3518 },
3519 ExprKind::Match(_, arms, _) => arms
3520 .iter()
3521 .map(|arm| arm.body)
3522 .any(|body| expr_requires_coercion(cx, body)),
3523 _ => false,
3524 }
3525}
3526
3527pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3530 if let Some(hir_id) = expr.res_local_id()
3531 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3532 {
3533 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3534 } else if let ExprKind::Path(p) = &expr.kind
3535 && let Some(mutability) = cx
3536 .qpath_res(p, expr.hir_id)
3537 .opt_def_id()
3538 .and_then(|id| cx.tcx.static_mutability(id))
3539 {
3540 mutability == Mutability::Mut
3541 } else if let ExprKind::Field(parent, _) = expr.kind {
3542 is_mutable(cx, parent)
3543 } else {
3544 true
3545 }
3546}
3547
3548pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3551 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3552 return hir_ty;
3553 };
3554 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3555 && let Some(segment) = path.segments.last()
3556 && segment.ident.name == sym::Option
3557 && let Res::Def(DefKind::Enum, def_id) = segment.res
3558 && def_id == option_def_id
3559 && let [GenericArg::Type(arg_ty)] = segment.args().args
3560 {
3561 hir_ty = arg_ty.as_unambig_ty();
3562 }
3563 hir_ty
3564}
3565
3566pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3569 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3570 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3571 && let ctxt = expr.span.ctxt()
3572 && for_each_expr_without_closures(into_future_arg, |e| {
3573 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3574 })
3575 .is_none()
3576 {
3577 Some(into_future_arg)
3578 } else {
3579 None
3580 }
3581}
3582
3583pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3585 if let ExprKind::Call(fn_expr, []) = &expr.kind
3586 && let ExprKind::Path(qpath) = &fn_expr.kind
3587 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3588 {
3589 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3590 } else {
3591 false
3592 }
3593}
3594
3595pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3611 let enclosing_body_owner = cx
3612 .tcx
3613 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3614 let mut prev_id = expr.hir_id;
3615 let mut skip_until_id = None;
3616 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3617 if hir_id == enclosing_body_owner {
3618 return true;
3619 }
3620 if let Some(id) = skip_until_id {
3621 prev_id = hir_id;
3622 if id == hir_id {
3623 skip_until_id = None;
3624 }
3625 continue;
3626 }
3627 match node {
3628 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3629 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3630 Node::Expr(expr) => match expr.kind {
3631 ExprKind::Ret(_) => return true,
3632 ExprKind::If(_, then, opt_else)
3633 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3634 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3635 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3636 ExprKind::Break(
3637 Destination {
3638 target_id: Ok(target_id),
3639 ..
3640 },
3641 _,
3642 ) => skip_until_id = Some(target_id),
3643 _ => break,
3644 },
3645 _ => break,
3646 }
3647 prev_id = hir_id;
3648 }
3649
3650 false
3653}
3654
3655pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3658 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3659 matches!(
3660 adj.kind,
3661 Adjust::Deref(DerefAdjustKind::Overloaded(_))
3662 | Adjust::Pointer(PointerCoercion::Unsize)
3663 | Adjust::NeverToAny
3664 )
3665 })
3666}
3667
3668pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3670 matches!(
3671 expr.kind,
3672 ExprKind::Closure(Closure {
3673 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3674 CoroutineDesugaring::Async,
3675 CoroutineSource::Block
3676 )),
3677 ..
3678 })
3679 )
3680}
3681
3682pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3684 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3685}
3686
3687#[inline]
3690pub fn hir_parent_with_src_iter(tcx: TyCtxt<'_>, mut id: HirId) -> impl Iterator<Item = (Node<'_>, HirId)> {
3691 tcx.hir_parent_id_iter(id)
3692 .map(move |parent| (tcx.hir_node(parent), mem::replace(&mut id, parent)))
3693}