1#![cfg_attr(bootstrap, feature(if_let_guard))]
2#![feature(box_patterns)]
3#![feature(macro_metavar_expr)]
4#![feature(never_type)]
5#![feature(rustc_private)]
6#![cfg_attr(bootstrap, feature(assert_matches))]
7#![feature(unwrap_infallible)]
8#![recursion_limit = "512"]
9#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
10#![warn(
11 trivial_casts,
12 trivial_numeric_casts,
13 rust_2018_idioms,
14 unused_lifetimes,
15 unused_qualifications,
16 rustc::internal
17)]
18
19extern crate rustc_abi;
22extern crate rustc_ast;
23extern crate rustc_attr_parsing;
24extern crate rustc_const_eval;
25extern crate rustc_data_structures;
26#[expect(
27 unused_extern_crates,
28 reason = "The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate."
29)]
30extern crate rustc_driver;
31extern crate rustc_errors;
32extern crate rustc_hir;
33extern crate rustc_hir_analysis;
34extern crate rustc_hir_typeck;
35extern crate rustc_index;
36extern crate rustc_infer;
37extern crate rustc_lexer;
38extern crate rustc_lint;
39extern crate rustc_middle;
40extern crate rustc_mir_dataflow;
41extern crate rustc_session;
42extern crate rustc_span;
43extern crate rustc_trait_selection;
44
45pub mod ast_utils;
46#[deny(missing_docs)]
47pub mod attrs;
48mod check_proc_macro;
49pub mod comparisons;
50pub mod consts;
51pub mod diagnostics;
52pub mod eager_or_lazy;
53pub mod higher;
54mod hir_utils;
55pub mod macros;
56pub mod mir;
57pub mod msrvs;
58pub mod numeric_literal;
59pub mod paths;
60pub mod qualify_min_const_fn;
61pub mod res;
62pub mod source;
63pub mod str_utils;
64pub mod sugg;
65pub mod sym;
66pub mod ty;
67pub mod usage;
68pub mod visitors;
69
70pub use self::attrs::*;
71pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
72pub use self::hir_utils::{
73 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
74 hash_stmt, is_bool, over,
75};
76
77use core::mem;
78use core::ops::ControlFlow;
79use std::collections::hash_map::Entry;
80use std::iter::{once, repeat_n, zip};
81use std::sync::{Mutex, MutexGuard, OnceLock};
82
83use itertools::Itertools;
84use rustc_abi::Integer;
85use rustc_ast::ast::{self, LitKind, RangeLimits};
86use rustc_ast::{LitIntType, join_path_syms};
87use rustc_data_structures::fx::FxHashMap;
88use rustc_data_structures::indexmap;
89use rustc_data_structures::packed::Pu128;
90use rustc_data_structures::unhash::UnindexMap;
91use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
92use rustc_hir::attrs::CfgEntry;
93use rustc_hir::def::{DefKind, Res};
94use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
95use rustc_hir::definitions::{DefPath, DefPathData};
96use rustc_hir::hir_id::{HirIdMap, HirIdSet};
97use rustc_hir::intravisit::{Visitor, walk_expr};
98use rustc_hir::{
99 self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
100 CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs,
101 HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId,
102 OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
103 TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr,
104};
105use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
106use rustc_lint::{LateContext, Level, Lint, LintContext};
107use rustc_middle::hir::nested_filter;
108use rustc_middle::hir::place::PlaceBase;
109use rustc_middle::lint::LevelAndSource;
110use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
111use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion};
112use rustc_middle::ty::layout::IntegerExt;
113use rustc_middle::ty::{
114 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
115 TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
116};
117use rustc_span::hygiene::{ExpnKind, MacroKind};
118use rustc_span::source_map::SourceMap;
119use rustc_span::symbol::{Ident, Symbol, kw};
120use rustc_span::{InnerSpan, Span};
121use source::{SpanRangeExt, walk_span_to_context};
122use visitors::{Visitable, for_each_unconsumed_temporary};
123
124use crate::ast_utils::unordered_over;
125use crate::consts::{ConstEvalCtxt, Constant};
126use crate::higher::Range;
127use crate::msrvs::Msrv;
128use crate::res::{MaybeDef, MaybeQPath, MaybeResPath};
129use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
130use crate::visitors::for_each_expr_without_closures;
131
132pub const VEC_METHODS_SHADOWING_SLICE_METHODS: [Symbol; 3] = [sym::as_ptr, sym::is_empty, sym::len];
134
135#[macro_export]
136macro_rules! extract_msrv_attr {
137 () => {
138 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
139 let sess = rustc_lint::LintContext::sess(cx);
140 self.msrv.check_attributes(sess, attrs);
141 }
142
143 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
144 let sess = rustc_lint::LintContext::sess(cx);
145 self.msrv.check_attributes_post(sess, attrs);
146 }
147 };
148}
149
150pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
173 while let Some(init) = expr
174 .res_local_id()
175 .and_then(|id| find_binding_init(cx, id))
176 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
177 {
178 expr = init;
179 }
180 expr
181}
182
183pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
192 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
193 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
194 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
195 {
196 return local.init;
197 }
198 None
199}
200
201pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
205 for (_, node) in cx.tcx.hir_parent_iter(local) {
206 match node {
207 Node::Pat(..) | Node::PatField(..) => {},
208 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
209 _ => return true,
210 }
211 }
212
213 false
214}
215
216pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
227 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
228 cx.enclosing_body.is_some_and(|id| {
229 cx.tcx
230 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
231 .is_some()
232 })
233}
234
235pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
242 use rustc_hir::ConstContext::{Const, ConstFn, Static};
243 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
244 return false;
245 };
246 match ctx {
247 ConstFn => false,
248 Static(_) | Const { inline: _ } => true,
249 }
250}
251
252pub fn is_enum_variant_ctor(
254 cx: &LateContext<'_>,
255 enum_item: Symbol,
256 variant_name: Symbol,
257 ctor_call_id: DefId,
258) -> bool {
259 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
260 return false;
261 };
262
263 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
264 variants
265 .filter(|variant| variant.name == variant_name)
266 .filter_map(|variant| variant.ctor.as_ref())
267 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
268}
269
270pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
272 let did = match cx.tcx.def_kind(did) {
273 DefKind::Ctor(..) => cx.tcx.parent(did),
274 DefKind::Variant => match cx.tcx.opt_parent(did) {
276 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
277 _ => did,
278 },
279 _ => did,
280 };
281
282 cx.tcx.is_diagnostic_item(item, did)
283}
284
285pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
287 let did = match cx.tcx.def_kind(did) {
288 DefKind::Ctor(..) => cx.tcx.parent(did),
289 DefKind::Variant => match cx.tcx.opt_parent(did) {
291 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
292 _ => did,
293 },
294 _ => did,
295 };
296
297 cx.tcx.lang_items().get(item) == Some(did)
298}
299
300pub fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
302 expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
303}
304
305pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
307 if let ExprKind::Call(e, [arg]) = expr.kind
308 && e.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
309 {
310 Some(arg)
311 } else {
312 None
313 }
314}
315
316pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
318 matches!(
319 expr.kind,
320 ExprKind::Block(
321 Block {
322 stmts: [],
323 expr: None,
324 ..
325 },
326 _
327 ) | ExprKind::Tup([])
328 )
329}
330
331pub fn is_wild(pat: &Pat<'_>) -> bool {
333 matches!(pat.kind, PatKind::Wild)
334}
335
336pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> {
343 if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind
344 && cx
345 .qpath_res(qpath, pat.hir_id)
346 .ctor_parent(cx)
347 .is_lang_item(cx, OptionSome)
348 {
349 Some(inner)
350 } else {
351 None
352 }
353}
354
355pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
357 matches!(pat.kind,
358 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
359 if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone))
360}
361
362pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
364 is_none_pattern(cx, arm.pat)
365 && matches!(
366 peel_blocks(arm.body).kind,
367 ExprKind::Path(qpath)
368 if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)
369 )
370}
371
372pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
374 match *qpath {
375 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
376 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
377 QPath::TypeRelative(..) => false,
378 }
379}
380
381pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
383 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
384 && let ItemKind::Impl(imp) = item.kind
385 {
386 imp.of_trait.is_some()
387 } else {
388 false
389 }
390}
391
392pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
393 match *path {
394 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
395 QPath::TypeRelative(_, seg) => seg,
396 }
397}
398
399pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
400 last_path_segment(qpath)
401 .args
402 .map_or(&[][..], |a| a.args)
403 .iter()
404 .filter_map(|a| match a {
405 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
406 _ => None,
407 })
408}
409
410pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
415 match expr.kind {
416 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
417 ExprKind::Path(QPath::Resolved(
418 _,
419 Path {
420 res: Res::Local(local), ..
421 },
422 )) => Some(*local),
423 _ => None,
424 }
425}
426
427pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
443 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
444 && let ItemKind::Impl(impl_) = &item.kind
445 && let Some(of_trait) = impl_.of_trait
446 {
447 return Some(&of_trait.trait_ref);
448 }
449 None
450}
451
452fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
460 let mut result = vec![];
461 let root = loop {
462 match e.kind {
463 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
464 result.push(e);
465 e = ep;
466 },
467 _ => break e,
468 }
469 };
470 result.reverse();
471 (result, root)
472}
473
474pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
476 cx.typeck_results()
477 .expr_adjustments(e)
478 .iter()
479 .find_map(|a| match a.kind {
480 Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)),
481 Adjust::Deref(DerefAdjustKind::Builtin) => None,
482 _ => Some(None),
483 })
484 .and_then(|x| x)
485}
486
487pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
490 let (s1, r1) = projection_stack(e1);
491 let (s2, r2) = projection_stack(e2);
492 if !eq_expr_value(cx, r1, r2) {
493 return true;
494 }
495 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
496 return false;
497 }
498
499 for (x1, x2) in zip(&s1, &s2) {
500 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
501 return false;
502 }
503
504 match (&x1.kind, &x2.kind) {
505 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
506 if i1 != i2 {
507 return true;
508 }
509 },
510 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
511 if !eq_expr_value(cx, i1, i2) {
512 return false;
513 }
514 },
515 _ => return false,
516 }
517 }
518 false
519}
520
521fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
524 let std_types_symbols = &[
525 sym::Vec,
526 sym::VecDeque,
527 sym::LinkedList,
528 sym::HashMap,
529 sym::BTreeMap,
530 sym::HashSet,
531 sym::BTreeSet,
532 sym::BinaryHeap,
533 ];
534
535 if let QPath::TypeRelative(_, method) = path
536 && method.ident.name == sym::new
537 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
538 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
539 {
540 return Some(adt.did()) == cx.tcx.lang_items().string()
541 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
542 }
543 false
544}
545
546pub fn is_default_equivalent_call(
548 cx: &LateContext<'_>,
549 repl_func: &Expr<'_>,
550 whole_call_expr: Option<&Expr<'_>>,
551) -> bool {
552 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
553 && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx)
554 && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default)
555 || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath))
556 {
557 return true;
558 }
559
560 let Some(e) = whole_call_expr else { return false };
563 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
564 return false;
565 };
566 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
567 return false;
568 };
569 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
570 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
571 cx.tcx.lifetimes.re_erased.into()
572 } else if param.index == 0 && param.name == kw::SelfUpper {
573 ty.into()
574 } else {
575 param.to_error(cx.tcx)
576 }
577 });
578 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
579
580 let Ok(Some(instance)) = instance else { return false };
581 if let rustc_ty::InstanceKind::Item(def) = instance.def
582 && !cx.tcx.is_mir_available(def)
583 {
584 return false;
585 }
586 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
587 return false;
588 };
589 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
590 return false;
591 };
592
593 let body = cx.tcx.instance_mir(instance.def);
599 for block_data in body.basic_blocks.iter() {
600 if block_data.statements.len() == 1
601 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
602 && assign.0.local == RETURN_PLACE
603 && let Rvalue::Aggregate(kind, _places) = &assign.1
604 && let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
605 && let def = cx.tcx.adt_def(did)
606 && let variant = &def.variant(*variant_index)
607 && variant.fields.is_empty()
608 && let Some((_, did)) = variant.ctor
609 && did == repl_def_id
610 {
611 return true;
612 } else if block_data.statements.is_empty()
613 && let Some(term) = &block_data.terminator
614 {
615 match &term.kind {
616 TerminatorKind::Call {
617 func: Operand::Constant(c),
618 ..
619 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
620 && *did == repl_def_id =>
621 {
622 return true;
623 },
624 TerminatorKind::TailCall {
625 func: Operand::Constant(c),
626 ..
627 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
628 && *did == repl_def_id =>
629 {
630 return true;
631 },
632 _ => {},
633 }
634 }
635 }
636 false
637}
638
639pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
643 match &e.kind {
644 ExprKind::Lit(lit) => match lit.node {
645 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
646 LitKind::Str(s, _) => s.is_empty(),
647 _ => false,
648 },
649 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
650 ExprKind::Repeat(x, len) => {
651 if let ConstArgKind::Anon(anon_const) = len.kind
652 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
653 && let LitKind::Int(v, _) = const_lit.node
654 && v <= 32
655 && is_default_equivalent(cx, x)
656 {
657 true
658 } else {
659 false
660 }
661 },
662 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
663 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
664 ExprKind::Path(qpath) => cx
665 .qpath_res(qpath, e.hir_id)
666 .ctor_parent(cx)
667 .is_lang_item(cx, OptionNone),
668 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
669 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
670 _ => false,
671 }
672}
673
674fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
675 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
676 && seg.ident.name == sym::from
677 {
678 match arg.kind {
679 ExprKind::Lit(hir::Lit {
680 node: LitKind::Str(sym, _),
681 ..
682 }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String),
683 ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec),
684 ExprKind::Repeat(_, len) => {
685 if let ConstArgKind::Anon(anon_const) = len.kind
686 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
687 && let LitKind::Int(v, _) = const_lit.node
688 {
689 return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec);
690 }
691 },
692 _ => (),
693 }
694 }
695 false
696}
697
698pub fn can_move_expr_to_closure_no_visit<'tcx>(
730 cx: &LateContext<'tcx>,
731 expr: &'tcx Expr<'_>,
732 loop_ids: &[HirId],
733 ignore_locals: &HirIdSet,
734) -> bool {
735 match expr.kind {
736 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
737 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
738 if loop_ids.contains(&id) =>
739 {
740 true
741 },
742 ExprKind::Break(..)
743 | ExprKind::Continue(_)
744 | ExprKind::Ret(_)
745 | ExprKind::Yield(..)
746 | ExprKind::InlineAsm(_) => false,
747 ExprKind::Field(
750 &Expr {
751 hir_id,
752 kind:
753 ExprKind::Path(QPath::Resolved(
754 _,
755 Path {
756 res: Res::Local(local_id),
757 ..
758 },
759 )),
760 ..
761 },
762 _,
763 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
764 false
766 },
767 _ => true,
768 }
769}
770
771#[derive(Debug, Clone, Copy, PartialEq, Eq)]
773pub enum CaptureKind {
774 Value,
775 Use,
776 Ref(Mutability),
777}
778impl CaptureKind {
779 pub fn is_imm_ref(self) -> bool {
780 self == Self::Ref(Mutability::Not)
781 }
782}
783impl std::ops::BitOr for CaptureKind {
784 type Output = Self;
785 fn bitor(self, rhs: Self) -> Self::Output {
786 match (self, rhs) {
787 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
788 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
789 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
790 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
791 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
792 }
793 }
794}
795impl std::ops::BitOrAssign for CaptureKind {
796 fn bitor_assign(&mut self, rhs: Self) {
797 *self = *self | rhs;
798 }
799}
800
801pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
807 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
808 let mut capture = CaptureKind::Ref(Mutability::Not);
809 pat.each_binding_or_first(&mut |_, id, span, _| match cx
810 .typeck_results()
811 .extract_binding_mode(cx.sess(), id, span)
812 .0
813 {
814 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
815 capture = CaptureKind::Value;
816 },
817 ByRef::Yes(_, Mutability::Mut) if capture != CaptureKind::Value => {
818 capture = CaptureKind::Ref(Mutability::Mut);
819 },
820 _ => (),
821 });
822 capture
823 }
824
825 debug_assert!(matches!(
826 e.kind,
827 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
828 ));
829
830 let mut child_id = e.hir_id;
831 let mut capture = CaptureKind::Value;
832 let mut capture_expr_ty = e;
833
834 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
835 if let [
836 Adjustment {
837 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
838 target,
839 },
840 ref adjust @ ..,
841 ] = *cx
842 .typeck_results()
843 .adjustments()
844 .get(child_id)
845 .map_or(&[][..], |x| &**x)
846 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
847 *adjust.last().map_or(target, |a| a.target).kind()
848 {
849 return CaptureKind::Ref(mutability);
850 }
851
852 match parent {
853 Node::Expr(e) => match e.kind {
854 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
855 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
856 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
857 return CaptureKind::Ref(Mutability::Mut);
858 },
859 ExprKind::Field(..) => {
860 if capture == CaptureKind::Value {
861 capture_expr_ty = e;
862 }
863 },
864 ExprKind::Let(let_expr) => {
865 let mutability = match pat_capture_kind(cx, let_expr.pat) {
866 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
867 CaptureKind::Ref(m) => m,
868 };
869 return CaptureKind::Ref(mutability);
870 },
871 ExprKind::Match(_, arms, _) => {
872 let mut mutability = Mutability::Not;
873 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
874 match capture {
875 CaptureKind::Value | CaptureKind::Use => break,
876 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
877 CaptureKind::Ref(Mutability::Not) => (),
878 }
879 }
880 return CaptureKind::Ref(mutability);
881 },
882 _ => break,
883 },
884 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
885 CaptureKind::Value | CaptureKind::Use => break,
886 capture @ CaptureKind::Ref(_) => return capture,
887 },
888 _ => break,
889 }
890
891 child_id = parent_id;
892 }
893
894 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
895 CaptureKind::Ref(Mutability::Not)
897 } else {
898 capture
899 }
900}
901
902pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
905 struct V<'cx, 'tcx> {
906 cx: &'cx LateContext<'tcx>,
907 loops: Vec<HirId>,
909 locals: HirIdSet,
911 allow_closure: bool,
913 captures: HirIdMap<CaptureKind>,
916 }
917 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
918 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
919 if !self.allow_closure {
920 return;
921 }
922
923 match e.kind {
924 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
925 if !self.locals.contains(&l) {
926 let cap = capture_local_usage(self.cx, e);
927 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
928 }
929 },
930 ExprKind::Closure(closure) => {
931 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
932 let local_id = match capture.place.base {
933 PlaceBase::Local(id) => id,
934 PlaceBase::Upvar(var) => var.var_path.hir_id,
935 _ => continue,
936 };
937 if !self.locals.contains(&local_id) {
938 let capture = match capture.info.capture_kind {
939 UpvarCapture::ByValue => CaptureKind::Value,
940 UpvarCapture::ByUse => CaptureKind::Use,
941 UpvarCapture::ByRef(kind) => match kind {
942 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
943 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
944 CaptureKind::Ref(Mutability::Mut)
945 },
946 },
947 };
948 self.captures
949 .entry(local_id)
950 .and_modify(|e| *e |= capture)
951 .or_insert(capture);
952 }
953 }
954 },
955 ExprKind::Loop(b, ..) => {
956 self.loops.push(e.hir_id);
957 self.visit_block(b);
958 self.loops.pop();
959 },
960 _ => {
961 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
962 walk_expr(self, e);
963 },
964 }
965 }
966
967 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
968 p.each_binding_or_first(&mut |_, id, _, _| {
969 self.locals.insert(id);
970 });
971 }
972 }
973
974 let mut v = V {
975 cx,
976 loops: Vec::new(),
977 locals: HirIdSet::default(),
978 allow_closure: true,
979 captures: HirIdMap::default(),
980 };
981 v.visit_expr(expr);
982 v.allow_closure.then_some(v.captures)
983}
984
985pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
987
988pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
991 let mut method_names = Vec::with_capacity(max_depth);
992 let mut arg_lists = Vec::with_capacity(max_depth);
993 let mut spans = Vec::with_capacity(max_depth);
994
995 let mut current = expr;
996 for _ in 0..max_depth {
997 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
998 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
999 break;
1000 }
1001 method_names.push(path.ident.name);
1002 arg_lists.push((*receiver, &**args));
1003 spans.push(path.ident.span);
1004 current = receiver;
1005 } else {
1006 break;
1007 }
1008 }
1009
1010 (method_names, arg_lists, spans)
1011}
1012
1013pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1020 let mut current = expr;
1021 let mut matched = Vec::with_capacity(methods.len());
1022 for method_name in methods.iter().rev() {
1023 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1025 if path.ident.name == *method_name {
1026 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1027 return None;
1028 }
1029 matched.push((receiver, args)); current = receiver; } else {
1032 return None;
1033 }
1034 } else {
1035 return None;
1036 }
1037 }
1038 matched.reverse();
1040 Some(matched)
1041}
1042
1043pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1045 cx.tcx
1046 .entry_fn(())
1047 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1048}
1049
1050pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1052 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1053 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1054}
1055
1056pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1058 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1059 match cx.tcx.hir_node_by_def_id(parent_id) {
1060 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1061 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1062 _ => None,
1063 }
1064}
1065
1066pub struct ContainsName<'a, 'tcx> {
1067 pub cx: &'a LateContext<'tcx>,
1068 pub name: Symbol,
1069}
1070
1071impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1072 type Result = ControlFlow<()>;
1073 type NestedFilter = nested_filter::OnlyBodies;
1074
1075 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1076 if self.name == name {
1077 ControlFlow::Break(())
1078 } else {
1079 ControlFlow::Continue(())
1080 }
1081 }
1082
1083 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1084 self.cx.tcx
1085 }
1086}
1087
1088pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1090 let mut cn = ContainsName { cx, name };
1091 cn.visit_expr(expr).is_break()
1092}
1093
1094pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1096 for_each_expr_without_closures(expr, |e| {
1097 if matches!(e.kind, ExprKind::Ret(..)) {
1098 ControlFlow::Break(())
1099 } else {
1100 ControlFlow::Continue(())
1101 }
1102 })
1103 .is_some()
1104}
1105
1106pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1108 get_parent_expr_for_hir(cx, e.hir_id)
1109}
1110
1111pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1114 match cx.tcx.parent_hir_node(hir_id) {
1115 Node::Expr(parent) => Some(parent),
1116 _ => None,
1117 }
1118}
1119
1120pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1122 let enclosing_node = cx
1123 .tcx
1124 .hir_get_enclosing_scope(hir_id)
1125 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1126 enclosing_node.and_then(|node| match node {
1127 Node::Block(block) => Some(block),
1128 Node::Item(&Item {
1129 kind: ItemKind::Fn { body: eid, .. },
1130 ..
1131 })
1132 | Node::ImplItem(&ImplItem {
1133 kind: ImplItemKind::Fn(_, eid),
1134 ..
1135 })
1136 | Node::TraitItem(&TraitItem {
1137 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1138 ..
1139 }) => match cx.tcx.hir_body(eid).value.kind {
1140 ExprKind::Block(block, _) => Some(block),
1141 _ => None,
1142 },
1143 _ => None,
1144 })
1145}
1146
1147pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1149 cx: &LateContext<'tcx>,
1150 expr: &Expr<'_>,
1151) -> Option<&'tcx Expr<'tcx>> {
1152 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1153 match node {
1154 Node::Expr(e) => match e.kind {
1155 ExprKind::Closure { .. }
1156 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1157 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1158
1159 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1161 _ => (),
1162 },
1163 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1164 _ => break,
1165 }
1166 }
1167 None
1168}
1169
1170pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1172 match tcx.hir_parent_iter(id).next() {
1173 Some((
1174 _,
1175 Node::Item(Item {
1176 kind: ItemKind::Impl(imp),
1177 ..
1178 }),
1179 )) => Some(imp),
1180 _ => None,
1181 }
1182}
1183
1184pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1195 while let ExprKind::Block(
1196 Block {
1197 stmts: [],
1198 expr: Some(inner),
1199 rules: BlockCheckMode::DefaultBlock,
1200 ..
1201 },
1202 _,
1203 ) = expr.kind
1204 {
1205 expr = inner;
1206 }
1207 expr
1208}
1209
1210pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1221 while let ExprKind::Block(
1222 Block {
1223 stmts: [],
1224 expr: Some(inner),
1225 rules: BlockCheckMode::DefaultBlock,
1226 ..
1227 }
1228 | Block {
1229 stmts:
1230 [
1231 Stmt {
1232 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1233 ..
1234 },
1235 ],
1236 expr: None,
1237 rules: BlockCheckMode::DefaultBlock,
1238 ..
1239 },
1240 _,
1241 ) = expr.kind
1242 {
1243 expr = inner;
1244 }
1245 expr
1246}
1247
1248pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1250 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1251 match iter.next() {
1252 Some((
1253 _,
1254 Node::Expr(Expr {
1255 kind: ExprKind::If(_, _, Some(else_expr)),
1256 ..
1257 }),
1258 )) => else_expr.hir_id == expr.hir_id,
1259 _ => false,
1260 }
1261}
1262
1263pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1266 let mut child_id = expr.hir_id;
1267 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1268 if let Node::LetStmt(LetStmt {
1269 init: Some(init),
1270 els: Some(els),
1271 ..
1272 }) = node
1273 && (init.hir_id == child_id || els.hir_id == child_id)
1274 {
1275 return true;
1276 }
1277
1278 child_id = parent_id;
1279 }
1280
1281 false
1282}
1283
1284pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1286 let mut child_id = expr.hir_id;
1287 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1288 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1289 && els.hir_id == child_id
1290 {
1291 return true;
1292 }
1293
1294 child_id = parent_id;
1295 }
1296
1297 false
1298}
1299
1300pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1315 let ty = cx.typeck_results().expr_ty(expr);
1316 if let Some(Range { start, end, limits, .. }) = Range::hir(cx, expr) {
1317 let start_is_none_or_min = start.is_none_or(|start| {
1318 if let rustc_ty::Adt(_, subst) = ty.kind()
1319 && let bnd_ty = subst.type_at(0)
1320 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1321 {
1322 start_const.is_numeric_min(cx.tcx, bnd_ty)
1323 } else {
1324 false
1325 }
1326 });
1327 let end_is_none_or_max = end.is_none_or(|end| match limits {
1328 RangeLimits::Closed => {
1329 if let rustc_ty::Adt(_, subst) = ty.kind()
1330 && let bnd_ty = subst.type_at(0)
1331 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1332 {
1333 end_const.is_numeric_max(cx.tcx, bnd_ty)
1334 } else {
1335 false
1336 }
1337 },
1338 RangeLimits::HalfOpen => {
1339 if let Some(container_path) = container_path
1340 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1341 && name.ident.name == sym::len
1342 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1343 {
1344 container_path.res == path.res
1345 } else {
1346 false
1347 }
1348 },
1349 });
1350 return start_is_none_or_min && end_is_none_or_max;
1351 }
1352 false
1353}
1354
1355pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1358 if is_integer_literal(e, value) {
1359 return true;
1360 }
1361 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1362 if let Some(Constant::Int(v)) =
1363 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1364 {
1365 return value == v;
1366 }
1367 false
1368}
1369
1370pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1372 if let ExprKind::Lit(spanned) = expr.kind
1374 && let LitKind::Int(v, _) = spanned.node
1375 {
1376 return v == value;
1377 }
1378 false
1379}
1380
1381pub fn is_integer_literal_untyped(expr: &Expr<'_>) -> bool {
1383 if let ExprKind::Lit(spanned) = expr.kind
1384 && let LitKind::Int(_, suffix) = spanned.node
1385 {
1386 return suffix == LitIntType::Unsuffixed;
1387 }
1388
1389 false
1390}
1391
1392pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1394 if let ExprKind::Lit(spanned) = expr.kind
1395 && let LitKind::Float(v, _) = spanned.node
1396 {
1397 v.as_str().parse() == Ok(value)
1398 } else {
1399 false
1400 }
1401}
1402
1403pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1411 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1412}
1413
1414#[must_use]
1418pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1419 loop {
1420 if span.from_expansion() {
1421 let data = span.ctxt().outer_expn_data();
1422 let new_span = data.call_site;
1423
1424 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1425 && mac_name == name
1426 {
1427 return Some(new_span);
1428 }
1429
1430 span = new_span;
1431 } else {
1432 return None;
1433 }
1434 }
1435}
1436
1437#[must_use]
1448pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1449 if span.from_expansion() {
1450 let data = span.ctxt().outer_expn_data();
1451 let new_span = data.call_site;
1452
1453 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1454 && mac_name == name
1455 {
1456 return Some(new_span);
1457 }
1458 }
1459
1460 None
1461}
1462
1463pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1465 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1466 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1467}
1468
1469pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1471 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1472 cx.tcx.instantiate_bound_regions_with_erased(arg)
1473}
1474
1475pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1477 if let ExprKind::Call(fun, _) = expr.kind
1478 && let ExprKind::Path(ref qp) = fun.kind
1479 {
1480 let res = cx.qpath_res(qp, fun.hir_id);
1481 return match res {
1482 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1483 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1484 _ => false,
1485 };
1486 }
1487 false
1488}
1489
1490pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1493 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1494 !matches!(
1495 cx.qpath_res(qpath, id),
1496 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1497 )
1498 }
1499
1500 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1501 i.into_iter().any(|pat| is_refutable(cx, pat))
1502 }
1503
1504 match pat.kind {
1505 PatKind::Missing => unreachable!(),
1506 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1508 PatKind::Box(pat) | PatKind::Ref(pat, _, _) => is_refutable(cx, pat),
1509 PatKind::Expr(PatExpr {
1510 kind: PatExprKind::Path(qpath),
1511 hir_id,
1512 ..
1513 }) => is_qpath_refutable(cx, qpath, *hir_id),
1514 PatKind::Or(pats) => {
1515 are_refutable(cx, pats)
1517 },
1518 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1519 PatKind::Struct(ref qpath, fields, _) => {
1520 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1521 },
1522 PatKind::TupleStruct(ref qpath, pats, _) => {
1523 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1524 },
1525 PatKind::Slice(head, middle, tail) => {
1526 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1527 rustc_ty::Slice(..) => {
1528 !head.is_empty() || middle.is_none() || !tail.is_empty()
1530 },
1531 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1532 _ => {
1533 true
1535 },
1536 }
1537 },
1538 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1539 }
1540}
1541
1542pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1545 if let PatKind::Or(pats) = pat.kind {
1546 pats.iter().for_each(f);
1547 } else {
1548 f(pat);
1549 }
1550}
1551
1552pub fn is_self(slf: &Param<'_>) -> bool {
1553 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1554 name.name == kw::SelfLower
1555 } else {
1556 false
1557 }
1558}
1559
1560pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1561 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1562 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1563 {
1564 return true;
1565 }
1566 false
1567}
1568
1569pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1570 (0..decl.inputs.len()).map(move |i| &body.params[i])
1571}
1572
1573pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1576 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1577 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1578 && ddpos.as_opt_usize().is_none()
1579 && cx
1580 .qpath_res(path, arm.pat.hir_id)
1581 .ctor_parent(cx)
1582 .is_lang_item(cx, ResultOk)
1583 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1584 && arm.body.res_local_id() == Some(hir_id)
1585 {
1586 return true;
1587 }
1588 false
1589 }
1590
1591 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1592 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1593 cx.qpath_res(path, arm.pat.hir_id)
1594 .ctor_parent(cx)
1595 .is_lang_item(cx, ResultErr)
1596 } else {
1597 false
1598 }
1599 }
1600
1601 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1602 if let MatchSource::TryDesugar(_) = *source {
1604 return Some(expr);
1605 }
1606
1607 if arms.len() == 2
1608 && arms[0].guard.is_none()
1609 && arms[1].guard.is_none()
1610 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1611 {
1612 return Some(expr);
1613 }
1614 }
1615
1616 None
1617}
1618
1619pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1629 let mut suppress_lint = false;
1630
1631 for id in ids {
1632 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1633 if let Some(expectation) = lint_id {
1634 cx.fulfill_expectation(expectation);
1635 }
1636
1637 match level {
1638 Level::Allow | Level::Expect => suppress_lint = true,
1639 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1640 }
1641 }
1642
1643 suppress_lint
1644}
1645
1646pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1654 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1655}
1656
1657pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1658 while let PatKind::Ref(subpat, _, _) = pat.kind {
1659 pat = subpat;
1660 }
1661 pat
1662}
1663
1664pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1665 Integer::from_int_ty(&tcx, ity).size().bits()
1666}
1667
1668#[expect(clippy::cast_possible_wrap)]
1669pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1671 let amt = 128 - int_bits(tcx, ity);
1672 ((u as i128) << amt) >> amt
1673}
1674
1675#[expect(clippy::cast_sign_loss)]
1676pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1678 let amt = 128 - int_bits(tcx, ity);
1679 ((u as u128) << amt) >> amt
1680}
1681
1682pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1684 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1685 let amt = 128 - bits;
1686 (u << amt) >> amt
1687}
1688
1689pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1690 attrs.iter().any(|attr| attr.has_name(symbol))
1691}
1692
1693pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1694 find_attr!(cx.tcx.hir_attrs(hir_id), Repr { .. })
1695}
1696
1697pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1698 let mut prev_enclosing_node = None;
1699 let mut enclosing_node = node;
1700 while Some(enclosing_node) != prev_enclosing_node {
1701 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1702 return true;
1703 }
1704 prev_enclosing_node = Some(enclosing_node);
1705 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1706 }
1707
1708 false
1709}
1710
1711pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1714 tcx.hir_parent_owner_iter(id)
1715 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1716 .any(|(id, _)| {
1717 find_attr!(
1718 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1719 AutomaticallyDerived(..)
1720 )
1721 })
1722}
1723
1724pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1726 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1729}
1730
1731pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1736 let mut conds = Vec::new();
1737 let mut blocks: Vec<&Block<'_>> = Vec::new();
1738
1739 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1740 conds.push(cond);
1741 if let ExprKind::Block(block, _) = then.kind {
1742 blocks.push(block);
1743 } else {
1744 panic!("ExprKind::If node is not an ExprKind::Block");
1745 }
1746
1747 if let Some(else_expr) = r#else {
1748 expr = else_expr;
1749 } else {
1750 break;
1751 }
1752 }
1753
1754 if !blocks.is_empty()
1756 && let ExprKind::Block(block, _) = expr.kind
1757 {
1758 blocks.push(block);
1759 }
1760
1761 (conds, blocks)
1762}
1763
1764pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1766 if let ExprKind::Closure(&Closure {
1767 body,
1768 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1769 ..
1770 }) = expr.kind
1771 && let ExprKind::Block(
1772 Block {
1773 expr:
1774 Some(Expr {
1775 kind: ExprKind::DropTemps(inner_expr),
1776 ..
1777 }),
1778 ..
1779 },
1780 _,
1781 ) = tcx.hir_body(body).value.kind
1782 {
1783 Some(inner_expr)
1784 } else {
1785 None
1786 }
1787}
1788
1789pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1791 get_async_closure_expr(tcx, body.value)
1792}
1793
1794pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1796 let did = match expr.kind {
1797 ExprKind::Call(path, _) => {
1798 if let ExprKind::Path(ref qpath) = path.kind
1799 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1800 {
1801 Some(did)
1802 } else {
1803 None
1804 }
1805 },
1806 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1807 _ => None,
1808 };
1809
1810 did.is_some_and(|did| find_attr!(cx.tcx, did, MustUse { .. }))
1811}
1812
1813fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1825 let [param] = func.params else {
1826 return false;
1827 };
1828
1829 let mut expr = func.value;
1830 loop {
1831 match expr.kind {
1832 ExprKind::Block(
1833 &Block {
1834 stmts: [],
1835 expr: Some(e),
1836 ..
1837 },
1838 _,
1839 )
1840 | ExprKind::Ret(Some(e)) => expr = e,
1841 ExprKind::Block(
1842 &Block {
1843 stmts: [stmt],
1844 expr: None,
1845 ..
1846 },
1847 _,
1848 ) => {
1849 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1850 && let ExprKind::Ret(Some(ret_val)) = e.kind
1851 {
1852 expr = ret_val;
1853 } else {
1854 return false;
1855 }
1856 },
1857 _ => return is_expr_identity_of_pat(cx, param.pat, expr, true),
1858 }
1859 }
1860}
1861
1862pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1872 if cx
1873 .typeck_results()
1874 .pat_binding_modes()
1875 .get(pat.hir_id)
1876 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..)))
1877 {
1878 return false;
1882 }
1883
1884 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1886
1887 match (pat.kind, expr.kind) {
1888 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1889 expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1890 },
1891 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1892 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1893 },
1894 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1895 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1896 {
1897 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1898 },
1899 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1900 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1901 },
1902 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1903 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1904 {
1905 if let ExprKind::Path(ident) = &ident.kind
1907 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1908 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
1910 {
1911 true
1912 } else {
1913 false
1914 }
1915 },
1916 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
1917 if field_pats.len() == fields.len() =>
1918 {
1919 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1921 && unordered_over(field_pats, fields, |field_pat, field| {
1923 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
1924 })
1925 },
1926 _ => false,
1927 }
1928}
1929
1930pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1935 match expr.kind {
1936 ExprKind::Closure(&Closure { body, fn_decl, .. })
1937 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1938 {
1939 is_body_identity_function(cx, cx.tcx.hir_body(body))
1940 },
1941 ExprKind::Path(QPath::Resolved(_, path))
1942 if path.segments.iter().all(|seg| seg.infer_args)
1943 && let Some(did) = path.res.opt_def_id() =>
1944 {
1945 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1946 },
1947 _ => false,
1948 }
1949}
1950
1951pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1960 match expr.kind {
1961 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1962 _ => expr.basic_res().is_diag_item(cx, sym::convert_identity),
1963 }
1964}
1965
1966pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
1969 let mut child_id = expr.hir_id;
1970 let mut iter = tcx.hir_parent_iter(child_id);
1971 loop {
1972 match iter.next() {
1973 None => break None,
1974 Some((id, Node::Block(_))) => child_id = id,
1975 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
1976 Some((_, Node::Expr(expr))) => match expr.kind {
1977 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
1978 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
1979 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
1980 _ => break Some((Node::Expr(expr), child_id)),
1981 },
1982 Some((_, node)) => break Some((node, child_id)),
1983 }
1984 }
1985}
1986
1987pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1989 !matches!(
1990 get_expr_use_or_unification_node(tcx, expr),
1991 None | Some((
1992 Node::Stmt(Stmt {
1993 kind: StmtKind::Expr(_)
1994 | StmtKind::Semi(_)
1995 | StmtKind::Let(LetStmt {
1996 pat: Pat {
1997 kind: PatKind::Wild,
1998 ..
1999 },
2000 ..
2001 }),
2002 ..
2003 }),
2004 _
2005 ))
2006 )
2007}
2008
2009pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2011 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2012}
2013
2014pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2018 !expr.is_place_expr(|base| {
2019 cx.typeck_results()
2020 .adjustments()
2021 .get(base.hir_id)
2022 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2023 })
2024}
2025
2026pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2027 if is_no_core_crate(cx) {
2028 None
2029 } else if is_no_std_crate(cx) {
2030 Some("core")
2031 } else {
2032 Some("std")
2033 }
2034}
2035
2036pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2037 find_attr!(cx.tcx, crate, NoStd(..))
2038}
2039
2040pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2041 find_attr!(cx.tcx, crate, NoCore(..))
2042}
2043
2044pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2054 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2055 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2056 } else {
2057 false
2058 }
2059}
2060
2061pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2071 use rustc_trait_selection::traits;
2072 let predicates = cx
2073 .tcx
2074 .predicates_of(did)
2075 .predicates
2076 .iter()
2077 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2078 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2079}
2080
2081pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2083 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2084}
2085
2086pub fn fn_def_id_with_node_args<'tcx>(
2089 cx: &LateContext<'tcx>,
2090 expr: &Expr<'_>,
2091) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2092 let typeck = cx.typeck_results();
2093 match &expr.kind {
2094 ExprKind::MethodCall(..) => Some((
2095 typeck.type_dependent_def_id(expr.hir_id)?,
2096 typeck.node_args(expr.hir_id),
2097 )),
2098 ExprKind::Call(
2099 Expr {
2100 kind: ExprKind::Path(qpath),
2101 hir_id: path_hir_id,
2102 ..
2103 },
2104 ..,
2105 ) => {
2106 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2109 typeck.qpath_res(qpath, *path_hir_id)
2110 {
2111 Some((id, typeck.node_args(*path_hir_id)))
2112 } else {
2113 None
2114 }
2115 },
2116 _ => None,
2117 }
2118}
2119
2120pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2125 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2126 let expr_kind = expr_type.kind();
2127 let is_primitive = match expr_kind {
2128 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2129 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2130 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2131 is_recursively_primitive_type(*element_type)
2132 } else {
2133 unreachable!()
2134 }
2135 },
2136 _ => false,
2137 };
2138
2139 if is_primitive {
2140 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2143 rustc_ty::Slice(..) => return Some("slice".into()),
2144 rustc_ty::Array(..) => return Some("array".into()),
2145 rustc_ty::Tuple(..) => return Some("tuple".into()),
2146 _ => {
2147 let refs_peeled = expr_type.peel_refs();
2150 return Some(refs_peeled.walk().last().unwrap().to_string());
2151 },
2152 }
2153 }
2154 None
2155}
2156
2157pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2165where
2166 Hash: FnMut(&T) -> u64,
2167 Eq: FnMut(&T, &T) -> bool,
2168{
2169 match exprs {
2170 [a, b] if eq(a, b) => return vec![vec![a, b]],
2171 _ if exprs.len() <= 2 => return vec![],
2172 _ => {},
2173 }
2174
2175 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2176
2177 for expr in exprs {
2178 match buckets.entry(hash(expr)) {
2179 indexmap::map::Entry::Occupied(mut o) => {
2180 let bucket = o.get_mut();
2181 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2182 Some(group) => group.push(expr),
2183 None => bucket.push(vec![expr]),
2184 }
2185 },
2186 indexmap::map::Entry::Vacant(v) => {
2187 v.insert(vec![vec![expr]]);
2188 },
2189 }
2190 }
2191
2192 buckets
2193 .into_values()
2194 .flatten()
2195 .filter(|group| group.len() > 1)
2196 .collect()
2197}
2198
2199pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2202 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2203 if let PatKind::Ref(pat, _, _) = pat.kind {
2204 peel(pat, count + 1)
2205 } else {
2206 (pat, count)
2207 }
2208 }
2209 peel(pat, 0)
2210}
2211
2212pub fn peel_hir_expr_while<'tcx>(
2214 mut expr: &'tcx Expr<'tcx>,
2215 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2216) -> &'tcx Expr<'tcx> {
2217 while let Some(e) = f(expr) {
2218 expr = e;
2219 }
2220 expr
2221}
2222
2223pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2226 let mut remaining = count;
2227 let e = peel_hir_expr_while(expr, |e| match e.kind {
2228 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2229 remaining -= 1;
2230 Some(e)
2231 },
2232 _ => None,
2233 });
2234 (e, count - remaining)
2235}
2236
2237pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2240 let mut count: usize = 0;
2241 let mut curr_expr = expr;
2242 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2243 count = count.wrapping_add(1);
2244 curr_expr = local_expr;
2245 }
2246 (curr_expr, count)
2247}
2248
2249pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2252 let mut count = 0;
2253 let e = peel_hir_expr_while(expr, |e| match e.kind {
2254 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2255 count += 1;
2256 Some(e)
2257 },
2258 _ => None,
2259 });
2260 (e, count)
2261}
2262
2263pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2266 let mut count = 0;
2267 loop {
2268 match &ty.kind {
2269 TyKind::Ref(_, ref_ty) => {
2270 ty = ref_ty.ty;
2271 count += 1;
2272 },
2273 _ => break (ty, count),
2274 }
2275 }
2276}
2277
2278pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
2280 match &ty.kind {
2281 TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty),
2282 _ => ty,
2283 }
2284}
2285
2286pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2289 loop {
2290 match expr.kind {
2291 ExprKind::AddrOf(_, _, e) => expr = e,
2292 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2293 _ => break,
2294 }
2295 }
2296 expr
2297}
2298
2299pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2302 let mut operators = Vec::new();
2303 peel_hir_expr_while(expr, |expr| match expr.kind {
2304 ExprKind::AddrOf(_, _, e) => {
2305 operators.push(expr);
2306 Some(e)
2307 },
2308 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2309 operators.push(expr);
2310 Some(e)
2311 },
2312 _ => None,
2313 });
2314 operators
2315}
2316
2317pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2318 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2319 && let Res::Def(_, def_id) = path.res
2320 {
2321 #[allow(deprecated)]
2322 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2323 }
2324 false
2325}
2326
2327static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2328
2329fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2332 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2333 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2334 let value = map.entry(module);
2335 match value {
2336 Entry::Occupied(entry) => f(entry.get()),
2337 Entry::Vacant(entry) => {
2338 let mut names = Vec::new();
2339 for id in tcx.hir_module_free_items(module) {
2340 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2341 && let item = tcx.hir_item(id)
2342 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2343 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2344 && let Res::Def(DefKind::Struct, _) = path.res
2346 {
2347 if find_attr!(tcx.hir_attrs(item.hir_id()), RustcTestMarker(..)) {
2348 names.push(ident.name);
2349 }
2350 }
2351 }
2352 names.sort_unstable();
2353 f(entry.insert(names))
2354 },
2355 }
2356}
2357
2358pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2362 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2363 let node = tcx.hir_node(id);
2364 once((id, node))
2365 .chain(tcx.hir_parent_iter(id))
2366 .any(|(_id, node)| {
2369 if let Node::Item(item) = node
2370 && let ItemKind::Fn { ident, .. } = item.kind
2371 {
2372 return names.binary_search(&ident.name).is_ok();
2375 }
2376 false
2377 })
2378 })
2379}
2380
2381pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2388 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2389 if let Node::Item(item) = tcx.hir_node(id)
2390 && let ItemKind::Fn { ident, .. } = item.kind
2391 {
2392 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2393 names.binary_search(&ident.name).is_ok()
2394 })
2395 } else {
2396 false
2397 }
2398}
2399
2400pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2405 if let Some(cfgs) = find_attr!(tcx.hir_attrs(id), CfgTrace(cfgs) => cfgs)
2406 && cfgs
2407 .iter()
2408 .any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. }))
2409 {
2410 true
2411 } else {
2412 false
2413 }
2414}
2415
2416pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2418 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2419}
2420
2421pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2423 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2424}
2425
2426pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2428 find_attr!(tcx, def_id, CfgTrace(..))
2429 || find_attr!(
2430 tcx.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2431 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)),
2432 CfgTrace(..)
2433 )
2434}
2435
2436pub fn walk_to_expr_usage<'tcx, T>(
2447 cx: &LateContext<'tcx>,
2448 e: &Expr<'tcx>,
2449 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2450) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2451 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2452 let mut child_id = e.hir_id;
2453
2454 while let Some((parent_id, parent)) = iter.next() {
2455 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2456 return Some(ControlFlow::Break(x));
2457 }
2458 let parent_expr = match parent {
2459 Node::Expr(e) => e,
2460 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2461 child_id = parent_id;
2462 continue;
2463 },
2464 Node::Arm(a) if a.body.hir_id == child_id => {
2465 child_id = parent_id;
2466 continue;
2467 },
2468 _ => return Some(ControlFlow::Continue((parent, child_id))),
2469 };
2470 match parent_expr.kind {
2471 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2472 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2473 child_id = id;
2474 iter = cx.tcx.hir_parent_iter(id);
2475 },
2476 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2477 _ => return Some(ControlFlow::Continue((parent, child_id))),
2478 }
2479 }
2480 debug_assert!(false, "no parent node found for `{child_id:?}`");
2481 None
2482}
2483
2484#[derive(Clone, Copy)]
2486pub enum DefinedTy<'tcx> {
2487 Hir(&'tcx hir::Ty<'tcx>),
2489 Mir {
2497 def_site_def_id: Option<DefId>,
2498 ty: Binder<'tcx, Ty<'tcx>>,
2499 },
2500}
2501
2502pub struct ExprUseCtxt<'tcx> {
2504 pub node: Node<'tcx>,
2506 pub child_id: HirId,
2508 pub adjustments: &'tcx [Adjustment<'tcx>],
2510 pub is_ty_unified: bool,
2512 pub moved_before_use: bool,
2514 pub same_ctxt: bool,
2516}
2517impl<'tcx> ExprUseCtxt<'tcx> {
2518 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2519 match self.node {
2520 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2521 Node::ExprField(field) => ExprUseNode::Field(field),
2522
2523 Node::Item(&Item {
2524 kind: ItemKind::Static(..) | ItemKind::Const(..),
2525 owner_id,
2526 ..
2527 })
2528 | Node::TraitItem(&TraitItem {
2529 kind: TraitItemKind::Const(..),
2530 owner_id,
2531 ..
2532 })
2533 | Node::ImplItem(&ImplItem {
2534 kind: ImplItemKind::Const(..),
2535 owner_id,
2536 ..
2537 }) => ExprUseNode::ConstStatic(owner_id),
2538
2539 Node::Item(&Item {
2540 kind: ItemKind::Fn { .. },
2541 owner_id,
2542 ..
2543 })
2544 | Node::TraitItem(&TraitItem {
2545 kind: TraitItemKind::Fn(..),
2546 owner_id,
2547 ..
2548 })
2549 | Node::ImplItem(&ImplItem {
2550 kind: ImplItemKind::Fn(..),
2551 owner_id,
2552 ..
2553 }) => ExprUseNode::Return(owner_id),
2554
2555 Node::Expr(use_expr) => match use_expr.kind {
2556 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2557 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2558 }),
2559
2560 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2561 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2562 Some(i) => ExprUseNode::FnArg(func, i),
2563 None => ExprUseNode::Callee,
2564 },
2565 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2566 use_expr.hir_id,
2567 name.args,
2568 args.iter()
2569 .position(|arg| arg.hir_id == self.child_id)
2570 .map_or(0, |i| i + 1),
2571 ),
2572 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2573 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2574 _ => ExprUseNode::Other,
2575 },
2576 _ => ExprUseNode::Other,
2577 }
2578 }
2579}
2580
2581pub enum ExprUseNode<'tcx> {
2583 LetStmt(&'tcx LetStmt<'tcx>),
2585 ConstStatic(OwnerId),
2587 Return(OwnerId),
2589 Field(&'tcx ExprField<'tcx>),
2591 FnArg(&'tcx Expr<'tcx>, usize),
2593 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2595 Callee,
2597 FieldAccess(Ident),
2599 AddrOf(ast::BorrowKind, Mutability),
2601 Other,
2602}
2603impl<'tcx> ExprUseNode<'tcx> {
2604 pub fn is_return(&self) -> bool {
2606 matches!(self, Self::Return(_))
2607 }
2608
2609 pub fn is_recv(&self) -> bool {
2611 matches!(self, Self::MethodArg(_, _, 0))
2612 }
2613
2614 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2616 match *self {
2617 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2618 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2619 def_site_def_id: Some(id.def_id.to_def_id()),
2620 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2621 }),
2622 Self::Return(id) => {
2623 if let Node::Expr(Expr {
2624 kind: ExprKind::Closure(c),
2625 ..
2626 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2627 {
2628 match c.fn_decl.output {
2629 FnRetTy::DefaultReturn(_) => None,
2630 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2631 }
2632 } else {
2633 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2634 Some(DefinedTy::Mir {
2635 def_site_def_id: Some(id.def_id.to_def_id()),
2636 ty,
2637 })
2638 }
2639 },
2640 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2641 Some(Expr {
2642 hir_id,
2643 kind: ExprKind::Struct(path, ..),
2644 ..
2645 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2646 .and_then(|(adt, variant)| {
2647 variant
2648 .fields
2649 .iter()
2650 .find(|f| f.name == field.ident.name)
2651 .map(|f| (adt, f))
2652 })
2653 .map(|(adt, field_def)| DefinedTy::Mir {
2654 def_site_def_id: Some(adt.did()),
2655 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2656 }),
2657 _ => None,
2658 },
2659 Self::FnArg(callee, i) => {
2660 let sig = expr_sig(cx, callee)?;
2661 let (hir_ty, ty) = sig.input_with_hir(i)?;
2662 Some(match hir_ty {
2663 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2664 None => DefinedTy::Mir {
2665 def_site_def_id: sig.predicates_id(),
2666 ty,
2667 },
2668 })
2669 },
2670 Self::MethodArg(id, _, i) => {
2671 let id = cx.typeck_results().type_dependent_def_id(id)?;
2672 let sig = cx.tcx.fn_sig(id).skip_binder();
2673 Some(DefinedTy::Mir {
2674 def_site_def_id: Some(id),
2675 ty: sig.input(i),
2676 })
2677 },
2678 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2679 }
2680 }
2681}
2682
2683pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2685 let mut adjustments = [].as_slice();
2686 let mut is_ty_unified = false;
2687 let mut moved_before_use = false;
2688 let mut same_ctxt = true;
2689 let ctxt = e.span.ctxt();
2690 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2691 if adjustments.is_empty()
2692 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2693 {
2694 adjustments = cx.typeck_results().expr_adjustments(e);
2695 }
2696 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2697 if let Node::Expr(e) = parent {
2698 match e.kind {
2699 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2700 is_ty_unified = true;
2701 moved_before_use = true;
2702 },
2703 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2704 is_ty_unified = true;
2705 moved_before_use = true;
2706 },
2707 ExprKind::Block(..) => moved_before_use = true,
2708 _ => {},
2709 }
2710 }
2711 ControlFlow::Continue(())
2712 });
2713 match node {
2714 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2715 node,
2716 child_id,
2717 adjustments,
2718 is_ty_unified,
2719 moved_before_use,
2720 same_ctxt,
2721 },
2722 None => ExprUseCtxt {
2723 node: Node::Crate(cx.tcx.hir_root_module()),
2724 child_id: HirId::INVALID,
2725 adjustments: &[],
2726 is_ty_unified: true,
2727 moved_before_use: true,
2728 same_ctxt: false,
2729 },
2730 }
2731}
2732
2733pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2735 let mut pos = 0;
2736 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2737 let end = pos + t.len;
2738 let range = pos as usize..end as usize;
2739 let inner = InnerSpan::new(range.start, range.end);
2740 pos = end;
2741 (t.kind, s.get(range).unwrap_or_default(), inner)
2742 })
2743}
2744
2745pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2748 let Ok(snippet) = sm.span_to_snippet(span) else {
2749 return false;
2750 };
2751 return tokenize(&snippet, FrontmatterAllowed::No).any(|token| {
2752 matches!(
2753 token.kind,
2754 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2755 )
2756 });
2757}
2758
2759pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2764 matches!(span.get_source_text(cx), Some(snippet) if tokenize_with_text(&snippet).any(|(token, _, _)|
2765 match token {
2766 TokenKind::Whitespace => false,
2767 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2768 _ => true,
2769 }
2770 ))
2771}
2772pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2776 span_extract_comments(sm, span).join("\n")
2777}
2778
2779pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2783 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2784 tokenize_with_text(&snippet)
2785 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2786 .map(|(_, s, _)| s.to_string())
2787 .collect::<Vec<_>>()
2788}
2789
2790pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2791 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2792}
2793
2794pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2819 cx: &LateContext<'_>,
2820 pat: &'a Pat<'hir>,
2821 else_body: &Expr<'_>,
2822) -> Option<&'a Pat<'hir>> {
2823 if let Some([inner_pat]) = as_some_pattern(cx, pat)
2824 && !is_refutable(cx, inner_pat)
2825 && let else_body = peel_blocks(else_body)
2826 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2827 && let ExprKind::Path(ret_path) = ret_val.kind
2828 && cx
2829 .qpath_res(&ret_path, ret_val.hir_id)
2830 .ctor_parent(cx)
2831 .is_lang_item(cx, OptionNone)
2832 {
2833 Some(inner_pat)
2834 } else {
2835 None
2836 }
2837}
2838
2839macro_rules! op_utils {
2840 ($($name:ident $assign:ident)*) => {
2841 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2843
2844 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2846
2847 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2849 match kind {
2850 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2851 _ => None,
2852 }
2853 }
2854 };
2855}
2856
2857op_utils! {
2858 Add AddAssign
2859 Sub SubAssign
2860 Mul MulAssign
2861 Div DivAssign
2862 Rem RemAssign
2863 BitXor BitXorAssign
2864 BitAnd BitAndAssign
2865 BitOr BitOrAssign
2866 Shl ShlAssign
2867 Shr ShrAssign
2868}
2869
2870pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2873 match *pat {
2874 PatKind::Wild => true,
2875 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2876 !visitors::is_local_used(cx, body, id)
2877 },
2878 _ => false,
2879 }
2880}
2881
2882#[derive(Clone, Copy)]
2883pub enum RequiresSemi {
2884 Yes,
2885 No,
2886}
2887impl RequiresSemi {
2888 pub fn requires_semi(self) -> bool {
2889 matches!(self, Self::Yes)
2890 }
2891}
2892
2893#[expect(clippy::too_many_lines)]
2896pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2897 struct BreakTarget {
2898 id: HirId,
2899 unused: bool,
2900 }
2901
2902 struct V<'cx, 'tcx> {
2903 cx: &'cx LateContext<'tcx>,
2904 break_targets: Vec<BreakTarget>,
2905 break_targets_for_result_ty: u32,
2906 in_final_expr: bool,
2907 requires_semi: bool,
2908 is_never: bool,
2909 }
2910
2911 impl V<'_, '_> {
2912 fn push_break_target(&mut self, id: HirId) {
2913 self.break_targets.push(BreakTarget { id, unused: true });
2914 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2915 }
2916 }
2917
2918 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
2919 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
2920 if self.is_never && self.break_targets.is_empty() {
2937 if self.in_final_expr && !self.requires_semi {
2938 match e.kind {
2941 ExprKind::DropTemps(e) => self.visit_expr(e),
2942 ExprKind::If(_, then, Some(else_)) => {
2943 self.visit_expr(then);
2944 self.visit_expr(else_);
2945 },
2946 ExprKind::Match(_, arms, _) => {
2947 for arm in arms {
2948 self.visit_expr(arm.body);
2949 }
2950 },
2951 ExprKind::Loop(b, ..) => {
2952 self.push_break_target(e.hir_id);
2953 self.in_final_expr = false;
2954 self.visit_block(b);
2955 self.break_targets.pop();
2956 },
2957 ExprKind::Block(b, _) => {
2958 if b.targeted_by_break {
2959 self.push_break_target(b.hir_id);
2960 self.visit_block(b);
2961 self.break_targets.pop();
2962 } else {
2963 self.visit_block(b);
2964 }
2965 },
2966 _ => {
2967 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
2968 },
2969 }
2970 }
2971 return;
2972 }
2973 match e.kind {
2974 ExprKind::DropTemps(e) => self.visit_expr(e),
2975 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
2976 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
2977 self.in_final_expr = false;
2978 self.visit_expr(e);
2979 self.is_never = true;
2980 },
2981 ExprKind::Break(dest, e) => {
2982 if let Some(e) = e {
2983 self.in_final_expr = false;
2984 self.visit_expr(e);
2985 }
2986 if let Ok(id) = dest.target_id
2987 && let Some((i, target)) = self
2988 .break_targets
2989 .iter_mut()
2990 .enumerate()
2991 .find(|(_, target)| target.id == id)
2992 {
2993 target.unused &= self.is_never;
2994 if i < self.break_targets_for_result_ty as usize {
2995 self.requires_semi = true;
2996 }
2997 }
2998 self.is_never = true;
2999 },
3000 ExprKind::If(cond, then, else_) => {
3001 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3002 self.visit_expr(cond);
3003 self.in_final_expr = in_final_expr;
3004
3005 if self.is_never {
3006 self.visit_expr(then);
3007 if let Some(else_) = else_ {
3008 self.visit_expr(else_);
3009 }
3010 } else {
3011 self.visit_expr(then);
3012 let is_never = mem::replace(&mut self.is_never, false);
3013 if let Some(else_) = else_ {
3014 self.visit_expr(else_);
3015 self.is_never &= is_never;
3016 }
3017 }
3018 },
3019 ExprKind::Match(scrutinee, arms, _) => {
3020 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3021 self.visit_expr(scrutinee);
3022 self.in_final_expr = in_final_expr;
3023
3024 if self.is_never {
3025 for arm in arms {
3026 self.visit_arm(arm);
3027 }
3028 } else {
3029 let mut is_never = true;
3030 for arm in arms {
3031 self.is_never = false;
3032 if let Some(guard) = arm.guard {
3033 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3034 self.visit_expr(guard);
3035 self.in_final_expr = in_final_expr;
3036 self.is_never = false;
3038 }
3039 self.visit_expr(arm.body);
3040 is_never &= self.is_never;
3041 }
3042 self.is_never = is_never;
3043 }
3044 },
3045 ExprKind::Loop(b, _, _, _) => {
3046 self.push_break_target(e.hir_id);
3047 self.in_final_expr = false;
3048 self.visit_block(b);
3049 self.is_never = self.break_targets.pop().unwrap().unused;
3050 },
3051 ExprKind::Block(b, _) => {
3052 if b.targeted_by_break {
3053 self.push_break_target(b.hir_id);
3054 self.visit_block(b);
3055 self.is_never &= self.break_targets.pop().unwrap().unused;
3056 } else {
3057 self.visit_block(b);
3058 }
3059 },
3060 _ => {
3061 self.in_final_expr = false;
3062 walk_expr(self, e);
3063 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3064 },
3065 }
3066 }
3067
3068 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3069 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3070 for s in b.stmts {
3071 self.visit_stmt(s);
3072 }
3073 self.in_final_expr = in_final_expr;
3074 if let Some(e) = b.expr {
3075 self.visit_expr(e);
3076 }
3077 }
3078
3079 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3080 if let Some(e) = l.init {
3081 self.visit_expr(e);
3082 }
3083 if let Some(else_) = l.els {
3084 let is_never = self.is_never;
3085 self.visit_block(else_);
3086 self.is_never = is_never;
3087 }
3088 }
3089
3090 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3091 if let Some(guard) = arm.guard {
3092 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3093 self.visit_expr(guard);
3094 self.in_final_expr = in_final_expr;
3095 }
3096 self.visit_expr(arm.body);
3097 }
3098 }
3099
3100 if cx.typeck_results().expr_ty(e).is_never() {
3101 Some(RequiresSemi::No)
3102 } else if let ExprKind::Block(b, _) = e.kind
3103 && !b.targeted_by_break
3104 && b.expr.is_none()
3105 {
3106 None
3108 } else {
3109 let mut v = V {
3110 cx,
3111 break_targets: Vec::new(),
3112 break_targets_for_result_ty: 0,
3113 in_final_expr: true,
3114 requires_semi: false,
3115 is_never: false,
3116 };
3117 v.visit_expr(e);
3118 v.is_never
3119 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3120 RequiresSemi::Yes
3121 } else {
3122 RequiresSemi::No
3123 })
3124 }
3125}
3126
3127pub fn get_path_from_caller_to_method_type<'tcx>(
3133 tcx: TyCtxt<'tcx>,
3134 from: LocalDefId,
3135 method: DefId,
3136 args: GenericArgsRef<'tcx>,
3137) -> String {
3138 let assoc_item = tcx.associated_item(method);
3139 let def_id = assoc_item.container_id(tcx);
3140 match assoc_item.container {
3141 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3142 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3143 let ty = tcx.type_of(def_id).instantiate_identity();
3144 get_path_to_ty(tcx, from, ty, args)
3145 },
3146 }
3147}
3148
3149fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3150 match ty.kind() {
3151 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3152 rustc_ty::Array(..)
3154 | rustc_ty::Dynamic(..)
3155 | rustc_ty::Never
3156 | rustc_ty::RawPtr(_, _)
3157 | rustc_ty::Ref(..)
3158 | rustc_ty::Slice(_)
3159 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3160 _ => ty.to_string(),
3161 }
3162}
3163
3164fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3166 if callee.is_local() {
3168 let callee_path = tcx.def_path(callee);
3169 let caller_path = tcx.def_path(from.to_def_id());
3170 maybe_get_relative_path(&caller_path, &callee_path, 2)
3171 } else {
3172 tcx.def_path_str(callee)
3173 }
3174}
3175
3176fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3189 use itertools::EitherOrBoth::{Both, Left, Right};
3190
3191 let unique_parts = to
3193 .data
3194 .iter()
3195 .zip_longest(from.data.iter())
3196 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3197 .map(|el| match el {
3198 Both(l, r) => Both(l.data, r.data),
3199 Left(l) => Left(l.data),
3200 Right(r) => Right(r.data),
3201 });
3202
3203 let mut go_up_by = 0;
3205 let mut path = Vec::new();
3206 for el in unique_parts {
3207 match el {
3208 Both(l, r) => {
3209 if let DefPathData::TypeNs(sym) = l {
3219 path.push(sym);
3220 }
3221 if let DefPathData::TypeNs(_) = r {
3222 go_up_by += 1;
3223 }
3224 },
3225 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3230 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3235 _ => {},
3236 }
3237 }
3238
3239 if go_up_by > max_super {
3240 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3242 if let DefPathData::TypeNs(sym) = el.data {
3243 Some(sym)
3244 } else {
3245 None
3246 }
3247 })))
3248 } else {
3249 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3250 }
3251}
3252
3253pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3256 matches!(
3257 cx.tcx.parent_hir_node(id),
3258 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3259 )
3260}
3261
3262pub fn is_block_like(expr: &Expr<'_>) -> bool {
3265 matches!(
3266 expr.kind,
3267 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3268 )
3269}
3270
3271pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3273 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3274 match expr.kind {
3275 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3276 _ if is_block_like(expr) => is_operand,
3277 _ => false,
3278 }
3279 }
3280
3281 contains_block(expr, false)
3282}
3283
3284pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3286 if let Some(parent_expr) = get_parent_expr(cx, expr)
3287 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3288 && receiver.hir_id == expr.hir_id
3289 {
3290 return true;
3291 }
3292 false
3293}
3294
3295pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3298 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3299 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3300 && temporary_ty
3301 .walk()
3302 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3303 {
3304 ControlFlow::Break(())
3305 } else {
3306 ControlFlow::Continue(())
3307 }
3308 })
3309 .is_break()
3310}
3311
3312pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3323 let expr_ty_is_adjusted = cx
3324 .typeck_results()
3325 .expr_adjustments(expr)
3326 .iter()
3327 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3329 if expr_ty_is_adjusted {
3330 return true;
3331 }
3332
3333 match expr.kind {
3336 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3337 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3338
3339 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3340 return false;
3341 }
3342
3343 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3344 let mut args_with_ty_param = {
3345 fn_sig
3346 .inputs()
3347 .skip_binder()
3348 .iter()
3349 .skip(self_arg_count)
3350 .zip(args)
3351 .filter_map(|(arg_ty, arg)| {
3352 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3353 Some(arg)
3354 } else {
3355 None
3356 }
3357 })
3358 };
3359 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3360 },
3361 ExprKind::Struct(qpath, _, _) => {
3363 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3364 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3365 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3366 return true;
3368 };
3369 v_def
3370 .fields
3371 .iter()
3372 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3373 } else {
3374 false
3375 }
3376 },
3377 ExprKind::Block(
3379 &Block {
3380 expr: Some(ret_expr), ..
3381 },
3382 _,
3383 )
3384 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3385
3386 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3388 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3390 ExprKind::If(_, then, maybe_else) => {
3392 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3393 },
3394 ExprKind::Match(_, arms, _) => arms
3395 .iter()
3396 .map(|arm| arm.body)
3397 .any(|body| expr_requires_coercion(cx, body)),
3398 _ => false,
3399 }
3400}
3401
3402pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3405 if let Some(hir_id) = expr.res_local_id()
3406 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3407 {
3408 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3409 } else if let ExprKind::Path(p) = &expr.kind
3410 && let Some(mutability) = cx
3411 .qpath_res(p, expr.hir_id)
3412 .opt_def_id()
3413 .and_then(|id| cx.tcx.static_mutability(id))
3414 {
3415 mutability == Mutability::Mut
3416 } else if let ExprKind::Field(parent, _) = expr.kind {
3417 is_mutable(cx, parent)
3418 } else {
3419 true
3420 }
3421}
3422
3423pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3426 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3427 return hir_ty;
3428 };
3429 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3430 && let Some(segment) = path.segments.last()
3431 && segment.ident.name == sym::Option
3432 && let Res::Def(DefKind::Enum, def_id) = segment.res
3433 && def_id == option_def_id
3434 && let [GenericArg::Type(arg_ty)] = segment.args().args
3435 {
3436 hir_ty = arg_ty.as_unambig_ty();
3437 }
3438 hir_ty
3439}
3440
3441pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3444 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3445 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3446 && let ctxt = expr.span.ctxt()
3447 && for_each_expr_without_closures(into_future_arg, |e| {
3448 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3449 })
3450 .is_none()
3451 {
3452 Some(into_future_arg)
3453 } else {
3454 None
3455 }
3456}
3457
3458pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3460 if let ExprKind::Call(fn_expr, []) = &expr.kind
3461 && let ExprKind::Path(qpath) = &fn_expr.kind
3462 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3463 {
3464 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3465 } else {
3466 false
3467 }
3468}
3469
3470pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3487 let enclosing_body_owner = cx
3488 .tcx
3489 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3490 let mut prev_id = expr.hir_id;
3491 let mut skip_until_id = None;
3492 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3493 if hir_id == enclosing_body_owner {
3494 return true;
3495 }
3496 if let Some(id) = skip_until_id {
3497 prev_id = hir_id;
3498 if id == hir_id {
3499 skip_until_id = None;
3500 }
3501 continue;
3502 }
3503 match node {
3504 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3505 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3506 Node::Expr(expr) => match expr.kind {
3507 ExprKind::Ret(_) => return true,
3508 ExprKind::If(_, then, opt_else)
3509 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3510 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3511 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3512 ExprKind::Break(
3513 Destination {
3514 target_id: Ok(target_id),
3515 ..
3516 },
3517 _,
3518 ) => skip_until_id = Some(target_id),
3519 _ => break,
3520 },
3521 _ => break,
3522 }
3523 prev_id = hir_id;
3524 }
3525
3526 false
3529}
3530
3531pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3534 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3535 matches!(
3536 adj.kind,
3537 Adjust::Deref(DerefAdjustKind::Overloaded(_))
3538 | Adjust::Pointer(PointerCoercion::Unsize)
3539 | Adjust::NeverToAny
3540 )
3541 })
3542}
3543
3544pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3546 matches!(
3547 expr.kind,
3548 ExprKind::Closure(Closure {
3549 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3550 CoroutineDesugaring::Async,
3551 CoroutineSource::Block
3552 )),
3553 ..
3554 })
3555 )
3556}
3557
3558pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3560 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3561}