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())
1708}
1709
1710pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1715 let mut conds = Vec::new();
1716 let mut blocks: Vec<&Block<'_>> = Vec::new();
1717
1718 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1719 conds.push(cond);
1720 if let ExprKind::Block(block, _) = then.kind {
1721 blocks.push(block);
1722 } else {
1723 panic!("ExprKind::If node is not an ExprKind::Block");
1724 }
1725
1726 if let Some(else_expr) = r#else {
1727 expr = else_expr;
1728 } else {
1729 break;
1730 }
1731 }
1732
1733 if !blocks.is_empty()
1735 && let ExprKind::Block(block, _) = expr.kind
1736 {
1737 blocks.push(block);
1738 }
1739
1740 (conds, blocks)
1741}
1742
1743pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1745 if let ExprKind::Closure(&Closure {
1746 body,
1747 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1748 ..
1749 }) = expr.kind
1750 && let ExprKind::Block(
1751 Block {
1752 expr:
1753 Some(Expr {
1754 kind: ExprKind::DropTemps(inner_expr),
1755 ..
1756 }),
1757 ..
1758 },
1759 _,
1760 ) = tcx.hir_body(body).value.kind
1761 {
1762 Some(inner_expr)
1763 } else {
1764 None
1765 }
1766}
1767
1768pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1770 get_async_closure_expr(tcx, body.value)
1771}
1772
1773pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1775 let did = match expr.kind {
1776 ExprKind::Call(path, _) => {
1777 if let ExprKind::Path(ref qpath) = path.kind
1778 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1779 {
1780 Some(did)
1781 } else {
1782 None
1783 }
1784 },
1785 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1786 _ => None,
1787 };
1788
1789 did.is_some_and(|did| find_attr!(cx.tcx, did, MustUse { .. }))
1790}
1791
1792fn is_body_identity_function<'hir>(cx: &LateContext<'_>, func: &Body<'hir>) -> bool {
1806 let [param] = func.params else {
1807 return false;
1808 };
1809
1810 let mut param_pat = param.pat;
1811
1812 let mut advance_param_pat_over_stmts = |stmts: &[Stmt<'hir>]| {
1819 for stmt in stmts {
1820 if let StmtKind::Let(local) = stmt.kind
1821 && let Some(init) = local.init
1822 && is_expr_identity_of_pat(cx, param_pat, init, true)
1823 {
1824 param_pat = local.pat;
1825 } else {
1826 return false;
1827 }
1828 }
1829
1830 true
1831 };
1832
1833 let mut expr = func.value;
1834 loop {
1835 match expr.kind {
1836 ExprKind::Block(
1837 &Block {
1838 stmts: [],
1839 expr: Some(e),
1840 ..
1841 },
1842 _,
1843 )
1844 | ExprKind::Ret(Some(e)) => expr = e,
1845 ExprKind::Block(
1846 &Block {
1847 stmts: [stmt],
1848 expr: None,
1849 ..
1850 },
1851 _,
1852 ) => {
1853 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1854 && let ExprKind::Ret(Some(ret_val)) = e.kind
1855 {
1856 expr = ret_val;
1857 } else {
1858 return false;
1859 }
1860 },
1861 ExprKind::Block(
1862 &Block {
1863 stmts, expr: Some(e), ..
1864 },
1865 _,
1866 ) => {
1867 if !advance_param_pat_over_stmts(stmts) {
1868 return false;
1869 }
1870
1871 expr = e;
1872 },
1873 ExprKind::Block(&Block { stmts, expr: None, .. }, _) => {
1874 if let Some((last_stmt, stmts)) = stmts.split_last()
1875 && advance_param_pat_over_stmts(stmts)
1876 && let StmtKind::Semi(e) | StmtKind::Expr(e) = last_stmt.kind
1877 && let ExprKind::Ret(Some(ret_val)) = e.kind
1878 {
1879 expr = ret_val;
1880 } else {
1881 return false;
1882 }
1883 },
1884 _ => return is_expr_identity_of_pat(cx, param_pat, expr, true),
1885 }
1886 }
1887}
1888
1889pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1899 if cx
1900 .typeck_results()
1901 .pat_binding_modes()
1902 .get(pat.hir_id)
1903 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..)))
1904 {
1905 return false;
1909 }
1910
1911 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1913
1914 match (pat.kind, expr.kind) {
1915 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1916 expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1917 },
1918 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1919 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1920 },
1921 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1922 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1923 {
1924 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1925 },
1926 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1927 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1928 },
1929 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1930 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1931 {
1932 if let ExprKind::Path(ident) = &ident.kind
1934 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1935 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
1937 {
1938 true
1939 } else {
1940 false
1941 }
1942 },
1943 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
1944 if field_pats.len() == fields.len() =>
1945 {
1946 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1948 && unordered_over(field_pats, fields, |field_pat, field| {
1950 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
1951 })
1952 },
1953 _ => false,
1954 }
1955}
1956
1957pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1962 match expr.kind {
1963 ExprKind::Closure(&Closure { body, fn_decl, .. })
1964 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1965 {
1966 is_body_identity_function(cx, cx.tcx.hir_body(body))
1967 },
1968 ExprKind::Path(QPath::Resolved(_, path))
1969 if path.segments.iter().all(|seg| seg.infer_args)
1970 && let Some(did) = path.res.opt_def_id() =>
1971 {
1972 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1973 },
1974 _ => false,
1975 }
1976}
1977
1978pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1987 match expr.kind {
1988 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1989 _ => expr.basic_res().is_diag_item(cx, sym::convert_identity),
1990 }
1991}
1992
1993pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
1996 for (node, child_id) in hir_parent_with_src_iter(tcx, expr.hir_id) {
1997 match node {
1998 Node::Block(_) => {},
1999 Node::Arm(arm) if arm.body.hir_id == child_id => {},
2000 Node::Expr(expr) => match expr.kind {
2001 ExprKind::Block(..) | ExprKind::DropTemps(_) => {},
2002 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => {},
2003 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => return None,
2004 _ => return Some((Node::Expr(expr), child_id)),
2005 },
2006 node => return Some((node, child_id)),
2007 }
2008 }
2009 None
2010}
2011
2012pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2014 !matches!(
2015 get_expr_use_or_unification_node(tcx, expr),
2016 None | Some((
2017 Node::Stmt(Stmt {
2018 kind: StmtKind::Expr(_)
2019 | StmtKind::Semi(_)
2020 | StmtKind::Let(LetStmt {
2021 pat: Pat {
2022 kind: PatKind::Wild,
2023 ..
2024 },
2025 ..
2026 }),
2027 ..
2028 }),
2029 _
2030 ))
2031 )
2032}
2033
2034pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2036 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2037}
2038
2039pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2043 !expr.is_place_expr(|base| {
2044 cx.typeck_results()
2045 .adjustments()
2046 .get(base.hir_id)
2047 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2048 })
2049}
2050
2051pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2052 if is_no_core_crate(cx) {
2053 None
2054 } else if is_no_std_crate(cx) {
2055 Some("core")
2056 } else {
2057 Some("std")
2058 }
2059}
2060
2061pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2062 find_attr!(cx.tcx, crate, NoStd)
2063}
2064
2065pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2066 find_attr!(cx.tcx, crate, NoCore)
2067}
2068
2069pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2079 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2080 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2081 } else {
2082 false
2083 }
2084}
2085
2086pub fn fn_has_unsatisfiable_clauses(cx: &LateContext<'_>, did: DefId) -> bool {
2096 use rustc_trait_selection::traits;
2097 let clauses = cx
2098 .tcx
2099 .clauses_of(did)
2100 .clauses
2101 .iter()
2102 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2103 traits::impossible_clauses(cx.tcx, traits::elaborate(cx.tcx, clauses).collect::<Vec<_>>())
2104}
2105
2106pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2108 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2109}
2110
2111pub fn fn_def_id_with_node_args<'tcx>(
2114 cx: &LateContext<'tcx>,
2115 expr: &Expr<'_>,
2116) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2117 let typeck = cx.typeck_results();
2118 match &expr.kind {
2119 ExprKind::MethodCall(..) => Some((
2120 typeck.type_dependent_def_id(expr.hir_id)?,
2121 typeck.node_args(expr.hir_id),
2122 )),
2123 ExprKind::Call(
2124 Expr {
2125 kind: ExprKind::Path(qpath),
2126 hir_id: path_hir_id,
2127 ..
2128 },
2129 ..,
2130 ) => {
2131 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2134 typeck.qpath_res(qpath, *path_hir_id)
2135 {
2136 Some((id, typeck.node_args(*path_hir_id)))
2137 } else {
2138 None
2139 }
2140 },
2141 _ => None,
2142 }
2143}
2144
2145pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2150 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2151 let expr_kind = expr_type.kind();
2152 let is_primitive = match expr_kind {
2153 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2154 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2155 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2156 is_recursively_primitive_type(*element_type)
2157 } else {
2158 unreachable!()
2159 }
2160 },
2161 _ => false,
2162 };
2163
2164 if is_primitive {
2165 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2168 rustc_ty::Slice(..) => return Some("slice".into()),
2169 rustc_ty::Array(..) => return Some("array".into()),
2170 rustc_ty::Tuple(..) => return Some("tuple".into()),
2171 _ => {
2172 let refs_peeled = expr_type.peel_refs();
2175 return Some(refs_peeled.walk().last().unwrap().to_string());
2176 },
2177 }
2178 }
2179 None
2180}
2181
2182pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2190where
2191 Hash: FnMut(&T) -> u64,
2192 Eq: FnMut(&T, &T) -> bool,
2193{
2194 match exprs {
2195 [a, b] if eq(a, b) => return vec![vec![a, b]],
2196 _ if exprs.len() <= 2 => return vec![],
2197 _ => {},
2198 }
2199
2200 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2201
2202 for expr in exprs {
2203 match buckets.entry(hash(expr)) {
2204 indexmap::map::Entry::Occupied(mut o) => {
2205 let bucket = o.get_mut();
2206 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2207 Some(group) => group.push(expr),
2208 None => bucket.push(vec![expr]),
2209 }
2210 },
2211 indexmap::map::Entry::Vacant(v) => {
2212 v.insert(vec![vec![expr]]);
2213 },
2214 }
2215 }
2216
2217 buckets
2218 .into_values()
2219 .flatten()
2220 .filter(|group| group.len() > 1)
2221 .collect()
2222}
2223
2224pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2227 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2228 if let PatKind::Ref(pat, _, _) = pat.kind {
2229 peel(pat, count + 1)
2230 } else {
2231 (pat, count)
2232 }
2233 }
2234 peel(pat, 0)
2235}
2236
2237pub fn peel_hir_expr_while<'tcx>(
2239 mut expr: &'tcx Expr<'tcx>,
2240 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2241) -> &'tcx Expr<'tcx> {
2242 while let Some(e) = f(expr) {
2243 expr = e;
2244 }
2245 expr
2246}
2247
2248pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2251 let mut remaining = count;
2252 let e = peel_hir_expr_while(expr, |e| match e.kind {
2253 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2254 remaining -= 1;
2255 Some(e)
2256 },
2257 _ => None,
2258 });
2259 (e, count - remaining)
2260}
2261
2262pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2265 let mut count: usize = 0;
2266 let mut curr_expr = expr;
2267 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2268 count = count.wrapping_add(1);
2269 curr_expr = local_expr;
2270 }
2271 (curr_expr, count)
2272}
2273
2274pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2277 let mut count = 0;
2278 let e = peel_hir_expr_while(expr, |e| match e.kind {
2279 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2280 count += 1;
2281 Some(e)
2282 },
2283 _ => None,
2284 });
2285 (e, count)
2286}
2287
2288pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2291 let mut count = 0;
2292 loop {
2293 match &ty.kind {
2294 TyKind::Ref(_, ref_ty) => {
2295 ty = ref_ty.ty;
2296 count += 1;
2297 },
2298 _ => break (ty, count),
2299 }
2300 }
2301}
2302
2303pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
2305 match &ty.kind {
2306 TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty),
2307 _ => ty,
2308 }
2309}
2310
2311pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2314 loop {
2315 match expr.kind {
2316 ExprKind::AddrOf(_, _, e) => expr = e,
2317 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2318 _ => break,
2319 }
2320 }
2321 expr
2322}
2323
2324pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2327 let mut operators = Vec::new();
2328 peel_hir_expr_while(expr, |expr| match expr.kind {
2329 ExprKind::AddrOf(_, _, e) => {
2330 operators.push(expr);
2331 Some(e)
2332 },
2333 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2334 operators.push(expr);
2335 Some(e)
2336 },
2337 _ => None,
2338 });
2339 operators
2340}
2341
2342pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2343 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2344 && let Res::Def(_, def_id) = path.res
2345 {
2346 return find_attr!(cx.tcx, def_id, CfgTrace(..) | CfgAttrTrace(..));
2347 }
2348 false
2349}
2350
2351static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModId, Vec<Symbol>>>> = OnceLock::new();
2352
2353fn test_item_names(tcx: TyCtxt<'_>, module: LocalModId) -> Vec<Symbol> {
2356 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2357 let mut map = cache.lock().unwrap();
2358 match map.entry(module) {
2359 Entry::Occupied(entry) => entry.get().clone(),
2360 Entry::Vacant(entry) => {
2361 let mut names = Vec::new();
2362 for id in tcx.hir_module_free_items(module) {
2363 if matches!(tcx.def_kind(id.owner_id), DefKind::Const { .. })
2364 && let item = tcx.hir_item(id)
2365 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2366 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2367 && let Res::Def(DefKind::Struct, _) = path.res
2369 && find_attr!(tcx, item.hir_id(), RustcTestMarker(..))
2370 {
2371 names.push(ident.name);
2372 }
2373 }
2374 names.sort_unstable();
2375 entry.insert(names).clone()
2376 },
2377 }
2378}
2379
2380pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2384 let names = test_item_names(tcx, tcx.parent_module(id));
2385 if names.is_empty() {
2387 return false;
2388 }
2389 once((id, tcx.hir_node(id)))
2390 .chain(tcx.hir_parent_iter(id))
2391 .any(|(_id, node)| {
2394 if let Node::Item(item) = node
2395 && let ItemKind::Fn { ident, .. } = item.kind
2396 {
2397 return names.binary_search(&ident.name).is_ok();
2400 }
2401 false
2402 })
2403}
2404
2405pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2412 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2413 if let Node::Item(item) = tcx.hir_node(id)
2414 && let ItemKind::Fn { ident, .. } = item.kind
2415 {
2416 test_item_names(tcx, tcx.parent_module(id))
2417 .binary_search(&ident.name)
2418 .is_ok()
2419 } else {
2420 false
2421 }
2422}
2423
2424pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2429 if let Some(cfgs) = find_attr!(tcx, id, CfgTrace(cfgs) => cfgs)
2430 && cfgs
2431 .iter()
2432 .any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. }))
2433 {
2434 true
2435 } else {
2436 false
2437 }
2438}
2439
2440pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2442 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2443}
2444
2445pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2447 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2448}
2449
2450pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2452 find_attr!(tcx, def_id, CfgTrace(..))
2453 || find_attr!(
2454 tcx.hir_parent_id_iter(tcx.local_def_id_to_hir_id(def_id))
2455 .flat_map(|parent_id| tcx.hir_attrs(parent_id)),
2456 CfgTrace(..)
2457 )
2458}
2459
2460#[derive(Clone, Copy)]
2462pub enum DefinedTy<'tcx> {
2463 Hir(&'tcx hir::Ty<'tcx>),
2465 Mir {
2473 def_site_def_id: Option<DefId>,
2474 ty: Binder<'tcx, Ty<'tcx>>,
2475 },
2476}
2477
2478pub struct ExprUseSite<'tcx> {
2480 pub node: Node<'tcx>,
2482 pub child_id: HirId,
2484 pub adjustments: &'tcx [Adjustment<'tcx>],
2486 pub is_ty_unified: bool,
2488 pub moved_before_use: bool,
2490 pub same_ctxt: bool,
2492}
2493impl<'tcx> ExprUseSite<'tcx> {
2494 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2495 match self.node {
2496 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2497 Node::ExprField(field) => ExprUseNode::Field(field),
2498
2499 Node::Item(&Item {
2500 kind: ItemKind::Static(..) | ItemKind::Const(..),
2501 owner_id,
2502 ..
2503 })
2504 | Node::TraitItem(&TraitItem {
2505 kind: TraitItemKind::Const(..),
2506 owner_id,
2507 ..
2508 })
2509 | Node::ImplItem(&ImplItem {
2510 kind: ImplItemKind::Const(..),
2511 owner_id,
2512 ..
2513 }) => ExprUseNode::ConstStatic(owner_id),
2514
2515 Node::Item(&Item {
2516 kind: ItemKind::Fn { .. },
2517 owner_id,
2518 ..
2519 })
2520 | Node::TraitItem(&TraitItem {
2521 kind: TraitItemKind::Fn(..),
2522 owner_id,
2523 ..
2524 })
2525 | Node::ImplItem(&ImplItem {
2526 kind: ImplItemKind::Fn(..),
2527 owner_id,
2528 ..
2529 }) => ExprUseNode::Return(owner_id),
2530
2531 Node::Expr(use_expr) => match use_expr.kind {
2532 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2533 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2534 }),
2535
2536 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2537 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2538 Some(i) => ExprUseNode::FnArg(func, i),
2539 None => ExprUseNode::Callee,
2540 },
2541 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2542 use_expr.hir_id,
2543 name.args,
2544 args.iter()
2545 .position(|arg| arg.hir_id == self.child_id)
2546 .map_or(0, |i| i + 1),
2547 ),
2548 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2549 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2550 _ => ExprUseNode::Other,
2551 },
2552 _ => ExprUseNode::Other,
2553 }
2554 }
2555}
2556
2557pub enum ExprUseNode<'tcx> {
2559 LetStmt(&'tcx LetStmt<'tcx>),
2561 ConstStatic(OwnerId),
2563 Return(OwnerId),
2565 Field(&'tcx ExprField<'tcx>),
2567 FnArg(&'tcx Expr<'tcx>, usize),
2569 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2571 Callee,
2573 FieldAccess(Ident),
2575 AddrOf(ast::BorrowKind, Mutability),
2577 Other,
2578}
2579impl<'tcx> ExprUseNode<'tcx> {
2580 pub fn is_return(&self) -> bool {
2582 matches!(self, Self::Return(_))
2583 }
2584
2585 pub fn is_recv(&self) -> bool {
2587 matches!(self, Self::MethodArg(_, _, 0))
2588 }
2589
2590 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2592 match *self {
2593 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2594 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2595 def_site_def_id: Some(id.def_id.to_def_id()),
2596 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity().skip_norm_wip()),
2597 }),
2598 Self::Return(id) => {
2599 if let Node::Expr(Expr {
2600 kind: ExprKind::Closure(c),
2601 ..
2602 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2603 {
2604 match c.fn_decl.output {
2605 FnRetTy::DefaultReturn(_) => None,
2606 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2607 }
2608 } else {
2609 let ty = cx.tcx.fn_sig(id).instantiate_identity().skip_norm_wip().output();
2610 Some(DefinedTy::Mir {
2611 def_site_def_id: Some(id.def_id.to_def_id()),
2612 ty,
2613 })
2614 }
2615 },
2616 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2617 Some(Expr {
2618 hir_id,
2619 kind: ExprKind::Struct(path, ..),
2620 ..
2621 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2622 .and_then(|(adt, variant)| {
2623 variant
2624 .fields
2625 .iter()
2626 .find(|f| f.name == field.ident.name)
2627 .map(|f| (adt, f))
2628 })
2629 .map(|(adt, field_def)| DefinedTy::Mir {
2630 def_site_def_id: Some(adt.did()),
2631 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity().skip_norm_wip()),
2632 }),
2633 _ => None,
2634 },
2635 Self::FnArg(callee, i) => {
2636 let sig = expr_sig(cx, callee)?;
2637 let (hir_ty, ty) = sig.input_with_hir(i)?;
2638 Some(match hir_ty {
2639 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2640 None => DefinedTy::Mir {
2641 def_site_def_id: sig.predicates_id(),
2642 ty,
2643 },
2644 })
2645 },
2646 Self::MethodArg(id, _, i) => {
2647 let id = cx.typeck_results().type_dependent_def_id(id)?;
2648 let sig = cx.tcx.fn_sig(id).skip_binder();
2649 Some(DefinedTy::Mir {
2650 def_site_def_id: Some(id),
2651 ty: sig.input(i),
2652 })
2653 },
2654 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2655 }
2656 }
2657}
2658
2659struct ReplacingFilterMap<I, F>(I, F);
2660impl<I, F, U> Iterator for ReplacingFilterMap<I, F>
2661where
2662 I: Iterator,
2663 F: FnMut(&mut I, I::Item) -> Option<U>,
2664{
2665 type Item = U;
2666 fn next(&mut self) -> Option<U> {
2667 while let Some(x) = self.0.next() {
2668 if let Some(x) = (self.1)(&mut self.0, x) {
2669 return Some(x);
2670 }
2671 }
2672 None
2673 }
2674}
2675
2676#[expect(clippy::too_many_lines)]
2679pub fn expr_use_sites<'tcx>(
2680 tcx: TyCtxt<'tcx>,
2681 typeck: &'tcx TypeckResults<'tcx>,
2682 mut ctxt: SyntaxContext,
2683 e: &'tcx Expr<'tcx>,
2684) -> impl Iterator<Item = ExprUseSite<'tcx>> {
2685 let mut adjustments: &[_] = typeck.expr_adjustments(e);
2686 let mut is_ty_unified = false;
2687 let mut moved_before_use = false;
2688 let mut same_ctxt = true;
2689 ReplacingFilterMap(
2690 hir_parent_with_src_iter(tcx, e.hir_id),
2691 move |iter: &mut _, (parent, child_id)| {
2692 let parent_ctxt;
2693 let mut parent_adjustments: &[_] = &[];
2694 match parent {
2695 Node::Expr(parent_expr) => {
2696 parent_ctxt = parent_expr.span.ctxt();
2697 same_ctxt &= parent_ctxt == ctxt;
2698 parent_adjustments = typeck.expr_adjustments(parent_expr);
2699 match parent_expr.kind {
2700 ExprKind::Match(scrutinee, arms, _) if scrutinee.hir_id != child_id => {
2701 is_ty_unified |= arms.len() != 1;
2702 moved_before_use = true;
2703 if adjustments.is_empty() {
2704 adjustments = parent_adjustments;
2705 }
2706 return None;
2707 },
2708 ExprKind::If(cond, _, else_) if cond.hir_id != child_id => {
2709 is_ty_unified |= else_.is_some();
2710 moved_before_use = true;
2711 if adjustments.is_empty() {
2712 adjustments = parent_adjustments;
2713 }
2714 return None;
2715 },
2716 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2717 is_ty_unified = true;
2718 moved_before_use = true;
2719 *iter = hir_parent_with_src_iter(tcx, id);
2720 if adjustments.is_empty() {
2721 adjustments = parent_adjustments;
2722 }
2723 return None;
2724 },
2725 ExprKind::Block(b, _) => {
2726 is_ty_unified |= b.targeted_by_break;
2727 moved_before_use = true;
2728 if adjustments.is_empty() {
2729 adjustments = parent_adjustments;
2730 }
2731 return None;
2732 },
2733 ExprKind::DropTemps(_) | ExprKind::Type(..) => {
2734 if adjustments.is_empty() {
2735 adjustments = parent_adjustments;
2736 }
2737 return None;
2738 },
2739 _ => {},
2740 }
2741 },
2742 Node::Arm(arm) => {
2743 parent_ctxt = arm.span.ctxt();
2744 same_ctxt &= parent_ctxt == ctxt;
2745 if arm.body.hir_id == child_id {
2746 return None;
2747 }
2748 },
2749 Node::Block(b) => {
2750 same_ctxt &= b.span.ctxt() == ctxt;
2751 return None;
2752 },
2753 Node::ConstBlock(_) => parent_ctxt = ctxt,
2754 Node::ExprField(&ExprField { span, .. }) => {
2755 parent_ctxt = span.ctxt();
2756 same_ctxt &= parent_ctxt == ctxt;
2757 },
2758 Node::AnonConst(&AnonConst { span, .. })
2759 | Node::ConstArg(&ConstArg { span, .. })
2760 | Node::Field(&FieldDef { span, .. })
2761 | Node::ImplItem(&ImplItem { span, .. })
2762 | Node::Item(&Item { span, .. })
2763 | Node::LetStmt(&LetStmt { span, .. })
2764 | Node::Stmt(&Stmt { span, .. })
2765 | Node::TraitItem(&TraitItem { span, .. })
2766 | Node::Variant(&Variant { span, .. }) => {
2767 parent_ctxt = span.ctxt();
2768 same_ctxt &= parent_ctxt == ctxt;
2769 *iter = hir_parent_with_src_iter(tcx, CRATE_HIR_ID);
2770 },
2771 Node::AssocItemConstraint(_)
2772 | Node::ConstArgExprField(_)
2773 | Node::Crate(_)
2774 | Node::Ctor(_)
2775 | Node::Err(_)
2776 | Node::ForeignItem(_)
2777 | Node::GenericParam(_)
2778 | Node::Infer(_)
2779 | Node::Lifetime(_)
2780 | Node::OpaqueTy(_)
2781 | Node::Param(_)
2782 | Node::Pat(_)
2783 | Node::PatExpr(_)
2784 | Node::PatField(_)
2785 | Node::PathSegment(_)
2786 | Node::PreciseCapturingNonLifetimeArg(_)
2787 | Node::Synthetic
2788 | Node::TraitRef(_)
2789 | Node::Ty(_)
2790 | Node::TyPat(_)
2791 | Node::WherePredicate(_)
2792 | Node::TestBinderForall(_)
2793 | Node::TestBinderExists(_) => {
2794 debug_assert!(false, "found {parent:?} which is after the final use node");
2797 return None;
2798 },
2799 }
2800
2801 ctxt = parent_ctxt;
2802 Some(ExprUseSite {
2803 node: parent,
2804 child_id,
2805 adjustments: mem::replace(&mut adjustments, parent_adjustments),
2806 is_ty_unified: mem::replace(&mut is_ty_unified, false),
2807 moved_before_use: mem::replace(&mut moved_before_use, false),
2808 same_ctxt: mem::replace(&mut same_ctxt, true),
2809 })
2810 },
2811 )
2812}
2813
2814pub fn get_expr_use_site<'tcx>(
2815 tcx: TyCtxt<'tcx>,
2816 typeck: &'tcx TypeckResults<'tcx>,
2817 ctxt: SyntaxContext,
2818 e: &'tcx Expr<'tcx>,
2819) -> ExprUseSite<'tcx> {
2820 expr_use_sites(tcx, typeck, ctxt, e).next().unwrap_or_else(|| {
2823 debug_assert!(false, "failed to find a use site for expr {e:?}");
2824 ExprUseSite {
2825 node: Node::Synthetic, child_id: CRATE_HIR_ID,
2827 adjustments: &[],
2828 is_ty_unified: false,
2829 moved_before_use: false,
2830 same_ctxt: false,
2831 }
2832 })
2833}
2834
2835pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2837 let mut pos = 0;
2838 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2839 let end = pos + t.len;
2840 let range = pos as usize..end as usize;
2841 let inner = InnerSpan::new(range.start, range.end);
2842 pos = end;
2843 (t.kind, s.get(range).unwrap_or_default(), inner)
2844 })
2845}
2846
2847pub fn span_contains_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool {
2850 span.check_text(sm, |snippet| {
2851 tokenize(snippet, FrontmatterAllowed::No).any(|token| {
2852 matches!(
2853 token.kind,
2854 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2855 )
2856 })
2857 })
2858}
2859
2860pub fn span_contains_non_whitespace<'sm>(sm: impl HasSourceMap<'sm>, span: Span, skip_comments: bool) -> bool {
2865 span.check_text(sm, |snippet| {
2866 tokenize_with_text(snippet).any(|(token, _, _)| match token {
2867 TokenKind::Whitespace => false,
2868 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2869 _ => true,
2870 })
2871 })
2872}
2873
2874pub fn span_extract_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> String {
2878 span_extract_comments(sm, span).join("\n")
2879}
2880
2881pub fn span_extract_comments<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Vec<String> {
2885 span.with_source_text(sm, |snippet| {
2886 tokenize_with_text(snippet)
2887 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2888 .map(|(_, s, _)| s.to_string())
2889 .collect::<Vec<_>>()
2890 })
2891 .unwrap_or_default()
2892}
2893
2894pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2895 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2896}
2897
2898pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2923 cx: &LateContext<'_>,
2924 pat: &'a Pat<'hir>,
2925 else_body: &Expr<'_>,
2926) -> Option<&'a Pat<'hir>> {
2927 if let Some([inner_pat]) = as_some_pattern(cx, pat)
2928 && !is_refutable(cx, inner_pat)
2929 && let else_body = peel_blocks(else_body)
2930 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2931 && let ExprKind::Path(ret_path) = ret_val.kind
2932 && cx
2933 .qpath_res(&ret_path, ret_val.hir_id)
2934 .ctor_parent(cx)
2935 .is_lang_item(cx, OptionNone)
2936 {
2937 Some(inner_pat)
2938 } else {
2939 None
2940 }
2941}
2942
2943macro_rules! op_utils {
2944 ($($name:ident $assign:ident)*) => {
2945 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2947
2948 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2950
2951 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2953 match kind {
2954 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2955 _ => None,
2956 }
2957 }
2958 };
2959}
2960
2961op_utils! {
2962 Add AddAssign
2963 Sub SubAssign
2964 Mul MulAssign
2965 Div DivAssign
2966 Rem RemAssign
2967 BitXor BitXorAssign
2968 BitAnd BitAndAssign
2969 BitOr BitOrAssign
2970 Shl ShlAssign
2971 Shr ShrAssign
2972}
2973
2974pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2977 match *pat {
2978 PatKind::Wild => true,
2979 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2980 !visitors::is_local_used(cx, body, id)
2981 },
2982 _ => false,
2983 }
2984}
2985
2986#[derive(Clone, Copy)]
2987pub enum RequiresSemi {
2988 Yes,
2989 No,
2990}
2991impl RequiresSemi {
2992 pub fn requires_semi(self) -> bool {
2993 matches!(self, Self::Yes)
2994 }
2995}
2996
2997#[expect(clippy::too_many_lines)]
3000pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
3001 struct BreakTarget {
3002 id: HirId,
3003 unused: bool,
3004 }
3005
3006 struct V<'cx, 'tcx> {
3007 cx: &'cx LateContext<'tcx>,
3008 break_targets: Vec<BreakTarget>,
3009 break_targets_for_result_ty: u32,
3010 in_final_expr: bool,
3011 requires_semi: bool,
3012 is_never: bool,
3013 }
3014
3015 impl V<'_, '_> {
3016 fn push_break_target(&mut self, id: HirId) {
3017 self.break_targets.push(BreakTarget { id, unused: true });
3018 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3019 }
3020 }
3021
3022 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3023 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3024 if self.is_never && self.break_targets.is_empty() {
3041 if self.in_final_expr && !self.requires_semi {
3042 match e.kind {
3045 ExprKind::DropTemps(e) => self.visit_expr(e),
3046 ExprKind::If(_, then, Some(else_)) => {
3047 self.visit_expr(then);
3048 self.visit_expr(else_);
3049 },
3050 ExprKind::Match(_, arms, _) => {
3051 for arm in arms {
3052 self.visit_expr(arm.body);
3053 }
3054 },
3055 ExprKind::Loop(b, ..) => {
3056 self.push_break_target(e.hir_id);
3057 self.in_final_expr = false;
3058 self.visit_block(b);
3059 self.break_targets.pop();
3060 },
3061 ExprKind::Block(b, _) => {
3062 if b.targeted_by_break {
3063 self.push_break_target(b.hir_id);
3064 self.visit_block(b);
3065 self.break_targets.pop();
3066 } else {
3067 self.visit_block(b);
3068 }
3069 },
3070 _ => {
3071 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3072 },
3073 }
3074 }
3075 return;
3076 }
3077 match e.kind {
3078 ExprKind::DropTemps(e) => self.visit_expr(e),
3079 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3080 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3081 self.in_final_expr = false;
3082 self.visit_expr(e);
3083 self.is_never = true;
3084 },
3085 ExprKind::Break(dest, e) => {
3086 if let Some(e) = e {
3087 self.in_final_expr = false;
3088 self.visit_expr(e);
3089 }
3090 if let Ok(id) = dest.target_id
3091 && let Some((i, target)) = self
3092 .break_targets
3093 .iter_mut()
3094 .enumerate()
3095 .find(|(_, target)| target.id == id)
3096 {
3097 target.unused &= self.is_never;
3098 if i < self.break_targets_for_result_ty as usize {
3099 self.requires_semi = true;
3100 }
3101 }
3102 self.is_never = true;
3103 },
3104 ExprKind::If(cond, then, else_) => {
3105 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3106 self.visit_expr(cond);
3107 self.in_final_expr = in_final_expr;
3108
3109 if self.is_never {
3110 self.visit_expr(then);
3111 if let Some(else_) = else_ {
3112 self.visit_expr(else_);
3113 }
3114 } else {
3115 self.visit_expr(then);
3116 let is_never = mem::replace(&mut self.is_never, false);
3117 if let Some(else_) = else_ {
3118 self.visit_expr(else_);
3119 self.is_never &= is_never;
3120 }
3121 }
3122 },
3123 ExprKind::Match(scrutinee, arms, _) => {
3124 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3125 self.visit_expr(scrutinee);
3126 self.in_final_expr = in_final_expr;
3127
3128 if self.is_never {
3129 for arm in arms {
3130 self.visit_arm(arm);
3131 }
3132 } else {
3133 let mut is_never = true;
3134 for arm in arms {
3135 self.is_never = false;
3136 if let Some(guard) = arm.guard {
3137 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3138 self.visit_expr(guard);
3139 self.in_final_expr = in_final_expr;
3140 self.is_never = false;
3142 }
3143 self.visit_expr(arm.body);
3144 is_never &= self.is_never;
3145 }
3146 self.is_never = is_never;
3147 }
3148 },
3149 ExprKind::Loop(b, _, _, _) => {
3150 self.push_break_target(e.hir_id);
3151 self.in_final_expr = false;
3152 self.visit_block(b);
3153 self.is_never = self.break_targets.pop().unwrap().unused;
3154 },
3155 ExprKind::Block(b, _) => {
3156 if b.targeted_by_break {
3157 self.push_break_target(b.hir_id);
3158 self.visit_block(b);
3159 self.is_never &= self.break_targets.pop().unwrap().unused;
3160 } else {
3161 self.visit_block(b);
3162 }
3163 },
3164 _ => {
3165 self.in_final_expr = false;
3166 walk_expr(self, e);
3167 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3168 },
3169 }
3170 }
3171
3172 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3173 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3174 for s in b.stmts {
3175 self.visit_stmt(s);
3176 }
3177 self.in_final_expr = in_final_expr;
3178 if let Some(e) = b.expr {
3179 self.visit_expr(e);
3180 }
3181 }
3182
3183 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3184 if let Some(e) = l.init {
3185 self.visit_expr(e);
3186 }
3187 if let Some(else_) = l.els {
3188 let is_never = self.is_never;
3189 self.visit_block(else_);
3190 self.is_never = is_never;
3191 }
3192 }
3193
3194 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3195 if let Some(guard) = arm.guard {
3196 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3197 self.visit_expr(guard);
3198 self.in_final_expr = in_final_expr;
3199 }
3200 self.visit_expr(arm.body);
3201 }
3202 }
3203
3204 if cx.typeck_results().expr_ty(e).is_never() {
3205 Some(RequiresSemi::No)
3206 } else if let ExprKind::Block(b, _) = e.kind
3207 && !b.targeted_by_break
3208 && b.expr.is_none()
3209 {
3210 None
3212 } else {
3213 let mut v = V {
3214 cx,
3215 break_targets: Vec::new(),
3216 break_targets_for_result_ty: 0,
3217 in_final_expr: true,
3218 requires_semi: false,
3219 is_never: false,
3220 };
3221 v.visit_expr(e);
3222 v.is_never
3223 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3224 RequiresSemi::Yes
3225 } else {
3226 RequiresSemi::No
3227 })
3228 }
3229}
3230
3231pub fn get_path_from_caller_to_method_type<'tcx>(
3237 tcx: TyCtxt<'tcx>,
3238 from: LocalDefId,
3239 method: DefId,
3240 args: GenericArgsRef<'tcx>,
3241) -> String {
3242 let assoc_item = tcx.associated_item(method);
3243 let def_id = assoc_item.container_id(tcx);
3244 match assoc_item.container {
3245 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3246 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3247 let ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
3248 get_path_to_ty(tcx, from, ty, args)
3249 },
3250 }
3251}
3252
3253fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3254 match ty.kind() {
3255 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3256 rustc_ty::Array(..)
3258 | rustc_ty::Dynamic(..)
3259 | rustc_ty::Never
3260 | rustc_ty::RawPtr(_, _)
3261 | rustc_ty::Ref(..)
3262 | rustc_ty::Slice(_)
3263 | rustc_ty::Tuple(_) => format!(
3264 "<{}>",
3265 EarlyBinder::bind(tcx, ty).instantiate(tcx, args).skip_norm_wip()
3266 ),
3267 _ => ty.to_string(),
3268 }
3269}
3270
3271fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3273 if callee.is_local() {
3275 let callee_path = tcx.def_path(callee);
3276 let caller_path = tcx.def_path(from.to_def_id());
3277 maybe_get_relative_path(&caller_path, &callee_path, 2)
3278 } else {
3279 tcx.def_path_str(callee)
3280 }
3281}
3282
3283fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3296 use itertools::EitherOrBoth::{Both, Left, Right};
3297
3298 let unique_parts = to
3300 .data
3301 .iter()
3302 .zip_longest(from.data.iter())
3303 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3304 .map(|el| match el {
3305 Both(l, r) => Both(l.data, r.data),
3306 Left(l) => Left(l.data),
3307 Right(r) => Right(r.data),
3308 });
3309
3310 let mut go_up_by = 0;
3312 let mut path = Vec::new();
3313 for el in unique_parts {
3314 match el {
3315 Both(l, r) => {
3316 if let DefPathData::TypeNs(sym) = l {
3326 path.push(sym);
3327 }
3328 if let DefPathData::TypeNs(_) = r {
3329 go_up_by += 1;
3330 }
3331 },
3332 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3337 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3342 _ => {},
3343 }
3344 }
3345
3346 if go_up_by > max_super {
3347 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3349 if let DefPathData::TypeNs(sym) = el.data {
3350 Some(sym)
3351 } else {
3352 None
3353 }
3354 })))
3355 } else if go_up_by == 0 && path.is_empty() {
3356 String::from("Self")
3357 } else {
3358 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3359 }
3360}
3361
3362pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3365 matches!(
3366 cx.tcx.parent_hir_node(id),
3367 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3368 )
3369}
3370
3371pub fn is_block_like(expr: &Expr<'_>) -> bool {
3374 matches!(
3375 expr.kind,
3376 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3377 )
3378}
3379
3380pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3382 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3383 match expr.kind {
3384 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3385 _ if is_block_like(expr) => is_operand,
3386 _ => false,
3387 }
3388 }
3389
3390 contains_block(expr, false)
3391}
3392
3393pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3395 if let Some(parent_expr) = get_parent_expr(cx, expr)
3396 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3397 && receiver.hir_id == expr.hir_id
3398 {
3399 return true;
3400 }
3401 false
3402}
3403
3404pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3407 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3408 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3409 && temporary_ty
3410 .walk()
3411 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3412 {
3413 ControlFlow::Break(())
3414 } else {
3415 ControlFlow::Continue(())
3416 }
3417 })
3418 .is_break()
3419}
3420
3421pub fn leaks_droppable_temporary<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3424 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3425 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) {
3426 ControlFlow::Break(())
3427 } else {
3428 ControlFlow::Continue(())
3429 }
3430 })
3431 .is_break()
3432}
3433
3434pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3445 let expr_ty_is_adjusted = cx
3446 .typeck_results()
3447 .expr_adjustments(expr)
3448 .iter()
3449 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3451 if expr_ty_is_adjusted {
3452 return true;
3453 }
3454
3455 match expr.kind {
3458 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3459 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip();
3460
3461 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3462 return false;
3463 }
3464
3465 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3466 let mut args_with_ty_param = {
3467 fn_sig
3468 .inputs()
3469 .skip_binder()
3470 .iter()
3471 .skip(self_arg_count)
3472 .zip(args)
3473 .filter_map(|(arg_ty, arg)| {
3474 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3475 Some(arg)
3476 } else {
3477 None
3478 }
3479 })
3480 };
3481 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3482 },
3483 ExprKind::Struct(qpath, _, _) => {
3485 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3486 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3487 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3488 return true;
3490 };
3491 v_def
3492 .fields
3493 .iter()
3494 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3495 } else {
3496 false
3497 }
3498 },
3499 ExprKind::Block(
3501 &Block {
3502 expr: Some(ret_expr), ..
3503 },
3504 _,
3505 )
3506 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3507
3508 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3510 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3512 ExprKind::If(_, then, maybe_else) => {
3514 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3515 },
3516 ExprKind::Match(_, arms, _) => arms
3517 .iter()
3518 .map(|arm| arm.body)
3519 .any(|body| expr_requires_coercion(cx, body)),
3520 _ => false,
3521 }
3522}
3523
3524pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3527 if let Some(hir_id) = expr.res_local_id()
3528 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3529 {
3530 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3531 } else if let ExprKind::Path(p) = &expr.kind
3532 && let Some(mutability) = cx
3533 .qpath_res(p, expr.hir_id)
3534 .opt_def_id()
3535 .and_then(|id| cx.tcx.static_mutability(id))
3536 {
3537 mutability == Mutability::Mut
3538 } else if let ExprKind::Field(parent, _) = expr.kind {
3539 is_mutable(cx, parent)
3540 } else {
3541 true
3542 }
3543}
3544
3545pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3548 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3549 return hir_ty;
3550 };
3551 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3552 && let Some(segment) = path.segments.last()
3553 && segment.ident.name == sym::Option
3554 && let Res::Def(DefKind::Enum, def_id) = segment.res
3555 && def_id == option_def_id
3556 && let [GenericArg::Type(arg_ty)] = segment.args().args
3557 {
3558 hir_ty = arg_ty.as_unambig_ty();
3559 }
3560 hir_ty
3561}
3562
3563pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3566 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3567 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3568 && let ctxt = expr.span.ctxt()
3569 && for_each_expr_without_closures(into_future_arg, |e| {
3570 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3571 })
3572 .is_none()
3573 {
3574 Some(into_future_arg)
3575 } else {
3576 None
3577 }
3578}
3579
3580pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3582 if let ExprKind::Call(fn_expr, []) = &expr.kind
3583 && let ExprKind::Path(qpath) = &fn_expr.kind
3584 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3585 {
3586 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3587 } else {
3588 false
3589 }
3590}
3591
3592pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3609 let enclosing_body_owner = cx
3610 .tcx
3611 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3612 let mut prev_id = expr.hir_id;
3613 let mut skip_until_id = None;
3614 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3615 if hir_id == enclosing_body_owner {
3616 return true;
3617 }
3618 if let Some(id) = skip_until_id {
3619 prev_id = hir_id;
3620 if id == hir_id {
3621 skip_until_id = None;
3622 }
3623 continue;
3624 }
3625 match node {
3626 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3627 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3628 Node::Expr(expr) => match expr.kind {
3629 ExprKind::Ret(_) => return true,
3630 ExprKind::If(_, then, opt_else)
3631 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3632 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3633 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3634 ExprKind::Break(
3635 Destination {
3636 target_id: Ok(target_id),
3637 ..
3638 },
3639 _,
3640 ) => skip_until_id = Some(target_id),
3641 _ => break,
3642 },
3643 _ => break,
3644 }
3645 prev_id = hir_id;
3646 }
3647
3648 false
3651}
3652
3653pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3656 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3657 matches!(
3658 adj.kind,
3659 Adjust::Deref(DerefAdjustKind::Overloaded(_))
3660 | Adjust::Pointer(PointerCoercion::Unsize)
3661 | Adjust::NeverToAny
3662 )
3663 })
3664}
3665
3666pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3668 matches!(
3669 expr.kind,
3670 ExprKind::Closure(Closure {
3671 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3672 CoroutineDesugaring::Async,
3673 CoroutineSource::Block
3674 )),
3675 ..
3676 })
3677 )
3678}
3679
3680pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3682 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3683}
3684
3685#[inline]
3688pub fn hir_parent_with_src_iter(tcx: TyCtxt<'_>, mut id: HirId) -> impl Iterator<Item = (Node<'_>, HirId)> {
3689 tcx.hir_parent_id_iter(id)
3690 .map(move |parent| (tcx.hir_node(parent), mem::replace(&mut id, parent)))
3691}