1#![cfg_attr(bootstrap, feature(unwrap_infallible))]
2#![feature(deref_patterns)]
3#![feature(macro_metavar_expr)]
4#![feature(rustc_private)]
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_ir;
21extern crate rustc_attr_parsing;
22extern crate rustc_const_eval;
23extern crate rustc_data_structures;
24#[expect(
25 unused_extern_crates,
26 reason = "The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate."
27)]
28extern crate rustc_driver;
29extern crate rustc_errors;
30extern crate rustc_hir;
31extern crate rustc_hir_analysis;
32extern crate rustc_hir_typeck;
33extern crate rustc_index;
34extern crate rustc_infer;
35extern crate rustc_lexer;
36extern crate rustc_lint;
37extern crate rustc_middle;
38extern crate rustc_mir_dataflow;
39extern crate rustc_session;
40extern crate rustc_span;
41extern crate rustc_trait_selection;
42
43pub mod ast_utils;
44#[deny(missing_docs)]
45pub mod attrs;
46mod check_proc_macro;
47pub mod comparisons;
48pub mod consts;
49pub mod diagnostics;
50pub mod eager_or_lazy;
51pub mod higher;
52mod hir_utils;
53pub mod macros;
54pub mod mir;
55pub mod msrvs;
56pub mod numeric_literal;
57pub mod paths;
58pub mod qualify_min_const_fn;
59pub mod res;
60pub mod source;
61pub mod str_utils;
62pub mod sugg;
63pub mod sym;
64pub mod ty;
65pub mod usage;
66pub mod visitors;
67
68pub use self::attrs::*;
69pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
70pub use self::hir_utils::{
71 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
72 hash_stmt, is_bool, over,
73};
74
75use core::mem;
76use core::ops::ControlFlow;
77use std::collections::hash_map::Entry;
78use std::iter::{once, repeat_n, zip};
79use std::sync::{Mutex, OnceLock};
80
81use itertools::Itertools as _;
82use rustc_abi::Integer;
83use rustc_ast::ast::{self, LitKind, RangeLimits};
84use rustc_ast::{LitIntType, join_path_syms};
85use rustc_attr_ir::lang_items::LangItem;
86use rustc_attr_ir::lang_items::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
87use rustc_attr_ir::{CfgEntry, find_attr};
88use rustc_data_structures::fx::FxHashMap;
89use rustc_data_structures::indexmap;
90use rustc_data_structures::packed::Pu128;
91use rustc_data_structures::unhash::UnindexMap;
92use rustc_hir::def::{DefKind, Res};
93use rustc_hir::def_id::{DefId, LocalDefId, LocalModId};
94use rustc_hir::definitions::{DefPath, DefPathData};
95use rustc_hir::intravisit::{Visitor, walk_expr};
96use rustc_hir::{
97 self as hir, AnonConst, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, CRATE_HIR_ID, Closure, ConstArg,
98 ConstArgKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind,
99 FieldDef, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, HirIdSet, Impl, ImplItem, ImplItemKind, Item,
100 ItemKind, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind,
101 Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, Variant, def,
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_session::config::Input;
115use rustc_span::hygiene::{ExpnKind, MacroKind};
116use rustc_span::source_map::SourceMap;
117use rustc_span::symbol::{Ident, Symbol, kw};
118use rustc_span::{InnerSpan, Span, SyntaxContext};
119use source::{SpanExt as _, walk_span_to_context};
120use visitors::{Visitable, for_each_unconsumed_temporary};
121
122use crate::ast_utils::unordered_over;
123use crate::higher::Range;
124use crate::msrvs::Msrv;
125use crate::res::{MaybeDef as _, MaybeResPath as _};
126use crate::source::HasSourceMap;
127use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
128use crate::visitors::for_each_expr_without_closures;
129
130pub const VEC_METHODS_SHADOWING_SLICE_METHODS: [Symbol; 3] = [sym::as_ptr, sym::is_empty, sym::len];
132
133#[macro_export]
134macro_rules! extract_msrv_attr {
135 () => {
136 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
137 let sess = rustc_lint::LintContext::sess(cx);
138 self.msrv.check_attributes(attrs);
139 }
140
141 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
142 let sess = rustc_lint::LintContext::sess(cx);
143 self.msrv.check_attributes_post(attrs);
144 }
145 };
146}
147
148pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
171 while let Some(init) = expr
172 .res_local_id()
173 .and_then(|id| find_binding_init(cx, id))
174 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
175 {
176 expr = init;
177 }
178 expr
179}
180
181pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
190 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
191 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
192 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
193 {
194 return local.init;
195 }
196 None
197}
198
199pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
203 for (_, node) in cx.tcx.hir_parent_iter(local) {
204 match node {
205 Node::Pat(..) | Node::PatField(..) => {},
206 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
207 _ => return true,
208 }
209 }
210
211 false
212}
213
214pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
225 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
226 cx.enclosing_body.is_some_and(|id| {
227 cx.tcx
228 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
229 .is_some()
230 })
231}
232
233pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
240 use rustc_hir::ConstContext::{Const, ConstFn, Static};
241 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
242 return false;
243 };
244 match ctx {
245 ConstFn => false,
246 Static(_)
247 | Const {
248 allow_const_fn_promotion: _,
249 } => true,
250 }
251}
252
253pub fn is_enum_variant_ctor(
255 cx: &LateContext<'_>,
256 enum_item: Symbol,
257 variant_name: Symbol,
258 ctor_call_id: DefId,
259) -> bool {
260 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
261 return false;
262 };
263
264 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
265 variants
266 .filter(|variant| variant.name == variant_name)
267 .filter_map(|variant| variant.ctor.as_ref())
268 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
269}
270
271pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
273 let did = match cx.tcx.def_kind(did) {
274 DefKind::Ctor(..) => cx.tcx.parent(did),
275 DefKind::Variant => match cx.tcx.opt_parent(did) {
277 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
278 _ => did,
279 },
280 _ => did,
281 };
282
283 cx.tcx.is_diagnostic_item(item, did)
284}
285
286pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
288 let did = match cx.tcx.def_kind(did) {
289 DefKind::Ctor(..) => cx.tcx.parent(did),
290 DefKind::Variant => match cx.tcx.opt_parent(did) {
292 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
293 _ => did,
294 },
295 _ => did,
296 };
297
298 cx.tcx.lang_items().get(item) == Some(did)
299}
300
301pub fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
303 expr.basic_res().ctor_parent(cx).is_lang_item(cx, OptionNone)
304}
305
306pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
308 if let ExprKind::Call(e, [arg]) = expr.kind
309 && e.basic_res().ctor_parent(cx).is_lang_item(cx, OptionSome)
310 {
311 Some(arg)
312 } else {
313 None
314 }
315}
316
317pub fn is_empty_block(expr: &Expr<'_>) -> bool {
319 matches!(
320 expr.kind,
321 ExprKind::Block(
322 Block {
323 stmts: [],
324 expr: None,
325 ..
326 },
327 _,
328 )
329 )
330}
331
332pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
334 matches!(
335 expr.kind,
336 ExprKind::Block(
337 Block {
338 stmts: [],
339 expr: None,
340 ..
341 },
342 _
343 ) | ExprKind::Tup([])
344 )
345}
346
347pub fn is_wild(pat: &Pat<'_>) -> bool {
349 matches!(pat.kind, PatKind::Wild)
350}
351
352pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> {
359 if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind
360 && cx
361 .qpath_res(qpath, pat.hir_id)
362 .ctor_parent(cx)
363 .is_lang_item(cx, OptionSome)
364 {
365 Some(inner)
366 } else {
367 None
368 }
369}
370
371pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
373 matches!(pat.kind,
374 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
375 if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone))
376}
377
378pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
380 is_none_pattern(cx, arm.pat)
381 && matches!(
382 peel_blocks(arm.body).kind,
383 ExprKind::Path(qpath)
384 if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)
385 )
386}
387
388pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
390 match *qpath {
391 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
392 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
393 QPath::TypeRelative(..) => false,
394 }
395}
396
397pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
399 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
400 && let ItemKind::Impl(imp) = item.kind
401 {
402 imp.of_trait.is_some()
403 } else {
404 false
405 }
406}
407
408pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
409 match *path {
410 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
411 QPath::TypeRelative(_, seg) => seg,
412 }
413}
414
415pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
416 last_path_segment(qpath)
417 .args
418 .map_or(&[][..], |a| a.args)
419 .iter()
420 .filter_map(|a| match a {
421 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
422 _ => None,
423 })
424}
425
426pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
431 match expr.kind {
432 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
433 ExprKind::Path(QPath::Resolved(
434 _,
435 Path {
436 res: Res::Local(local), ..
437 },
438 )) => Some(*local),
439 _ => None,
440 }
441}
442
443pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
459 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
460 && let ItemKind::Impl(impl_) = &item.kind
461 && let Some(of_trait) = impl_.of_trait
462 {
463 return Some(&of_trait.trait_ref);
464 }
465 None
466}
467
468fn projection_stack<'a, 'hir>(
476 mut e: &'a Expr<'hir>,
477 ctxt: SyntaxContext,
478) -> Option<(Vec<&'a Expr<'hir>>, &'a Expr<'hir>)> {
479 let mut result = vec![];
480 let root = loop {
481 match e.kind {
482 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) if e.span.ctxt() == ctxt => {
483 result.push(e);
484 e = ep;
485 },
486 ExprKind::Index(..) | ExprKind::Field(..) => return None,
487 _ => break e,
488 }
489 };
490 result.reverse();
491 Some((result, root))
492}
493
494pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
496 cx.typeck_results()
497 .expr_adjustments(e)
498 .iter()
499 .find_map(|a| match a.kind {
500 Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)),
501 Adjust::Deref(DerefAdjustKind::Builtin) => None,
502 _ => Some(None),
503 })
504 .and_then(|x| x)
505}
506
507pub fn can_mut_borrow_both(cx: &LateContext<'_>, ctxt: SyntaxContext, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
510 let Some((s1, r1)) = projection_stack(e1, ctxt) else {
511 return false;
512 };
513 let Some((s2, r2)) = projection_stack(e2, ctxt) else {
514 return false;
515 };
516 if !eq_expr_value(cx, ctxt, r1, r2) {
517 return true;
518 }
519 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
520 return false;
521 }
522
523 for (x1, x2) in zip(&s1, &s2) {
524 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
525 return false;
526 }
527
528 match (&x1.kind, &x2.kind) {
529 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
530 if i1 != i2 {
531 return true;
532 }
533 },
534 _ => return false,
535 }
536 }
537 false
538}
539
540fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
543 let std_types_symbols = &[
544 sym::Vec,
545 sym::VecDeque,
546 sym::LinkedList,
547 sym::HashMap,
548 sym::BTreeMap,
549 sym::HashSet,
550 sym::BTreeSet,
551 sym::BinaryHeap,
552 ];
553
554 if let QPath::TypeRelative(_, method) = path
555 && method.ident.name == sym::new
556 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
557 && let Some(adt) = cx
558 .tcx
559 .type_of(impl_did)
560 .instantiate_identity()
561 .skip_norm_wip()
562 .ty_adt_def()
563 {
564 return Some(adt.did()) == cx.tcx.lang_items().string()
565 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
566 }
567 false
568}
569
570pub fn is_default_equivalent_call(
572 cx: &LateContext<'_>,
573 repl_func: &Expr<'_>,
574 whole_call_expr: Option<&Expr<'_>>,
575) -> bool {
576 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
577 && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx)
578 && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default)
579 || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath))
580 {
581 return true;
582 }
583
584 let Some(e) = whole_call_expr else { return false };
587 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
588 return false;
589 };
590 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
591 return false;
592 };
593 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
594 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
595 cx.tcx.lifetimes.re_erased.into()
596 } else if param.index == 0 && param.name == kw::SelfUpper {
597 ty.into()
598 } else {
599 param.to_error(cx.tcx)
600 }
601 });
602 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
603
604 let Ok(Some(instance)) = instance else { return false };
605 if let rustc_ty::InstanceKind::Item(def) = instance.def
606 && !cx.tcx.is_mir_available(def)
607 {
608 return false;
609 }
610 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
611 return false;
612 };
613 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
614 return false;
615 };
616
617 let body = cx.tcx.instance_mir(instance.def);
623 for block_data in body.basic_blocks.iter() {
624 if block_data.statements.len() == 1
625 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
626 && assign.0.local == RETURN_PLACE
627 && let Rvalue::Aggregate(kind, _places) = &assign.1
628 && let AggregateKind::Adt(did, variant_index, _, _, _) = **kind
629 && let def = cx.tcx.adt_def(did)
630 && let variant = &def.variant(variant_index)
631 && variant.fields.is_empty()
632 && let Some((_, did)) = variant.ctor
633 && did == repl_def_id
634 {
635 return true;
636 } else if block_data.statements.is_empty()
637 && let Some(term) = &block_data.terminator
638 {
639 match &term.kind {
640 TerminatorKind::Call {
641 func: Operand::Constant(c),
642 ..
643 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
644 && *did == repl_def_id =>
645 {
646 return true;
647 },
648 TerminatorKind::TailCall {
649 func: Operand::Constant(c),
650 ..
651 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
652 && *did == repl_def_id =>
653 {
654 return true;
655 },
656 _ => {},
657 }
658 }
659 }
660 false
661}
662
663pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
667 match &e.kind {
668 ExprKind::Lit(lit) => match lit.node {
669 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
670 LitKind::Str(s, _) => s.is_empty(),
671 _ => false,
672 },
673 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
674 ExprKind::Repeat(x, len) => {
675 if let ConstArgKind::Anon(anon_const) = len.kind
676 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
677 && let LitKind::Int(v, _) = const_lit.node
678 && v <= 32
679 && is_default_equivalent(cx, x)
680 {
681 true
682 } else {
683 false
684 }
685 },
686 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
687 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
688 ExprKind::Path(qpath) => cx
689 .qpath_res(qpath, e.hir_id)
690 .ctor_parent(cx)
691 .is_lang_item(cx, OptionNone),
692 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
693 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
694 _ => false,
695 }
696}
697
698fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
699 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
700 && seg.ident.name == sym::from
701 {
702 match arg.kind {
703 ExprKind::Lit(hir::Lit {
704 node: LitKind::Str(sym, _),
705 ..
706 }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String),
707 ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec),
708 ExprKind::Repeat(_, len) => {
709 if let ConstArgKind::Anon(anon_const) = len.kind
710 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
711 && let LitKind::Int(v, _) = const_lit.node
712 {
713 return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec);
714 }
715 },
716 _ => (),
717 }
718 }
719 false
720}
721
722pub fn can_move_expr_to_closure_no_visit<'tcx>(
754 cx: &LateContext<'tcx>,
755 expr: &'tcx Expr<'_>,
756 loop_ids: &[HirId],
757 ignore_locals: &HirIdSet,
758) -> bool {
759 match expr.kind {
760 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
761 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
762 if loop_ids.contains(&id) =>
763 {
764 true
765 },
766 ExprKind::Break(..)
767 | ExprKind::Continue(_)
768 | ExprKind::Ret(_)
769 | ExprKind::Yield(..)
770 | ExprKind::InlineAsm(_) => false,
771 ExprKind::Field(
774 &Expr {
775 hir_id,
776 kind:
777 ExprKind::Path(QPath::Resolved(
778 _,
779 Path {
780 res: Res::Local(local_id),
781 ..
782 },
783 )),
784 ..
785 },
786 _,
787 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
788 false
790 },
791 _ => true,
792 }
793}
794
795#[derive(Debug, Clone, Copy, PartialEq, Eq)]
797pub enum CaptureKind {
798 Value,
799 Use,
800 Ref(Mutability),
801}
802impl CaptureKind {
803 pub fn is_imm_ref(self) -> bool {
804 self == Self::Ref(Mutability::Not)
805 }
806}
807impl std::ops::BitOr for CaptureKind {
808 type Output = Self;
809 fn bitor(self, rhs: Self) -> Self::Output {
810 match (self, rhs) {
811 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
812 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
813 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
814 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
815 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
816 }
817 }
818}
819impl std::ops::BitOrAssign for CaptureKind {
820 fn bitor_assign(&mut self, rhs: Self) {
821 *self = *self | rhs;
822 }
823}
824
825pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
831 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
832 let mut capture = CaptureKind::Ref(Mutability::Not);
833 pat.each_binding_or_first(&mut |_, id, span, _| match cx
834 .typeck_results()
835 .extract_binding_mode(cx.sess(), id, span)
836 .0
837 {
838 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
839 capture = CaptureKind::Value;
840 },
841 ByRef::Yes(_, Mutability::Mut) if capture != CaptureKind::Value => {
842 capture = CaptureKind::Ref(Mutability::Mut);
843 },
844 _ => (),
845 });
846 capture
847 }
848
849 debug_assert!(matches!(
850 e.kind,
851 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
852 ));
853
854 let mut capture = CaptureKind::Value;
855 let mut capture_expr_ty = e;
856
857 for (parent, child_id) in hir_parent_with_src_iter(cx.tcx, e.hir_id) {
858 if let [
859 Adjustment {
860 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
861 target,
862 },
863 ref adjust @ ..,
864 ] = *cx
865 .typeck_results()
866 .adjustments()
867 .get(child_id)
868 .map_or(&[][..], |x| &**x)
869 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
870 *adjust.last().map_or(target, |a| a.target).kind()
871 {
872 return CaptureKind::Ref(mutability);
873 }
874
875 match parent {
876 Node::Expr(e) => match e.kind {
877 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
878 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
879 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
880 return CaptureKind::Ref(Mutability::Mut);
881 },
882 ExprKind::Field(..) => {
883 if capture == CaptureKind::Value {
884 capture_expr_ty = e;
885 }
886 },
887 ExprKind::Let(let_expr) => {
888 let mutability = match pat_capture_kind(cx, let_expr.pat) {
889 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
890 CaptureKind::Ref(m) => m,
891 };
892 return CaptureKind::Ref(mutability);
893 },
894 ExprKind::Match(_, arms, _) => {
895 let mut mutability = Mutability::Not;
896 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
897 match capture {
898 CaptureKind::Value | CaptureKind::Use => break,
899 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
900 CaptureKind::Ref(Mutability::Not) => (),
901 }
902 }
903 return CaptureKind::Ref(mutability);
904 },
905 _ => break,
906 },
907 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
908 CaptureKind::Value | CaptureKind::Use => break,
909 capture @ CaptureKind::Ref(_) => return capture,
910 },
911 _ => break,
912 }
913 }
914
915 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
916 CaptureKind::Ref(Mutability::Not)
918 } else {
919 capture
920 }
921}
922
923pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
926 struct V<'cx, 'tcx> {
927 cx: &'cx LateContext<'tcx>,
928 loops: Vec<HirId>,
930 locals: HirIdSet,
932 allow_closure: bool,
934 captures: HirIdMap<CaptureKind>,
937 }
938 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
939 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
940 if !self.allow_closure {
941 return;
942 }
943
944 match e.kind {
945 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
946 if !self.locals.contains(&l) {
947 let cap = capture_local_usage(self.cx, e);
948 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
949 }
950 },
951 ExprKind::Closure(closure) => {
952 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
953 let local_id = match capture.place.base {
954 PlaceBase::Local(id) => id,
955 PlaceBase::Upvar(var) => var.var_path.hir_id,
956 _ => continue,
957 };
958 if !self.locals.contains(&local_id) {
959 let capture = match capture.info.capture_kind {
960 UpvarCapture::ByValue => CaptureKind::Value,
961 UpvarCapture::ByUse => CaptureKind::Use,
962 UpvarCapture::ByRef(kind) => match kind {
963 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
964 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
965 CaptureKind::Ref(Mutability::Mut)
966 },
967 },
968 };
969 self.captures
970 .entry(local_id)
971 .and_modify(|e| *e |= capture)
972 .or_insert(capture);
973 }
974 }
975 },
976 ExprKind::Loop(b, ..) => {
977 self.loops.push(e.hir_id);
978 self.visit_block(b);
979 self.loops.pop();
980 },
981 _ => {
982 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
983 walk_expr(self, e);
984 },
985 }
986 }
987
988 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
989 p.each_binding_or_first(&mut |_, id, _, _| {
990 self.locals.insert(id);
991 });
992 }
993 }
994
995 let mut v = V {
996 cx,
997 loops: Vec::new(),
998 locals: HirIdSet::default(),
999 allow_closure: true,
1000 captures: HirIdMap::default(),
1001 };
1002 v.visit_expr(expr);
1003 v.allow_closure.then_some(v.captures)
1004}
1005
1006pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1008
1009pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1012 let mut method_names = Vec::with_capacity(max_depth);
1013 let mut arg_lists = Vec::with_capacity(max_depth);
1014 let mut spans = Vec::with_capacity(max_depth);
1015
1016 let mut current = expr;
1017 for _ in 0..max_depth {
1018 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1019 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1020 break;
1021 }
1022 method_names.push(path.ident.name);
1023 arg_lists.push((*receiver, &**args));
1024 spans.push(path.ident.span);
1025 current = receiver;
1026 } else {
1027 break;
1028 }
1029 }
1030
1031 (method_names, arg_lists, spans)
1032}
1033
1034pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1041 let mut current = expr;
1042 let mut matched = Vec::with_capacity(methods.len());
1043 for method_name in methods.iter().rev() {
1044 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1046 if path.ident.name == *method_name {
1047 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1048 return None;
1049 }
1050 matched.push((receiver, args)); current = receiver; } else {
1053 return None;
1054 }
1055 } else {
1056 return None;
1057 }
1058 }
1059 matched.reverse();
1061 Some(matched)
1062}
1063
1064pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1066 cx.tcx
1067 .entry_fn(())
1068 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1069}
1070
1071pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1073 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1074 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1075}
1076
1077pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1079 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1080 match cx.tcx.hir_node_by_def_id(parent_id) {
1081 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1082 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1083 _ => None,
1084 }
1085}
1086
1087pub struct ContainsName<'a, 'tcx> {
1088 pub cx: &'a LateContext<'tcx>,
1089 pub name: Symbol,
1090}
1091
1092impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1093 type Result = ControlFlow<()>;
1094 type NestedFilter = nested_filter::OnlyBodies;
1095
1096 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1097 if self.name == name {
1098 ControlFlow::Break(())
1099 } else {
1100 ControlFlow::Continue(())
1101 }
1102 }
1103
1104 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1105 self.cx.tcx
1106 }
1107}
1108
1109pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1111 let mut cn = ContainsName { cx, name };
1112 cn.visit_expr(expr).is_break()
1113}
1114
1115pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1117 for_each_expr_without_closures(expr, |e| {
1118 if matches!(e.kind, ExprKind::Ret(..)) {
1119 ControlFlow::Break(())
1120 } else {
1121 ControlFlow::Continue(())
1122 }
1123 })
1124 .is_some()
1125}
1126
1127pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1129 get_parent_expr_for_hir(cx, e.hir_id)
1130}
1131
1132pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1135 match cx.tcx.parent_hir_node(hir_id) {
1136 Node::Expr(parent) => Some(parent),
1137 _ => None,
1138 }
1139}
1140
1141pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1143 let enclosing_node = cx
1144 .tcx
1145 .hir_get_enclosing_scope(hir_id)
1146 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1147 enclosing_node.and_then(|node| match node {
1148 Node::Block(block) => Some(block),
1149 Node::Item(&Item {
1150 kind: ItemKind::Fn { body: eid, .. },
1151 ..
1152 })
1153 | Node::ImplItem(&ImplItem {
1154 kind: ImplItemKind::Fn(_, eid),
1155 ..
1156 })
1157 | Node::TraitItem(&TraitItem {
1158 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1159 ..
1160 }) => match cx.tcx.hir_body(eid).value.kind {
1161 ExprKind::Block(block, _) => Some(block),
1162 _ => None,
1163 },
1164 _ => None,
1165 })
1166}
1167
1168pub fn get_enclosing_closure<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Closure<'tcx>> {
1170 cx.tcx.hir_parent_iter(hir_id).find_map(|(_, node)| {
1171 if let Node::Expr(expr) = node
1172 && let ExprKind::Closure(closure) = expr.kind
1173 {
1174 Some(closure)
1175 } else {
1176 None
1177 }
1178 })
1179}
1180
1181pub fn is_upvar_in_closure(cx: &LateContext<'_>, closure: &Closure<'_>, local_id: HirId) -> bool {
1183 cx.typeck_results()
1184 .closure_min_captures
1185 .get(&closure.def_id)
1186 .is_some_and(|x| x.contains_key(&local_id))
1187}
1188
1189pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1191 cx: &LateContext<'tcx>,
1192 expr: &Expr<'_>,
1193) -> Option<&'tcx Expr<'tcx>> {
1194 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1195 match node {
1196 Node::Expr(e) => match e.kind {
1197 ExprKind::Closure { .. }
1198 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1199 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1200
1201 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1203 _ => (),
1204 },
1205 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1206 _ => break,
1207 }
1208 }
1209 None
1210}
1211
1212pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1214 match tcx.hir_parent_iter(id).next() {
1215 Some((
1216 _,
1217 Node::Item(Item {
1218 kind: ItemKind::Impl(imp),
1219 ..
1220 }),
1221 )) => Some(imp),
1222 _ => None,
1223 }
1224}
1225
1226pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1237 while let ExprKind::Block(
1238 Block {
1239 stmts: [],
1240 expr: Some(inner),
1241 rules: BlockCheckMode::DefaultBlock,
1242 ..
1243 },
1244 _,
1245 ) = expr.kind
1246 {
1247 expr = inner;
1248 }
1249 expr
1250}
1251
1252pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1263 while let ExprKind::Block(
1264 Block {
1265 stmts: [],
1266 expr: Some(inner),
1267 rules: BlockCheckMode::DefaultBlock,
1268 ..
1269 }
1270 | Block {
1271 stmts:
1272 [
1273 Stmt {
1274 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1275 ..
1276 },
1277 ],
1278 expr: None,
1279 rules: BlockCheckMode::DefaultBlock,
1280 ..
1281 },
1282 _,
1283 ) = expr.kind
1284 {
1285 expr = inner;
1286 }
1287 expr
1288}
1289
1290pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1292 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1293 match iter.next() {
1294 Some((
1295 _,
1296 Node::Expr(Expr {
1297 kind: ExprKind::If(_, _, Some(else_expr)),
1298 ..
1299 }),
1300 )) => else_expr.hir_id == expr.hir_id,
1301 _ => false,
1302 }
1303}
1304
1305pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1308 hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {
1309 matches!(
1310 node,
1311 Node::LetStmt(LetStmt {
1312 init: Some(init),
1313 els: Some(els),
1314 ..
1315 })
1316 if init.hir_id == child_id || els.hir_id == child_id
1317 )
1318 })
1319}
1320
1321pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1323 hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {
1324 matches!(
1325 node,
1326 Node::LetStmt(LetStmt { els: Some(els), .. })
1327 if els.hir_id == child_id
1328 )
1329 })
1330}
1331
1332pub fn is_full_collection_range(cx: &LateContext<'_>, container: Option<HirId>, expr: &Expr<'_>) -> bool {
1334 if let Some(Range { start, end, ty, .. }) = Range::hir(cx, expr) {
1335 start.is_none_or(|start| is_integer_literal(start, 0))
1336 && end.is_none_or(|end| {
1337 if ty.limits() == RangeLimits::HalfOpen
1338 && let Some(container) = container
1339 && let ExprKind::MethodCall(seg, recv, [], _) = end.kind
1340 {
1341 seg.ident.name == sym::len && recv.res_local_id() == Some(container)
1342 } else {
1343 false
1344 }
1345 })
1346 } else {
1347 false
1348 }
1349}
1350
1351pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1353 if let ExprKind::Lit(spanned) = expr.kind
1354 && let LitKind::Int(v, _) = spanned.node
1355 {
1356 return v == value;
1357 }
1358 false
1359}
1360
1361pub fn is_integer_literal_untyped(expr: &Expr<'_>) -> bool {
1363 if let ExprKind::Lit(spanned) = expr.kind
1364 && let LitKind::Int(_, suffix) = spanned.node
1365 {
1366 return suffix == LitIntType::Unsuffixed;
1367 }
1368
1369 false
1370}
1371
1372pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1374 if let ExprKind::Lit(spanned) = expr.kind
1375 && let LitKind::Float(v, _) = spanned.node
1376 {
1377 v.as_str().parse() == Ok(value)
1378 } else {
1379 false
1380 }
1381}
1382
1383pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1391 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1392}
1393
1394#[must_use]
1398pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1399 loop {
1400 if span.from_expansion() {
1401 let data = span.ctxt().outer_expn_data();
1402 let new_span = data.call_site;
1403
1404 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1405 && mac_name == name
1406 {
1407 return Some(new_span);
1408 }
1409
1410 span = new_span;
1411 } else {
1412 return None;
1413 }
1414 }
1415}
1416
1417#[must_use]
1428pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1429 if span.from_expansion() {
1430 let data = span.ctxt().outer_expn_data();
1431 let new_span = data.call_site;
1432
1433 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1434 && mac_name == name
1435 {
1436 return Some(new_span);
1437 }
1438 }
1439
1440 None
1441}
1442
1443pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1445 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().skip_norm_wip().output();
1446 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1447}
1448
1449pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1451 let arg = cx
1452 .tcx
1453 .fn_sig(fn_def_id)
1454 .instantiate_identity()
1455 .skip_norm_wip()
1456 .input(nth);
1457 cx.tcx.instantiate_bound_regions_with_erased(arg)
1458}
1459
1460pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1462 if let ExprKind::Call(fun, _) = expr.kind
1463 && let ExprKind::Path(ref qp) = fun.kind
1464 {
1465 let res = cx.qpath_res(qp, fun.hir_id);
1466 return match res {
1467 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1468 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1469 _ => false,
1470 };
1471 }
1472 false
1473}
1474
1475pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1478 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1479 !matches!(
1480 cx.qpath_res(qpath, id),
1481 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1482 )
1483 }
1484
1485 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1486 i.into_iter().any(|pat| is_refutable(cx, pat))
1487 }
1488
1489 match pat.kind {
1490 PatKind::Missing => unreachable!(),
1491 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1493 PatKind::Ref(pat, _, _) => is_refutable(cx, pat),
1494 PatKind::Expr(PatExpr {
1495 kind: PatExprKind::Path(qpath),
1496 hir_id,
1497 ..
1498 }) => is_qpath_refutable(cx, qpath, *hir_id),
1499 PatKind::Or(pats) => {
1500 are_refutable(cx, pats)
1502 },
1503 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1504 PatKind::Struct(ref qpath, fields, _) => {
1505 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1506 },
1507 PatKind::TupleStruct(ref qpath, pats, _) => {
1508 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1509 },
1510 PatKind::Slice(head, middle, tail) => {
1511 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1512 rustc_ty::Slice(..) => {
1513 !head.is_empty() || middle.is_none() || !tail.is_empty()
1515 },
1516 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1517 _ => {
1518 true
1520 },
1521 }
1522 },
1523 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1524 }
1525}
1526
1527pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1530 if let PatKind::Or(pats) = pat.kind {
1531 pats.iter().for_each(f);
1532 } else {
1533 f(pat);
1534 }
1535}
1536
1537pub fn is_self(slf: &Param<'_>) -> bool {
1538 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1539 name.name == kw::SelfLower
1540 } else {
1541 false
1542 }
1543}
1544
1545pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1546 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1547 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1548 {
1549 return true;
1550 }
1551 false
1552}
1553
1554pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1555 (0..decl.inputs.len()).map(move |i| &body.params[i])
1556}
1557
1558pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1561 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1562 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1563 && ddpos.as_opt_usize().is_none()
1564 && cx
1565 .qpath_res(path, arm.pat.hir_id)
1566 .ctor_parent(cx)
1567 .is_lang_item(cx, ResultOk)
1568 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1569 && arm.body.res_local_id() == Some(hir_id)
1570 {
1571 return true;
1572 }
1573 false
1574 }
1575
1576 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1577 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1578 cx.qpath_res(path, arm.pat.hir_id)
1579 .ctor_parent(cx)
1580 .is_lang_item(cx, ResultErr)
1581 } else {
1582 false
1583 }
1584 }
1585
1586 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1587 if let MatchSource::TryDesugar(_) = *source {
1589 return Some(expr);
1590 }
1591
1592 if arms.len() == 2
1593 && arms[0].guard.is_none()
1594 && arms[1].guard.is_none()
1595 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1596 {
1597 return Some(expr);
1598 }
1599 }
1600
1601 None
1602}
1603
1604pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1614 let mut suppress_lint = false;
1615
1616 for id in ids {
1617 let level_spec = cx.tcx.lint_level_spec_at_node(lint, id);
1618 if let Some(expectation) = level_spec.lint_id() {
1619 cx.fulfill_expectation(expectation);
1620 }
1621
1622 match level_spec.level() {
1623 Level::Allow | Level::Expect => suppress_lint = true,
1624 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1625 }
1626 }
1627
1628 suppress_lint
1629}
1630
1631pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1639 cx.tcx.lint_level_spec_at_node(lint, id).is_allow()
1640}
1641
1642pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1643 while let PatKind::Ref(subpat, _, _) = pat.kind {
1644 pat = subpat;
1645 }
1646 pat
1647}
1648
1649pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1650 Integer::from_int_ty(&tcx, ity).size().bits()
1651}
1652
1653#[expect(clippy::cast_possible_wrap)]
1654pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1656 let amt = 128 - int_bits(tcx, ity);
1657 ((u as i128) << amt) >> amt
1658}
1659
1660#[expect(clippy::cast_sign_loss)]
1661pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1663 let amt = 128 - int_bits(tcx, ity);
1664 ((u as u128) << amt) >> amt
1665}
1666
1667pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1669 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1670 let amt = 128 - bits;
1671 (u << amt) >> amt
1672}
1673
1674pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1675 find_attr!(cx.tcx, hir_id, Repr { .. })
1676}
1677
1678pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1681 tcx.hir_parent_owner_iter(id)
1682 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1683 .any(|(id, _)| find_attr!(tcx, id.def_id, AutomaticallyDerived))
1684}
1685
1686pub fn requires_exact_signature(attrs: &[rustc_attr_ir::Attribute]) -> bool {
1688 find_attr!(attrs, ProcMacro | ProcMacroAttribute | ProcMacroDerive { .. })
1689}
1690
1691pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1693 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1697}
1698
1699pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1704 let mut conds = Vec::new();
1705 let mut blocks: Vec<&Block<'_>> = Vec::new();
1706
1707 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1708 conds.push(cond);
1709 if let ExprKind::Block(block, _) = then.kind {
1710 blocks.push(block);
1711 } else {
1712 panic!("ExprKind::If node is not an ExprKind::Block");
1713 }
1714
1715 if let Some(else_expr) = r#else {
1716 expr = else_expr;
1717 } else {
1718 break;
1719 }
1720 }
1721
1722 if !blocks.is_empty()
1724 && let ExprKind::Block(block, _) = expr.kind
1725 {
1726 blocks.push(block);
1727 }
1728
1729 (conds, blocks)
1730}
1731
1732pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1734 if let ExprKind::Closure(&Closure {
1735 body,
1736 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1737 ..
1738 }) = expr.kind
1739 && let ExprKind::Block(
1740 Block {
1741 expr:
1742 Some(Expr {
1743 kind: ExprKind::DropTemps(inner_expr),
1744 ..
1745 }),
1746 ..
1747 },
1748 _,
1749 ) = tcx.hir_body(body).value.kind
1750 {
1751 Some(inner_expr)
1752 } else {
1753 None
1754 }
1755}
1756
1757pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1759 get_async_closure_expr(tcx, body.value)
1760}
1761
1762pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1764 let did = match expr.kind {
1765 ExprKind::Call(path, _) => {
1766 if let ExprKind::Path(ref qpath) = path.kind
1767 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1768 {
1769 Some(did)
1770 } else {
1771 None
1772 }
1773 },
1774 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1775 _ => None,
1776 };
1777
1778 did.is_some_and(|did| find_attr!(cx.tcx, did, MustUse { .. }))
1779}
1780
1781fn is_body_identity_function<'hir>(cx: &LateContext<'_>, func: &Body<'hir>) -> bool {
1795 let [param] = func.params else {
1796 return false;
1797 };
1798
1799 let mut param_pat = param.pat;
1800
1801 let mut advance_param_pat_over_stmts = |stmts: &[Stmt<'hir>]| {
1808 for stmt in stmts {
1809 if let StmtKind::Let(local) = stmt.kind
1810 && let Some(init) = local.init
1811 && is_expr_identity_of_pat(cx, param_pat, init, true)
1812 {
1813 param_pat = local.pat;
1814 } else {
1815 return false;
1816 }
1817 }
1818
1819 true
1820 };
1821
1822 let mut expr = func.value;
1823 loop {
1824 match expr.kind {
1825 ExprKind::Block(
1826 &Block {
1827 stmts: [],
1828 expr: Some(e),
1829 ..
1830 },
1831 _,
1832 )
1833 | ExprKind::Ret(Some(e)) => expr = e,
1834 ExprKind::Block(
1835 &Block {
1836 stmts: [stmt],
1837 expr: None,
1838 ..
1839 },
1840 _,
1841 ) => {
1842 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1843 && let ExprKind::Ret(Some(ret_val)) = e.kind
1844 {
1845 expr = ret_val;
1846 } else {
1847 return false;
1848 }
1849 },
1850 ExprKind::Block(
1851 &Block {
1852 stmts, expr: Some(e), ..
1853 },
1854 _,
1855 ) => {
1856 if !advance_param_pat_over_stmts(stmts) {
1857 return false;
1858 }
1859
1860 expr = e;
1861 },
1862 ExprKind::Block(&Block { stmts, expr: None, .. }, _) => {
1863 if let Some((last_stmt, stmts)) = stmts.split_last()
1864 && advance_param_pat_over_stmts(stmts)
1865 && let StmtKind::Semi(e) | StmtKind::Expr(e) = last_stmt.kind
1866 && let ExprKind::Ret(Some(ret_val)) = e.kind
1867 {
1868 expr = ret_val;
1869 } else {
1870 return false;
1871 }
1872 },
1873 _ => return is_expr_identity_of_pat(cx, param_pat, expr, true),
1874 }
1875 }
1876}
1877
1878pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1888 if cx
1889 .typeck_results()
1890 .pat_binding_modes()
1891 .get(pat.hir_id)
1892 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..)))
1893 {
1894 return false;
1898 }
1899
1900 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1902
1903 match (pat.kind, expr.kind) {
1904 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1905 expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1906 },
1907 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1908 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1909 },
1910 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1911 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1912 {
1913 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1914 },
1915 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1916 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1917 },
1918 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1919 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1920 {
1921 if let ExprKind::Path(ident) = &ident.kind
1923 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1924 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
1926 {
1927 true
1928 } else {
1929 false
1930 }
1931 },
1932 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
1933 if field_pats.len() == fields.len() =>
1934 {
1935 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1937 && unordered_over(field_pats, fields, |field_pat, field| {
1939 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
1940 })
1941 },
1942 _ => false,
1943 }
1944}
1945
1946pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1951 match expr.kind {
1952 ExprKind::Closure(&Closure { body, fn_decl, .. })
1953 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1954 {
1955 is_body_identity_function(cx, cx.tcx.hir_body(body))
1956 },
1957 ExprKind::Path(QPath::Resolved(_, path))
1958 if path.segments.iter().all(|seg| seg.infer_args)
1959 && let Some(did) = path.res.opt_def_id() =>
1960 {
1961 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1962 },
1963 _ => false,
1964 }
1965}
1966
1967pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1976 match expr.kind {
1977 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1978 _ => expr.basic_res().is_diag_item(cx, sym::convert_identity),
1979 }
1980}
1981
1982pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
1985 for (node, child_id) in hir_parent_with_src_iter(tcx, expr.hir_id) {
1986 match node {
1987 Node::Block(_) => {},
1988 Node::Arm(arm) if arm.body.hir_id == child_id => {},
1989 Node::Expr(expr) => match expr.kind {
1990 ExprKind::Block(..) | ExprKind::DropTemps(_) => {},
1991 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => {},
1992 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => return None,
1993 _ => return Some((Node::Expr(expr), child_id)),
1994 },
1995 node => return Some((node, child_id)),
1996 }
1997 }
1998 None
1999}
2000
2001pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2003 !matches!(
2004 get_expr_use_or_unification_node(tcx, expr),
2005 None | Some((
2006 Node::Stmt(Stmt {
2007 kind: StmtKind::Expr(_)
2008 | StmtKind::Semi(_)
2009 | StmtKind::Let(LetStmt {
2010 pat: Pat {
2011 kind: PatKind::Wild,
2012 ..
2013 },
2014 ..
2015 }),
2016 ..
2017 }),
2018 _
2019 ))
2020 )
2021}
2022
2023pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2025 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2026}
2027
2028pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2032 !expr.is_place_expr(|base| {
2033 cx.typeck_results()
2034 .adjustments()
2035 .get(base.hir_id)
2036 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2037 })
2038}
2039
2040pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2041 if is_no_core_crate(cx) {
2042 None
2043 } else if is_no_std_crate(cx) {
2044 Some("core")
2045 } else {
2046 Some("std")
2047 }
2048}
2049
2050pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2051 find_attr!(cx.tcx, crate, NoStd)
2052}
2053
2054pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2055 find_attr!(cx.tcx, crate, NoCore)
2056}
2057
2058pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2068 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2069 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2070 } else {
2071 false
2072 }
2073}
2074
2075pub fn fn_has_unsatisfiable_clauses(cx: &LateContext<'_>, did: DefId) -> bool {
2085 use rustc_trait_selection::traits;
2086 let clauses = cx
2087 .tcx
2088 .clauses_of(did)
2089 .clauses
2090 .iter()
2091 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2092 traits::impossible_clauses(cx.tcx, traits::elaborate(cx.tcx, clauses).collect::<Vec<_>>())
2093}
2094
2095pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2097 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2098}
2099
2100pub fn fn_def_id_with_node_args<'tcx>(
2103 cx: &LateContext<'tcx>,
2104 expr: &Expr<'_>,
2105) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2106 let typeck = cx.typeck_results();
2107 match &expr.kind {
2108 ExprKind::MethodCall(..) => Some((
2109 typeck.type_dependent_def_id(expr.hir_id)?,
2110 typeck.node_args(expr.hir_id),
2111 )),
2112 ExprKind::Call(
2113 Expr {
2114 kind: ExprKind::Path(qpath),
2115 hir_id: path_hir_id,
2116 ..
2117 },
2118 ..,
2119 ) => {
2120 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2123 typeck.qpath_res(qpath, *path_hir_id)
2124 {
2125 Some((id, typeck.node_args(*path_hir_id)))
2126 } else {
2127 None
2128 }
2129 },
2130 _ => None,
2131 }
2132}
2133
2134pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2139 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2140 let expr_kind = expr_type.kind();
2141 let is_primitive = match expr_kind {
2142 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2143 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2144 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2145 is_recursively_primitive_type(*element_type)
2146 } else {
2147 unreachable!()
2148 }
2149 },
2150 _ => false,
2151 };
2152
2153 if is_primitive {
2154 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2157 rustc_ty::Slice(..) => return Some("slice".into()),
2158 rustc_ty::Array(..) => return Some("array".into()),
2159 rustc_ty::Tuple(..) => return Some("tuple".into()),
2160 _ => {
2161 let refs_peeled = expr_type.peel_refs();
2164 return Some(refs_peeled.walk().last().unwrap().to_string());
2165 },
2166 }
2167 }
2168 None
2169}
2170
2171pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2179where
2180 Hash: FnMut(&T) -> u64,
2181 Eq: FnMut(&T, &T) -> bool,
2182{
2183 match exprs {
2184 [a, b] if eq(a, b) => return vec![vec![a, b]],
2185 _ if exprs.len() <= 2 => return vec![],
2186 _ => {},
2187 }
2188
2189 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2190
2191 for expr in exprs {
2192 match buckets.entry(hash(expr)) {
2193 indexmap::map::Entry::Occupied(mut o) => {
2194 let bucket = o.get_mut();
2195 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2196 Some(group) => group.push(expr),
2197 None => bucket.push(vec![expr]),
2198 }
2199 },
2200 indexmap::map::Entry::Vacant(v) => {
2201 v.insert(vec![vec![expr]]);
2202 },
2203 }
2204 }
2205
2206 buckets
2207 .into_values()
2208 .flatten()
2209 .filter(|group| group.len() > 1)
2210 .collect()
2211}
2212
2213pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2216 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2217 if let PatKind::Ref(pat, _, _) = pat.kind {
2218 peel(pat, count + 1)
2219 } else {
2220 (pat, count)
2221 }
2222 }
2223 peel(pat, 0)
2224}
2225
2226pub fn peel_hir_expr_while<'tcx>(
2228 mut expr: &'tcx Expr<'tcx>,
2229 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2230) -> &'tcx Expr<'tcx> {
2231 while let Some(e) = f(expr) {
2232 expr = e;
2233 }
2234 expr
2235}
2236
2237pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2240 let mut remaining = count;
2241 let e = peel_hir_expr_while(expr, |e| match e.kind {
2242 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2243 remaining -= 1;
2244 Some(e)
2245 },
2246 _ => None,
2247 });
2248 (e, count - remaining)
2249}
2250
2251pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2254 let mut count: usize = 0;
2255 let mut curr_expr = expr;
2256 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2257 count = count.wrapping_add(1);
2258 curr_expr = local_expr;
2259 }
2260 (curr_expr, count)
2261}
2262
2263pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2266 let mut count = 0;
2267 let e = peel_hir_expr_while(expr, |e| match e.kind {
2268 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2269 count += 1;
2270 Some(e)
2271 },
2272 _ => None,
2273 });
2274 (e, count)
2275}
2276
2277pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2280 let mut count = 0;
2281 loop {
2282 match &ty.kind {
2283 TyKind::Ref(_, inner_ty, _) => {
2284 ty = inner_ty;
2285 count += 1;
2286 },
2287 _ => break (ty, count),
2288 }
2289 }
2290}
2291
2292pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
2294 match &ty.kind {
2295 TyKind::Ptr(inner_ty, _) | TyKind::Ref(_, inner_ty, _) => peel_hir_ty_refs_and_ptrs(inner_ty),
2296 _ => ty,
2297 }
2298}
2299
2300pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2303 loop {
2304 match expr.kind {
2305 ExprKind::AddrOf(_, _, e) => expr = e,
2306 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2307 _ => break,
2308 }
2309 }
2310 expr
2311}
2312
2313pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2316 let mut operators = Vec::new();
2317 peel_hir_expr_while(expr, |expr| match expr.kind {
2318 ExprKind::AddrOf(_, _, e) => {
2319 operators.push(expr);
2320 Some(e)
2321 },
2322 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2323 operators.push(expr);
2324 Some(e)
2325 },
2326 _ => None,
2327 });
2328 operators
2329}
2330
2331pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2332 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2333 && let Res::Def(_, def_id) = path.res
2334 {
2335 return find_attr!(cx.tcx, def_id, CfgTrace(..) | CfgAttrTrace(..));
2336 }
2337 false
2338}
2339
2340static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModId, Vec<Symbol>>>> = OnceLock::new();
2341
2342fn test_item_names(tcx: TyCtxt<'_>, module: LocalModId) -> Vec<Symbol> {
2345 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2346 let mut map = cache.lock().unwrap();
2347 match map.entry(module) {
2348 Entry::Occupied(entry) => entry.get().clone(),
2349 Entry::Vacant(entry) => {
2350 let mut names = Vec::new();
2351 for id in tcx.hir_module_free_items(module) {
2352 if matches!(tcx.def_kind(id.owner_id), DefKind::Static { .. })
2353 && let item = tcx.hir_item(id)
2354 && let ItemKind::Static(_mut, ident, ty, _body) = item.kind
2355 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2356 && let Res::Def(DefKind::Struct, _) = path.res
2358 && find_attr!(tcx, item.hir_id(), RustcTestMarker(..))
2359 {
2360 names.push(ident.name);
2361 }
2362 }
2363 names.sort_unstable();
2364 entry.insert(names).clone()
2365 },
2366 }
2367}
2368
2369pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2373 let names = test_item_names(tcx, tcx.parent_module(id));
2374 if names.is_empty() {
2376 return false;
2377 }
2378 once((id, tcx.hir_node(id)))
2379 .chain(tcx.hir_parent_iter(id))
2380 .any(|(_id, node)| {
2383 if let Node::Item(item) = node
2384 && let ItemKind::Fn { ident, .. } = item.kind
2385 {
2386 return names.binary_search(&ident.name).is_ok();
2389 }
2390 false
2391 })
2392}
2393
2394pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2401 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2402 if let Node::Item(item) = tcx.hir_node(id)
2403 && let ItemKind::Fn { ident, .. } = item.kind
2404 {
2405 test_item_names(tcx, tcx.parent_module(id))
2406 .binary_search(&ident.name)
2407 .is_ok()
2408 } else {
2409 false
2410 }
2411}
2412
2413pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2418 if let Some(cfgs) = find_attr!(tcx, id, CfgTrace(cfgs) => cfgs)
2419 && cfgs
2420 .iter()
2421 .any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. }))
2422 {
2423 true
2424 } else {
2425 false
2426 }
2427}
2428
2429pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2431 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2432}
2433
2434pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2436 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id) || is_in_integration_test_file(tcx)
2437}
2438
2439fn is_in_integration_test_file(tcx: TyCtxt<'_>) -> bool {
2441 if let Input::File(ref path) = tcx.sess.io.input
2442 && !tcx.sess.opts.unstable_opts.ui_testing
2443 {
2444 path.starts_with("tests")
2445 } else {
2446 false
2447 }
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::NestedUseTree(_)
2792 | Node::WherePredicate(_)
2793 | Node::TestBinderForall(_)
2794 | Node::TestBinderExists(_)
2795 | Node::TestBinderBoundTypeConstraint(_) => {
2796 debug_assert!(false, "found {parent:?} which is after the final use node");
2799 return None;
2800 },
2801 }
2802
2803 ctxt = parent_ctxt;
2804 Some(ExprUseSite {
2805 node: parent,
2806 child_id,
2807 adjustments: mem::replace(&mut adjustments, parent_adjustments),
2808 is_ty_unified: mem::replace(&mut is_ty_unified, false),
2809 moved_before_use: mem::replace(&mut moved_before_use, false),
2810 same_ctxt: mem::replace(&mut same_ctxt, true),
2811 })
2812 },
2813 )
2814}
2815
2816pub fn get_expr_use_site<'tcx>(
2817 tcx: TyCtxt<'tcx>,
2818 typeck: &'tcx TypeckResults<'tcx>,
2819 ctxt: SyntaxContext,
2820 e: &'tcx Expr<'tcx>,
2821) -> ExprUseSite<'tcx> {
2822 expr_use_sites(tcx, typeck, ctxt, e).next().unwrap_or_else(|| {
2825 debug_assert!(false, "failed to find a use site for expr {e:?}");
2826 ExprUseSite {
2827 node: Node::Synthetic, child_id: CRATE_HIR_ID,
2829 adjustments: &[],
2830 is_ty_unified: false,
2831 moved_before_use: false,
2832 same_ctxt: false,
2833 }
2834 })
2835}
2836
2837pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2839 let mut pos = 0;
2840 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2841 let end = pos + t.len;
2842 let range = pos as usize..end as usize;
2843 let inner = InnerSpan::new(range.start, range.end);
2844 pos = end;
2845 (t.kind, s.get(range).unwrap_or_default(), inner)
2846 })
2847}
2848
2849pub fn span_contains_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool {
2852 span.check_text(sm, |snippet| {
2853 tokenize(snippet, FrontmatterAllowed::No).any(|token| {
2854 matches!(
2855 token.kind,
2856 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2857 )
2858 })
2859 })
2860}
2861
2862pub fn span_contains_non_whitespace<'sm>(sm: impl HasSourceMap<'sm>, span: Span, skip_comments: bool) -> bool {
2867 span.check_text(sm, |snippet| {
2868 tokenize_with_text(snippet).any(|(token, _, _)| match token {
2869 TokenKind::Whitespace => false,
2870 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2871 _ => true,
2872 })
2873 })
2874}
2875
2876pub fn span_extract_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> String {
2880 span_extract_comments(sm, span).join("\n")
2881}
2882
2883pub fn span_extract_comments<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Vec<String> {
2887 span.with_source_text(sm, |snippet| {
2888 tokenize_with_text(snippet)
2889 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2890 .map(|(_, s, _)| s.to_string())
2891 .collect::<Vec<_>>()
2892 })
2893 .unwrap_or_default()
2894}
2895
2896pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2897 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2898}
2899
2900pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2925 cx: &LateContext<'_>,
2926 pat: &'a Pat<'hir>,
2927 else_body: &Expr<'_>,
2928) -> Option<&'a Pat<'hir>> {
2929 if let Some([inner_pat]) = as_some_pattern(cx, pat)
2930 && !is_refutable(cx, inner_pat)
2931 && let else_body = peel_blocks(else_body)
2932 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2933 && let ExprKind::Path(ret_path) = ret_val.kind
2934 && cx
2935 .qpath_res(&ret_path, ret_val.hir_id)
2936 .ctor_parent(cx)
2937 .is_lang_item(cx, OptionNone)
2938 {
2939 Some(inner_pat)
2940 } else {
2941 None
2942 }
2943}
2944
2945macro_rules! op_utils {
2946 ($($name:ident $assign:ident)*) => {
2947 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2949
2950 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2952
2953 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2955 match kind {
2956 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2957 _ => None,
2958 }
2959 }
2960 };
2961}
2962
2963op_utils! {
2964 Add AddAssign
2965 Sub SubAssign
2966 Mul MulAssign
2967 Div DivAssign
2968 Rem RemAssign
2969 BitXor BitXorAssign
2970 BitAnd BitAndAssign
2971 BitOr BitOrAssign
2972 Shl ShlAssign
2973 Shr ShrAssign
2974}
2975
2976pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2979 match *pat {
2980 PatKind::Wild => true,
2981 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2982 !visitors::is_local_used(cx, body, id)
2983 },
2984 _ => false,
2985 }
2986}
2987
2988#[derive(Clone, Copy)]
2989pub enum RequiresSemi {
2990 Yes,
2991 No,
2992}
2993impl RequiresSemi {
2994 pub fn requires_semi(self) -> bool {
2995 matches!(self, Self::Yes)
2996 }
2997}
2998
2999#[expect(clippy::too_many_lines)]
3002pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
3003 struct BreakTarget {
3004 id: HirId,
3005 unused: bool,
3006 }
3007
3008 struct V<'cx, 'tcx> {
3009 cx: &'cx LateContext<'tcx>,
3010 break_targets: Vec<BreakTarget>,
3011 break_targets_for_result_ty: u32,
3012 in_final_expr: bool,
3013 requires_semi: bool,
3014 is_never: bool,
3015 }
3016
3017 impl V<'_, '_> {
3018 fn push_break_target(&mut self, id: HirId) {
3019 self.break_targets.push(BreakTarget { id, unused: true });
3020 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3021 }
3022 }
3023
3024 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3025 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3026 if self.is_never && self.break_targets.is_empty() {
3043 if self.in_final_expr && !self.requires_semi {
3044 match e.kind {
3047 ExprKind::DropTemps(e) => self.visit_expr(e),
3048 ExprKind::If(_, then, Some(else_)) => {
3049 self.visit_expr(then);
3050 self.visit_expr(else_);
3051 },
3052 ExprKind::Match(_, arms, _) => {
3053 for arm in arms {
3054 self.visit_expr(arm.body);
3055 }
3056 },
3057 ExprKind::Loop(b, ..) => {
3058 self.push_break_target(e.hir_id);
3059 self.in_final_expr = false;
3060 self.visit_block(b);
3061 self.break_targets.pop();
3062 },
3063 ExprKind::Block(b, _) => {
3064 if b.targeted_by_break {
3065 self.push_break_target(b.hir_id);
3066 self.visit_block(b);
3067 self.break_targets.pop();
3068 } else {
3069 self.visit_block(b);
3070 }
3071 },
3072 _ => {
3073 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3074 },
3075 }
3076 }
3077 return;
3078 }
3079 match e.kind {
3080 ExprKind::DropTemps(e) => self.visit_expr(e),
3081 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3082 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3083 self.in_final_expr = false;
3084 self.visit_expr(e);
3085 self.is_never = true;
3086 },
3087 ExprKind::Break(dest, e) => {
3088 if let Some(e) = e {
3089 self.in_final_expr = false;
3090 self.visit_expr(e);
3091 }
3092 if let Ok(id) = dest.target_id
3093 && let Some((i, target)) = self
3094 .break_targets
3095 .iter_mut()
3096 .enumerate()
3097 .find(|(_, target)| target.id == id)
3098 {
3099 target.unused &= self.is_never;
3100 if i < self.break_targets_for_result_ty as usize {
3101 self.requires_semi = true;
3102 }
3103 }
3104 self.is_never = true;
3105 },
3106 ExprKind::If(cond, then, else_) => {
3107 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3108 self.visit_expr(cond);
3109 self.in_final_expr = in_final_expr;
3110
3111 if self.is_never {
3112 self.visit_expr(then);
3113 if let Some(else_) = else_ {
3114 self.visit_expr(else_);
3115 }
3116 } else {
3117 self.visit_expr(then);
3118 let is_never = mem::replace(&mut self.is_never, false);
3119 if let Some(else_) = else_ {
3120 self.visit_expr(else_);
3121 self.is_never &= is_never;
3122 }
3123 }
3124 },
3125 ExprKind::Match(scrutinee, arms, _) => {
3126 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3127 self.visit_expr(scrutinee);
3128 self.in_final_expr = in_final_expr;
3129
3130 if self.is_never {
3131 for arm in arms {
3132 self.visit_arm(arm);
3133 }
3134 } else {
3135 let mut is_never = true;
3136 for arm in arms {
3137 self.is_never = false;
3138 if let Some(guard) = arm.guard {
3139 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3140 self.visit_expr(guard);
3141 self.in_final_expr = in_final_expr;
3142 self.is_never = false;
3145 }
3146 self.visit_expr(arm.body);
3147 is_never &= self.is_never;
3148 }
3149 self.is_never = is_never;
3150 }
3151 },
3152 ExprKind::Loop(b, _, _, _) => {
3153 self.push_break_target(e.hir_id);
3154 self.in_final_expr = false;
3155 self.visit_block(b);
3156 self.is_never = self.break_targets.pop().unwrap().unused;
3157 },
3158 ExprKind::Block(b, _) => {
3159 if b.targeted_by_break {
3160 self.push_break_target(b.hir_id);
3161 self.visit_block(b);
3162 self.is_never &= self.break_targets.pop().unwrap().unused;
3163 } else {
3164 self.visit_block(b);
3165 }
3166 },
3167 _ => {
3168 self.in_final_expr = false;
3169 walk_expr(self, e);
3170 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3171 },
3172 }
3173 }
3174
3175 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3176 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3177 for s in b.stmts {
3178 self.visit_stmt(s);
3179 }
3180 self.in_final_expr = in_final_expr;
3181 if let Some(e) = b.expr {
3182 self.visit_expr(e);
3183 }
3184 }
3185
3186 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3187 if let Some(e) = l.init {
3188 self.visit_expr(e);
3189 }
3190 if let Some(else_) = l.els {
3191 let is_never = self.is_never;
3192 self.visit_block(else_);
3193 self.is_never = is_never;
3194 }
3195 }
3196
3197 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3198 if let Some(guard) = arm.guard {
3199 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3200 self.visit_expr(guard);
3201 self.in_final_expr = in_final_expr;
3202 }
3203 self.visit_expr(arm.body);
3204 }
3205 }
3206
3207 if cx.typeck_results().expr_ty(e).is_never() {
3208 Some(RequiresSemi::No)
3209 } else if let ExprKind::Block(b, _) = e.kind
3210 && !b.targeted_by_break
3211 && b.expr.is_none()
3212 {
3213 None
3215 } else {
3216 let mut v = V {
3217 cx,
3218 break_targets: Vec::new(),
3219 break_targets_for_result_ty: 0,
3220 in_final_expr: true,
3221 requires_semi: false,
3222 is_never: false,
3223 };
3224 v.visit_expr(e);
3225 v.is_never
3226 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3227 RequiresSemi::Yes
3228 } else {
3229 RequiresSemi::No
3230 })
3231 }
3232}
3233
3234pub fn get_path_from_caller_to_method_type<'tcx>(
3240 tcx: TyCtxt<'tcx>,
3241 from: LocalDefId,
3242 method: DefId,
3243 args: GenericArgsRef<'tcx>,
3244) -> String {
3245 let assoc_item = tcx.associated_item(method);
3246 let def_id = assoc_item.container_id(tcx);
3247 match assoc_item.container {
3248 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3249 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3250 let ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
3251 get_path_to_ty(tcx, from, ty, args)
3252 },
3253 }
3254}
3255
3256fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3257 match ty.kind() {
3258 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3259 rustc_ty::Array(..)
3261 | rustc_ty::Dynamic(..)
3262 | rustc_ty::Never
3263 | rustc_ty::RawPtr(_, _)
3264 | rustc_ty::Ref(..)
3265 | rustc_ty::Slice(_)
3266 | rustc_ty::Tuple(_) => format!(
3267 "<{}>",
3268 EarlyBinder::bind(tcx, ty).instantiate(tcx, args).skip_norm_wip()
3269 ),
3270 _ => ty.to_string(),
3271 }
3272}
3273
3274fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3276 if callee.is_local() {
3278 let callee_path = tcx.def_path(callee);
3279 let caller_path = tcx.def_path(from.to_def_id());
3280 maybe_get_relative_path(&caller_path, &callee_path, 2)
3281 } else {
3282 tcx.def_path_str(callee)
3283 }
3284}
3285
3286fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3299 use itertools::EitherOrBoth::{Both, Left, Right};
3300
3301 let unique_parts = to
3303 .data
3304 .iter()
3305 .zip_longest(from.data.iter())
3306 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3307 .map(|el| match el {
3308 Both(l, r) => Both(l.data, r.data),
3309 Left(l) => Left(l.data),
3310 Right(r) => Right(r.data),
3311 });
3312
3313 let mut go_up_by = 0;
3315 let mut path = Vec::new();
3316 for el in unique_parts {
3317 match el {
3318 Both(l, r) => {
3319 if let DefPathData::TypeNs(sym) = l {
3329 path.push(sym);
3330 }
3331 if let DefPathData::TypeNs(_) = r {
3332 go_up_by += 1;
3333 }
3334 },
3335 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3340 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3345 _ => {},
3346 }
3347 }
3348
3349 if go_up_by > max_super {
3350 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3352 if let DefPathData::TypeNs(sym) = el.data {
3353 Some(sym)
3354 } else {
3355 None
3356 }
3357 })))
3358 } else if go_up_by == 0 && path.is_empty() {
3359 String::from("Self")
3360 } else {
3361 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3362 }
3363}
3364
3365pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3368 matches!(
3369 cx.tcx.parent_hir_node(id),
3370 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3371 )
3372}
3373
3374pub fn is_block_like(expr: &Expr<'_>) -> bool {
3377 matches!(
3378 expr.kind,
3379 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3380 )
3381}
3382
3383pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3385 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3386 match expr.kind {
3387 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3388 _ if is_block_like(expr) => is_operand,
3389 _ => false,
3390 }
3391 }
3392
3393 contains_block(expr, false)
3394}
3395
3396pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3398 if let Some(parent_expr) = get_parent_expr(cx, expr)
3399 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3400 && receiver.hir_id == expr.hir_id
3401 {
3402 return true;
3403 }
3404 false
3405}
3406
3407pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3410 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3411 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3412 && temporary_ty
3413 .walk()
3414 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3415 {
3416 ControlFlow::Break(())
3417 } else {
3418 ControlFlow::Continue(())
3419 }
3420 })
3421 .is_break()
3422}
3423
3424pub fn leaks_droppable_temporary<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3427 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3428 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) {
3429 ControlFlow::Break(())
3430 } else {
3431 ControlFlow::Continue(())
3432 }
3433 })
3434 .is_break()
3435}
3436
3437pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3448 let expr_ty_is_adjusted = cx
3449 .typeck_results()
3450 .expr_adjustments(expr)
3451 .iter()
3452 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3454 if expr_ty_is_adjusted {
3455 return true;
3456 }
3457
3458 match expr.kind {
3461 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3462 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip();
3463
3464 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3465 return false;
3466 }
3467
3468 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3469 let mut args_with_ty_param = {
3470 fn_sig
3471 .inputs()
3472 .skip_binder()
3473 .iter()
3474 .skip(self_arg_count)
3475 .zip(args)
3476 .filter_map(|(arg_ty, arg)| {
3477 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3478 Some(arg)
3479 } else {
3480 None
3481 }
3482 })
3483 };
3484 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3485 },
3486 ExprKind::Struct(qpath, _, _) => {
3488 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3489 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3490 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3491 return true;
3493 };
3494 v_def
3495 .fields
3496 .iter()
3497 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3498 } else {
3499 false
3500 }
3501 },
3502 ExprKind::Block(
3504 &Block {
3505 expr: Some(ret_expr), ..
3506 },
3507 _,
3508 )
3509 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3510
3511 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3513 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3515 ExprKind::If(_, then, maybe_else) => {
3517 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3518 },
3519 ExprKind::Match(_, arms, _) => arms
3520 .iter()
3521 .map(|arm| arm.body)
3522 .any(|body| expr_requires_coercion(cx, body)),
3523 _ => false,
3524 }
3525}
3526
3527pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3530 if let Some(hir_id) = expr.res_local_id()
3531 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3532 {
3533 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3534 } else if let ExprKind::Path(p) = &expr.kind
3535 && let Some(mutability) = cx
3536 .qpath_res(p, expr.hir_id)
3537 .opt_def_id()
3538 .and_then(|id| cx.tcx.static_mutability(id))
3539 {
3540 mutability == Mutability::Mut
3541 } else if let ExprKind::Field(parent, _) = expr.kind {
3542 is_mutable(cx, parent)
3543 } else {
3544 true
3545 }
3546}
3547
3548pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3551 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3552 return hir_ty;
3553 };
3554 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3555 && let Some(segment) = path.segments.last()
3556 && segment.ident.name == sym::Option
3557 && let Res::Def(DefKind::Enum, def_id) = segment.res
3558 && def_id == option_def_id
3559 && let [GenericArg::Type(arg_ty)] = segment.args().args
3560 {
3561 hir_ty = arg_ty.as_unambig_ty();
3562 }
3563 hir_ty
3564}
3565
3566pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3569 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3570 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3571 && let ctxt = expr.span.ctxt()
3572 && for_each_expr_without_closures(into_future_arg, |e| {
3573 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3574 })
3575 .is_none()
3576 {
3577 Some(into_future_arg)
3578 } else {
3579 None
3580 }
3581}
3582
3583pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3585 if let ExprKind::Call(fn_expr, []) = &expr.kind
3586 && let ExprKind::Path(qpath) = &fn_expr.kind
3587 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3588 {
3589 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3590 } else {
3591 false
3592 }
3593}
3594
3595pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3611 let enclosing_body_owner = cx
3612 .tcx
3613 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3614 let mut prev_id = expr.hir_id;
3615 let mut skip_until_id = None;
3616 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3617 if hir_id == enclosing_body_owner {
3618 return true;
3619 }
3620 if let Some(id) = skip_until_id {
3621 prev_id = hir_id;
3622 if id == hir_id {
3623 skip_until_id = None;
3624 }
3625 continue;
3626 }
3627 match node {
3628 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3629 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3630 Node::Expr(expr) => match expr.kind {
3631 ExprKind::Ret(_) => return true,
3632 ExprKind::If(_, then, opt_else)
3633 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3634 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3635 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3636 ExprKind::Break(
3637 Destination {
3638 target_id: Ok(target_id),
3639 ..
3640 },
3641 _,
3642 ) => skip_until_id = Some(target_id),
3643 _ => break,
3644 },
3645 _ => break,
3646 }
3647 prev_id = hir_id;
3648 }
3649
3650 false
3653}
3654
3655pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3658 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3659 matches!(
3660 adj.kind,
3661 Adjust::Deref(DerefAdjustKind::Overloaded(_))
3662 | Adjust::Pointer(PointerCoercion::Unsize)
3663 | Adjust::NeverToAny
3664 )
3665 })
3666}
3667
3668pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3670 matches!(
3671 expr.kind,
3672 ExprKind::Closure(Closure {
3673 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3674 CoroutineDesugaring::Async,
3675 CoroutineSource::Block
3676 )),
3677 ..
3678 })
3679 )
3680}
3681
3682pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3684 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3685}
3686
3687#[inline]
3690pub fn hir_parent_with_src_iter(tcx: TyCtxt<'_>, mut id: HirId) -> impl Iterator<Item = (Node<'_>, HirId)> {
3691 tcx.hir_parent_id_iter(id)
3692 .map(move |parent| (tcx.hir_node(parent), mem::replace(&mut id, parent)))
3693}