1#![feature(box_patterns)]
2#![feature(if_let_guard)]
3#![feature(macro_metavar_expr)]
4#![feature(never_type)]
5#![feature(rustc_private)]
6#![feature(assert_matches)]
7#![feature(unwrap_infallible)]
8#![cfg_attr(bootstrap, feature(array_windows))]
9#![recursion_limit = "512"]
10#![allow(
11 clippy::missing_errors_doc,
12 clippy::missing_panics_doc,
13 clippy::must_use_candidate,
14 rustc::diagnostic_outside_of_impl,
15 rustc::untranslatable_diagnostic
16)]
17#![warn(
18 trivial_casts,
19 trivial_numeric_casts,
20 rust_2018_idioms,
21 unused_lifetimes,
22 unused_qualifications,
23 rustc::internal
24)]
25
26extern crate rustc_abi;
29extern crate rustc_ast;
30extern crate rustc_attr_parsing;
31extern crate rustc_const_eval;
32extern crate rustc_data_structures;
33#[expect(
34 unused_extern_crates,
35 reason = "The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate."
36)]
37extern crate rustc_driver;
38extern crate rustc_errors;
39extern crate rustc_hir;
40extern crate rustc_hir_analysis;
41extern crate rustc_hir_typeck;
42extern crate rustc_index;
43extern crate rustc_infer;
44extern crate rustc_lexer;
45extern crate rustc_lint;
46extern crate rustc_middle;
47extern crate rustc_mir_dataflow;
48extern crate rustc_session;
49extern crate rustc_span;
50extern crate rustc_trait_selection;
51
52pub mod ast_utils;
53#[deny(missing_docs)]
54pub mod attrs;
55mod check_proc_macro;
56pub mod comparisons;
57pub mod consts;
58pub mod diagnostics;
59pub mod eager_or_lazy;
60pub mod higher;
61mod hir_utils;
62pub mod macros;
63pub mod mir;
64pub mod msrvs;
65pub mod numeric_literal;
66pub mod paths;
67pub mod qualify_min_const_fn;
68pub mod res;
69pub mod source;
70pub mod str_utils;
71pub mod sugg;
72pub mod sym;
73pub mod ty;
74pub mod usage;
75pub mod visitors;
76
77pub use self::attrs::*;
78pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
79pub use self::hir_utils::{
80 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
81 hash_stmt, is_bool, over,
82};
83
84use core::mem;
85use core::ops::ControlFlow;
86use std::collections::hash_map::Entry;
87use std::iter::{once, repeat_n, zip};
88use std::sync::{Mutex, MutexGuard, OnceLock};
89
90use itertools::Itertools;
91use rustc_abi::Integer;
92use rustc_ast::ast::{self, LitKind, RangeLimits};
93use rustc_ast::join_path_syms;
94use rustc_data_structures::fx::FxHashMap;
95use rustc_data_structures::indexmap;
96use rustc_data_structures::packed::Pu128;
97use rustc_data_structures::unhash::UnindexMap;
98use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
99use rustc_hir::attrs::AttributeKind;
100use rustc_hir::def::{DefKind, Res};
101use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
102use rustc_hir::definitions::{DefPath, DefPathData};
103use rustc_hir::hir_id::{HirIdMap, HirIdSet};
104use rustc_hir::intravisit::{Visitor, walk_expr};
105use rustc_hir::{
106 self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
107 CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs,
108 HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId,
109 OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
110 TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr,
111};
112use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
113use rustc_lint::{LateContext, Level, Lint, LintContext};
114use rustc_middle::hir::nested_filter;
115use rustc_middle::hir::place::PlaceBase;
116use rustc_middle::lint::LevelAndSource;
117use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
118use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, PointerCoercion};
119use rustc_middle::ty::layout::IntegerExt;
120use rustc_middle::ty::{
121 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
122 TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
123};
124use rustc_span::hygiene::{ExpnKind, MacroKind};
125use rustc_span::source_map::SourceMap;
126use rustc_span::symbol::{Ident, Symbol, kw};
127use rustc_span::{InnerSpan, Span};
128use source::{SpanRangeExt, walk_span_to_context};
129use visitors::{Visitable, for_each_unconsumed_temporary};
130
131use crate::ast_utils::unordered_over;
132use crate::consts::{ConstEvalCtxt, Constant};
133use crate::higher::Range;
134use crate::msrvs::Msrv;
135use crate::res::{MaybeDef, MaybeQPath, MaybeResPath};
136use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
137use crate::visitors::for_each_expr_without_closures;
138
139pub const VEC_METHODS_SHADOWING_SLICE_METHODS: [Symbol; 3] = [sym::as_ptr, sym::is_empty, sym::len];
141
142#[macro_export]
143macro_rules! extract_msrv_attr {
144 () => {
145 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
146 let sess = rustc_lint::LintContext::sess(cx);
147 self.msrv.check_attributes(sess, attrs);
148 }
149
150 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
151 let sess = rustc_lint::LintContext::sess(cx);
152 self.msrv.check_attributes_post(sess, attrs);
153 }
154 };
155}
156
157pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
180 while let Some(init) = expr
181 .res_local_id()
182 .and_then(|id| find_binding_init(cx, id))
183 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
184 {
185 expr = init;
186 }
187 expr
188}
189
190pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
199 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
200 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
201 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
202 {
203 return local.init;
204 }
205 None
206}
207
208pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
212 for (_, node) in cx.tcx.hir_parent_iter(local) {
213 match node {
214 Node::Pat(..) | Node::PatField(..) => {},
215 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
216 _ => return true,
217 }
218 }
219
220 false
221}
222
223pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
234 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
235 cx.enclosing_body.is_some_and(|id| {
236 cx.tcx
237 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
238 .is_some()
239 })
240}
241
242pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
249 use rustc_hir::ConstContext::{Const, ConstFn, Static};
250 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
251 return false;
252 };
253 match ctx {
254 ConstFn => false,
255 Static(_) | Const { inline: _ } => true,
256 }
257}
258
259pub fn is_enum_variant_ctor(
261 cx: &LateContext<'_>,
262 enum_item: Symbol,
263 variant_name: Symbol,
264 ctor_call_id: DefId,
265) -> bool {
266 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
267 return false;
268 };
269
270 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
271 variants
272 .filter(|variant| variant.name == variant_name)
273 .filter_map(|variant| variant.ctor.as_ref())
274 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
275}
276
277pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
279 let did = match cx.tcx.def_kind(did) {
280 DefKind::Ctor(..) => cx.tcx.parent(did),
281 DefKind::Variant => match cx.tcx.opt_parent(did) {
283 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
284 _ => did,
285 },
286 _ => did,
287 };
288
289 cx.tcx.is_diagnostic_item(item, did)
290}
291
292pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
294 let did = match cx.tcx.def_kind(did) {
295 DefKind::Ctor(..) => cx.tcx.parent(did),
296 DefKind::Variant => match cx.tcx.opt_parent(did) {
298 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
299 _ => did,
300 },
301 _ => did,
302 };
303
304 cx.tcx.lang_items().get(item) == Some(did)
305}
306
307pub fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
309 expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
310}
311
312pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
314 if let ExprKind::Call(e, [arg]) = expr.kind
315 && e.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
316 {
317 Some(arg)
318 } else {
319 None
320 }
321}
322
323pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
325 matches!(
326 expr.kind,
327 ExprKind::Block(
328 Block {
329 stmts: [],
330 expr: None,
331 ..
332 },
333 _
334 ) | ExprKind::Tup([])
335 )
336}
337
338pub fn is_wild(pat: &Pat<'_>) -> bool {
340 matches!(pat.kind, PatKind::Wild)
341}
342
343pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> {
350 if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind
351 && cx
352 .qpath_res(qpath, pat.hir_id)
353 .ctor_parent(cx)
354 .is_lang_item(cx, OptionSome)
355 {
356 Some(inner)
357 } else {
358 None
359 }
360}
361
362pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
364 matches!(pat.kind,
365 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
366 if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone))
367}
368
369pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
371 is_none_pattern(cx, arm.pat)
372 && matches!(
373 peel_blocks(arm.body).kind,
374 ExprKind::Path(qpath)
375 if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)
376 )
377}
378
379pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
381 match *qpath {
382 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
383 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
384 QPath::TypeRelative(..) => false,
385 }
386}
387
388pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
390 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
391 && let ItemKind::Impl(imp) = item.kind
392 {
393 imp.of_trait.is_some()
394 } else {
395 false
396 }
397}
398
399pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
400 match *path {
401 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
402 QPath::TypeRelative(_, seg) => seg,
403 }
404}
405
406pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
407 last_path_segment(qpath)
408 .args
409 .map_or(&[][..], |a| a.args)
410 .iter()
411 .filter_map(|a| match a {
412 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
413 _ => None,
414 })
415}
416
417pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
422 match expr.kind {
423 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
424 ExprKind::Path(QPath::Resolved(
425 _,
426 Path {
427 res: Res::Local(local), ..
428 },
429 )) => Some(*local),
430 _ => None,
431 }
432}
433
434pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
450 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
451 && let ItemKind::Impl(impl_) = &item.kind
452 && let Some(of_trait) = impl_.of_trait
453 {
454 return Some(&of_trait.trait_ref);
455 }
456 None
457}
458
459fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
467 let mut result = vec![];
468 let root = loop {
469 match e.kind {
470 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
471 result.push(e);
472 e = ep;
473 },
474 _ => break e,
475 }
476 };
477 result.reverse();
478 (result, root)
479}
480
481pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
483 cx.typeck_results()
484 .expr_adjustments(e)
485 .iter()
486 .find_map(|a| match a.kind {
487 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
488 Adjust::Deref(None) => None,
489 _ => Some(None),
490 })
491 .and_then(|x| x)
492}
493
494pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
497 let (s1, r1) = projection_stack(e1);
498 let (s2, r2) = projection_stack(e2);
499 if !eq_expr_value(cx, r1, r2) {
500 return true;
501 }
502 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
503 return false;
504 }
505
506 for (x1, x2) in zip(&s1, &s2) {
507 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
508 return false;
509 }
510
511 match (&x1.kind, &x2.kind) {
512 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
513 if i1 != i2 {
514 return true;
515 }
516 },
517 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
518 if !eq_expr_value(cx, i1, i2) {
519 return false;
520 }
521 },
522 _ => return false,
523 }
524 }
525 false
526}
527
528fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
531 let std_types_symbols = &[
532 sym::Vec,
533 sym::VecDeque,
534 sym::LinkedList,
535 sym::HashMap,
536 sym::BTreeMap,
537 sym::HashSet,
538 sym::BTreeSet,
539 sym::BinaryHeap,
540 ];
541
542 if let QPath::TypeRelative(_, method) = path
543 && method.ident.name == sym::new
544 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
545 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
546 {
547 return Some(adt.did()) == cx.tcx.lang_items().string()
548 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
549 }
550 false
551}
552
553pub fn is_default_equivalent_call(
555 cx: &LateContext<'_>,
556 repl_func: &Expr<'_>,
557 whole_call_expr: Option<&Expr<'_>>,
558) -> bool {
559 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
560 && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx)
561 && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default)
562 || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath))
563 {
564 return true;
565 }
566
567 let Some(e) = whole_call_expr else { return false };
570 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
571 return false;
572 };
573 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
574 return false;
575 };
576 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
577 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
578 cx.tcx.lifetimes.re_erased.into()
579 } else if param.index == 0 && param.name == kw::SelfUpper {
580 ty.into()
581 } else {
582 param.to_error(cx.tcx)
583 }
584 });
585 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
586
587 let Ok(Some(instance)) = instance else { return false };
588 if let rustc_ty::InstanceKind::Item(def) = instance.def
589 && !cx.tcx.is_mir_available(def)
590 {
591 return false;
592 }
593 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
594 return false;
595 };
596 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
597 return false;
598 };
599
600 let body = cx.tcx.instance_mir(instance.def);
606 for block_data in body.basic_blocks.iter() {
607 if block_data.statements.len() == 1
608 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
609 && assign.0.local == RETURN_PLACE
610 && let Rvalue::Aggregate(kind, _places) = &assign.1
611 && let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
612 && let def = cx.tcx.adt_def(did)
613 && let variant = &def.variant(*variant_index)
614 && variant.fields.is_empty()
615 && let Some((_, did)) = variant.ctor
616 && did == repl_def_id
617 {
618 return true;
619 } else if block_data.statements.is_empty()
620 && let Some(term) = &block_data.terminator
621 {
622 match &term.kind {
623 TerminatorKind::Call {
624 func: Operand::Constant(c),
625 ..
626 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
627 && *did == repl_def_id =>
628 {
629 return true;
630 },
631 TerminatorKind::TailCall {
632 func: Operand::Constant(c),
633 ..
634 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
635 && *did == repl_def_id =>
636 {
637 return true;
638 },
639 _ => {},
640 }
641 }
642 }
643 false
644}
645
646pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
650 match &e.kind {
651 ExprKind::Lit(lit) => match lit.node {
652 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
653 LitKind::Str(s, _) => s.is_empty(),
654 _ => false,
655 },
656 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
657 ExprKind::Repeat(x, len) => {
658 if let ConstArgKind::Anon(anon_const) = len.kind
659 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
660 && let LitKind::Int(v, _) = const_lit.node
661 && v <= 32
662 && is_default_equivalent(cx, x)
663 {
664 true
665 } else {
666 false
667 }
668 },
669 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
670 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
671 ExprKind::Path(qpath) => cx
672 .qpath_res(qpath, e.hir_id)
673 .ctor_parent(cx)
674 .is_lang_item(cx, OptionNone),
675 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
676 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
677 _ => false,
678 }
679}
680
681fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
682 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
683 && seg.ident.name == sym::from
684 {
685 match arg.kind {
686 ExprKind::Lit(hir::Lit {
687 node: LitKind::Str(sym, _),
688 ..
689 }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String),
690 ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec),
691 ExprKind::Repeat(_, len) => {
692 if let ConstArgKind::Anon(anon_const) = len.kind
693 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
694 && let LitKind::Int(v, _) = const_lit.node
695 {
696 return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec);
697 }
698 },
699 _ => (),
700 }
701 }
702 false
703}
704
705pub fn can_move_expr_to_closure_no_visit<'tcx>(
737 cx: &LateContext<'tcx>,
738 expr: &'tcx Expr<'_>,
739 loop_ids: &[HirId],
740 ignore_locals: &HirIdSet,
741) -> bool {
742 match expr.kind {
743 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
744 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
745 if loop_ids.contains(&id) =>
746 {
747 true
748 },
749 ExprKind::Break(..)
750 | ExprKind::Continue(_)
751 | ExprKind::Ret(_)
752 | ExprKind::Yield(..)
753 | ExprKind::InlineAsm(_) => false,
754 ExprKind::Field(
757 &Expr {
758 hir_id,
759 kind:
760 ExprKind::Path(QPath::Resolved(
761 _,
762 Path {
763 res: Res::Local(local_id),
764 ..
765 },
766 )),
767 ..
768 },
769 _,
770 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
771 false
773 },
774 _ => true,
775 }
776}
777
778#[derive(Debug, Clone, Copy, PartialEq, Eq)]
780pub enum CaptureKind {
781 Value,
782 Use,
783 Ref(Mutability),
784}
785impl CaptureKind {
786 pub fn is_imm_ref(self) -> bool {
787 self == Self::Ref(Mutability::Not)
788 }
789}
790impl std::ops::BitOr for CaptureKind {
791 type Output = Self;
792 fn bitor(self, rhs: Self) -> Self::Output {
793 match (self, rhs) {
794 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
795 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
796 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
797 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
798 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
799 }
800 }
801}
802impl std::ops::BitOrAssign for CaptureKind {
803 fn bitor_assign(&mut self, rhs: Self) {
804 *self = *self | rhs;
805 }
806}
807
808pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
814 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
815 let mut capture = CaptureKind::Ref(Mutability::Not);
816 pat.each_binding_or_first(&mut |_, id, span, _| match cx
817 .typeck_results()
818 .extract_binding_mode(cx.sess(), id, span)
819 .0
820 {
821 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
822 capture = CaptureKind::Value;
823 },
824 ByRef::Yes(_, Mutability::Mut) if capture != CaptureKind::Value => {
825 capture = CaptureKind::Ref(Mutability::Mut);
826 },
827 _ => (),
828 });
829 capture
830 }
831
832 debug_assert!(matches!(
833 e.kind,
834 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
835 ));
836
837 let mut child_id = e.hir_id;
838 let mut capture = CaptureKind::Value;
839 let mut capture_expr_ty = e;
840
841 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
842 if let [
843 Adjustment {
844 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
845 target,
846 },
847 ref adjust @ ..,
848 ] = *cx
849 .typeck_results()
850 .adjustments()
851 .get(child_id)
852 .map_or(&[][..], |x| &**x)
853 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
854 *adjust.last().map_or(target, |a| a.target).kind()
855 {
856 return CaptureKind::Ref(mutability);
857 }
858
859 match parent {
860 Node::Expr(e) => match e.kind {
861 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
862 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
863 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
864 return CaptureKind::Ref(Mutability::Mut);
865 },
866 ExprKind::Field(..) => {
867 if capture == CaptureKind::Value {
868 capture_expr_ty = e;
869 }
870 },
871 ExprKind::Let(let_expr) => {
872 let mutability = match pat_capture_kind(cx, let_expr.pat) {
873 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
874 CaptureKind::Ref(m) => m,
875 };
876 return CaptureKind::Ref(mutability);
877 },
878 ExprKind::Match(_, arms, _) => {
879 let mut mutability = Mutability::Not;
880 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
881 match capture {
882 CaptureKind::Value | CaptureKind::Use => break,
883 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
884 CaptureKind::Ref(Mutability::Not) => (),
885 }
886 }
887 return CaptureKind::Ref(mutability);
888 },
889 _ => break,
890 },
891 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
892 CaptureKind::Value | CaptureKind::Use => break,
893 capture @ CaptureKind::Ref(_) => return capture,
894 },
895 _ => break,
896 }
897
898 child_id = parent_id;
899 }
900
901 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
902 CaptureKind::Ref(Mutability::Not)
904 } else {
905 capture
906 }
907}
908
909pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
912 struct V<'cx, 'tcx> {
913 cx: &'cx LateContext<'tcx>,
914 loops: Vec<HirId>,
916 locals: HirIdSet,
918 allow_closure: bool,
920 captures: HirIdMap<CaptureKind>,
923 }
924 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
925 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
926 if !self.allow_closure {
927 return;
928 }
929
930 match e.kind {
931 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
932 if !self.locals.contains(&l) {
933 let cap = capture_local_usage(self.cx, e);
934 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
935 }
936 },
937 ExprKind::Closure(closure) => {
938 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
939 let local_id = match capture.place.base {
940 PlaceBase::Local(id) => id,
941 PlaceBase::Upvar(var) => var.var_path.hir_id,
942 _ => continue,
943 };
944 if !self.locals.contains(&local_id) {
945 let capture = match capture.info.capture_kind {
946 UpvarCapture::ByValue => CaptureKind::Value,
947 UpvarCapture::ByUse => CaptureKind::Use,
948 UpvarCapture::ByRef(kind) => match kind {
949 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
950 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
951 CaptureKind::Ref(Mutability::Mut)
952 },
953 },
954 };
955 self.captures
956 .entry(local_id)
957 .and_modify(|e| *e |= capture)
958 .or_insert(capture);
959 }
960 }
961 },
962 ExprKind::Loop(b, ..) => {
963 self.loops.push(e.hir_id);
964 self.visit_block(b);
965 self.loops.pop();
966 },
967 _ => {
968 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
969 walk_expr(self, e);
970 },
971 }
972 }
973
974 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
975 p.each_binding_or_first(&mut |_, id, _, _| {
976 self.locals.insert(id);
977 });
978 }
979 }
980
981 let mut v = V {
982 cx,
983 loops: Vec::new(),
984 locals: HirIdSet::default(),
985 allow_closure: true,
986 captures: HirIdMap::default(),
987 };
988 v.visit_expr(expr);
989 v.allow_closure.then_some(v.captures)
990}
991
992pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
994
995pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
998 let mut method_names = Vec::with_capacity(max_depth);
999 let mut arg_lists = Vec::with_capacity(max_depth);
1000 let mut spans = Vec::with_capacity(max_depth);
1001
1002 let mut current = expr;
1003 for _ in 0..max_depth {
1004 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1005 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1006 break;
1007 }
1008 method_names.push(path.ident.name);
1009 arg_lists.push((*receiver, &**args));
1010 spans.push(path.ident.span);
1011 current = receiver;
1012 } else {
1013 break;
1014 }
1015 }
1016
1017 (method_names, arg_lists, spans)
1018}
1019
1020pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1027 let mut current = expr;
1028 let mut matched = Vec::with_capacity(methods.len());
1029 for method_name in methods.iter().rev() {
1030 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1032 if path.ident.name == *method_name {
1033 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1034 return None;
1035 }
1036 matched.push((receiver, args)); current = receiver; } else {
1039 return None;
1040 }
1041 } else {
1042 return None;
1043 }
1044 }
1045 matched.reverse();
1047 Some(matched)
1048}
1049
1050pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1052 cx.tcx
1053 .entry_fn(())
1054 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1055}
1056
1057pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1059 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1060 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1061}
1062
1063pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1065 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1066 match cx.tcx.hir_node_by_def_id(parent_id) {
1067 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1068 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1069 _ => None,
1070 }
1071}
1072
1073pub struct ContainsName<'a, 'tcx> {
1074 pub cx: &'a LateContext<'tcx>,
1075 pub name: Symbol,
1076}
1077
1078impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1079 type Result = ControlFlow<()>;
1080 type NestedFilter = nested_filter::OnlyBodies;
1081
1082 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1083 if self.name == name {
1084 ControlFlow::Break(())
1085 } else {
1086 ControlFlow::Continue(())
1087 }
1088 }
1089
1090 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1091 self.cx.tcx
1092 }
1093}
1094
1095pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1097 let mut cn = ContainsName { cx, name };
1098 cn.visit_expr(expr).is_break()
1099}
1100
1101pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1103 for_each_expr_without_closures(expr, |e| {
1104 if matches!(e.kind, ExprKind::Ret(..)) {
1105 ControlFlow::Break(())
1106 } else {
1107 ControlFlow::Continue(())
1108 }
1109 })
1110 .is_some()
1111}
1112
1113pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1115 get_parent_expr_for_hir(cx, e.hir_id)
1116}
1117
1118pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1121 match cx.tcx.parent_hir_node(hir_id) {
1122 Node::Expr(parent) => Some(parent),
1123 _ => None,
1124 }
1125}
1126
1127pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1129 let enclosing_node = cx
1130 .tcx
1131 .hir_get_enclosing_scope(hir_id)
1132 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1133 enclosing_node.and_then(|node| match node {
1134 Node::Block(block) => Some(block),
1135 Node::Item(&Item {
1136 kind: ItemKind::Fn { body: eid, .. },
1137 ..
1138 })
1139 | Node::ImplItem(&ImplItem {
1140 kind: ImplItemKind::Fn(_, eid),
1141 ..
1142 })
1143 | Node::TraitItem(&TraitItem {
1144 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1145 ..
1146 }) => match cx.tcx.hir_body(eid).value.kind {
1147 ExprKind::Block(block, _) => Some(block),
1148 _ => None,
1149 },
1150 _ => None,
1151 })
1152}
1153
1154pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1156 cx: &LateContext<'tcx>,
1157 expr: &Expr<'_>,
1158) -> Option<&'tcx Expr<'tcx>> {
1159 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1160 match node {
1161 Node::Expr(e) => match e.kind {
1162 ExprKind::Closure { .. }
1163 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1164 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1165
1166 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1168 _ => (),
1169 },
1170 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1171 _ => break,
1172 }
1173 }
1174 None
1175}
1176
1177pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1179 match tcx.hir_parent_iter(id).next() {
1180 Some((
1181 _,
1182 Node::Item(Item {
1183 kind: ItemKind::Impl(imp),
1184 ..
1185 }),
1186 )) => Some(imp),
1187 _ => None,
1188 }
1189}
1190
1191pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1202 while let ExprKind::Block(
1203 Block {
1204 stmts: [],
1205 expr: Some(inner),
1206 rules: BlockCheckMode::DefaultBlock,
1207 ..
1208 },
1209 _,
1210 ) = expr.kind
1211 {
1212 expr = inner;
1213 }
1214 expr
1215}
1216
1217pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1228 while let ExprKind::Block(
1229 Block {
1230 stmts: [],
1231 expr: Some(inner),
1232 rules: BlockCheckMode::DefaultBlock,
1233 ..
1234 }
1235 | Block {
1236 stmts:
1237 [
1238 Stmt {
1239 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1240 ..
1241 },
1242 ],
1243 expr: None,
1244 rules: BlockCheckMode::DefaultBlock,
1245 ..
1246 },
1247 _,
1248 ) = expr.kind
1249 {
1250 expr = inner;
1251 }
1252 expr
1253}
1254
1255pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1257 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1258 match iter.next() {
1259 Some((
1260 _,
1261 Node::Expr(Expr {
1262 kind: ExprKind::If(_, _, Some(else_expr)),
1263 ..
1264 }),
1265 )) => else_expr.hir_id == expr.hir_id,
1266 _ => false,
1267 }
1268}
1269
1270pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1273 let mut child_id = expr.hir_id;
1274 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1275 if let Node::LetStmt(LetStmt {
1276 init: Some(init),
1277 els: Some(els),
1278 ..
1279 }) = node
1280 && (init.hir_id == child_id || els.hir_id == child_id)
1281 {
1282 return true;
1283 }
1284
1285 child_id = parent_id;
1286 }
1287
1288 false
1289}
1290
1291pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1293 let mut child_id = expr.hir_id;
1294 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1295 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1296 && els.hir_id == child_id
1297 {
1298 return true;
1299 }
1300
1301 child_id = parent_id;
1302 }
1303
1304 false
1305}
1306
1307pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1322 let ty = cx.typeck_results().expr_ty(expr);
1323 if let Some(Range { start, end, limits, .. }) = Range::hir(cx, expr) {
1324 let start_is_none_or_min = start.is_none_or(|start| {
1325 if let rustc_ty::Adt(_, subst) = ty.kind()
1326 && let bnd_ty = subst.type_at(0)
1327 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1328 {
1329 start_const.is_numeric_min(cx.tcx, bnd_ty)
1330 } else {
1331 false
1332 }
1333 });
1334 let end_is_none_or_max = end.is_none_or(|end| match limits {
1335 RangeLimits::Closed => {
1336 if let rustc_ty::Adt(_, subst) = ty.kind()
1337 && let bnd_ty = subst.type_at(0)
1338 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1339 {
1340 end_const.is_numeric_max(cx.tcx, bnd_ty)
1341 } else {
1342 false
1343 }
1344 },
1345 RangeLimits::HalfOpen => {
1346 if let Some(container_path) = container_path
1347 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1348 && name.ident.name == sym::len
1349 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1350 {
1351 container_path.res == path.res
1352 } else {
1353 false
1354 }
1355 },
1356 });
1357 return start_is_none_or_min && end_is_none_or_max;
1358 }
1359 false
1360}
1361
1362pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1365 if is_integer_literal(e, value) {
1366 return true;
1367 }
1368 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1369 if let Some(Constant::Int(v)) =
1370 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1371 {
1372 return value == v;
1373 }
1374 false
1375}
1376
1377pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1379 if let ExprKind::Lit(spanned) = expr.kind
1381 && let LitKind::Int(v, _) = spanned.node
1382 {
1383 return v == value;
1384 }
1385 false
1386}
1387
1388pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1390 if let ExprKind::Lit(spanned) = expr.kind
1391 && let LitKind::Float(v, _) = spanned.node
1392 {
1393 v.as_str().parse() == Ok(value)
1394 } else {
1395 false
1396 }
1397}
1398
1399pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1407 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1408}
1409
1410#[must_use]
1414pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1415 loop {
1416 if span.from_expansion() {
1417 let data = span.ctxt().outer_expn_data();
1418 let new_span = data.call_site;
1419
1420 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1421 && mac_name == name
1422 {
1423 return Some(new_span);
1424 }
1425
1426 span = new_span;
1427 } else {
1428 return None;
1429 }
1430 }
1431}
1432
1433#[must_use]
1444pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1445 if span.from_expansion() {
1446 let data = span.ctxt().outer_expn_data();
1447 let new_span = data.call_site;
1448
1449 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1450 && mac_name == name
1451 {
1452 return Some(new_span);
1453 }
1454 }
1455
1456 None
1457}
1458
1459pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1461 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1462 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1463}
1464
1465pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1467 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1468 cx.tcx.instantiate_bound_regions_with_erased(arg)
1469}
1470
1471pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1473 if let ExprKind::Call(fun, _) = expr.kind
1474 && let ExprKind::Path(ref qp) = fun.kind
1475 {
1476 let res = cx.qpath_res(qp, fun.hir_id);
1477 return match res {
1478 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1479 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1480 _ => false,
1481 };
1482 }
1483 false
1484}
1485
1486pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1489 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1490 !matches!(
1491 cx.qpath_res(qpath, id),
1492 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1493 )
1494 }
1495
1496 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1497 i.into_iter().any(|pat| is_refutable(cx, pat))
1498 }
1499
1500 match pat.kind {
1501 PatKind::Missing => unreachable!(),
1502 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1504 PatKind::Box(pat) | PatKind::Ref(pat, _, _) => is_refutable(cx, pat),
1505 PatKind::Expr(PatExpr {
1506 kind: PatExprKind::Path(qpath),
1507 hir_id,
1508 ..
1509 }) => is_qpath_refutable(cx, qpath, *hir_id),
1510 PatKind::Or(pats) => {
1511 are_refutable(cx, pats)
1513 },
1514 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1515 PatKind::Struct(ref qpath, fields, _) => {
1516 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1517 },
1518 PatKind::TupleStruct(ref qpath, pats, _) => {
1519 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1520 },
1521 PatKind::Slice(head, middle, tail) => {
1522 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1523 rustc_ty::Slice(..) => {
1524 !head.is_empty() || middle.is_none() || !tail.is_empty()
1526 },
1527 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1528 _ => {
1529 true
1531 },
1532 }
1533 },
1534 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1535 }
1536}
1537
1538pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1541 if let PatKind::Or(pats) = pat.kind {
1542 pats.iter().for_each(f);
1543 } else {
1544 f(pat);
1545 }
1546}
1547
1548pub fn is_self(slf: &Param<'_>) -> bool {
1549 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1550 name.name == kw::SelfLower
1551 } else {
1552 false
1553 }
1554}
1555
1556pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1557 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1558 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1559 {
1560 return true;
1561 }
1562 false
1563}
1564
1565pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1566 (0..decl.inputs.len()).map(move |i| &body.params[i])
1567}
1568
1569pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1572 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1573 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1574 && ddpos.as_opt_usize().is_none()
1575 && cx
1576 .qpath_res(path, arm.pat.hir_id)
1577 .ctor_parent(cx)
1578 .is_lang_item(cx, ResultOk)
1579 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1580 && arm.body.res_local_id() == Some(hir_id)
1581 {
1582 return true;
1583 }
1584 false
1585 }
1586
1587 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1588 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1589 cx.qpath_res(path, arm.pat.hir_id)
1590 .ctor_parent(cx)
1591 .is_lang_item(cx, ResultErr)
1592 } else {
1593 false
1594 }
1595 }
1596
1597 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1598 if let MatchSource::TryDesugar(_) = *source {
1600 return Some(expr);
1601 }
1602
1603 if arms.len() == 2
1604 && arms[0].guard.is_none()
1605 && arms[1].guard.is_none()
1606 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1607 {
1608 return Some(expr);
1609 }
1610 }
1611
1612 None
1613}
1614
1615pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1625 let mut suppress_lint = false;
1626
1627 for id in ids {
1628 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1629 if let Some(expectation) = lint_id {
1630 cx.fulfill_expectation(expectation);
1631 }
1632
1633 match level {
1634 Level::Allow | Level::Expect => suppress_lint = true,
1635 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1636 }
1637 }
1638
1639 suppress_lint
1640}
1641
1642pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1650 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1651}
1652
1653pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1654 while let PatKind::Ref(subpat, _, _) = pat.kind {
1655 pat = subpat;
1656 }
1657 pat
1658}
1659
1660pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1661 Integer::from_int_ty(&tcx, ity).size().bits()
1662}
1663
1664#[expect(clippy::cast_possible_wrap)]
1665pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1667 let amt = 128 - int_bits(tcx, ity);
1668 ((u as i128) << amt) >> amt
1669}
1670
1671#[expect(clippy::cast_sign_loss)]
1672pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1674 let amt = 128 - int_bits(tcx, ity);
1675 ((u as u128) << amt) >> amt
1676}
1677
1678pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1680 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1681 let amt = 128 - bits;
1682 (u << amt) >> amt
1683}
1684
1685pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1686 attrs.iter().any(|attr| attr.has_name(symbol))
1687}
1688
1689pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1690 find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr { .. })
1691}
1692
1693pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1694 let mut prev_enclosing_node = None;
1695 let mut enclosing_node = node;
1696 while Some(enclosing_node) != prev_enclosing_node {
1697 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1698 return true;
1699 }
1700 prev_enclosing_node = Some(enclosing_node);
1701 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1702 }
1703
1704 false
1705}
1706
1707pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1710 tcx.hir_parent_owner_iter(id)
1711 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1712 .any(|(id, _)| {
1713 find_attr!(
1714 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1715 AttributeKind::AutomaticallyDerived(..)
1716 )
1717 })
1718}
1719
1720pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1722 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1725}
1726
1727pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1732 let mut conds = Vec::new();
1733 let mut blocks: Vec<&Block<'_>> = Vec::new();
1734
1735 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1736 conds.push(cond);
1737 if let ExprKind::Block(block, _) = then.kind {
1738 blocks.push(block);
1739 } else {
1740 panic!("ExprKind::If node is not an ExprKind::Block");
1741 }
1742
1743 if let Some(else_expr) = r#else {
1744 expr = else_expr;
1745 } else {
1746 break;
1747 }
1748 }
1749
1750 if !blocks.is_empty()
1752 && let ExprKind::Block(block, _) = expr.kind
1753 {
1754 blocks.push(block);
1755 }
1756
1757 (conds, blocks)
1758}
1759
1760pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1762 if let ExprKind::Closure(&Closure {
1763 body,
1764 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1765 ..
1766 }) = expr.kind
1767 && let ExprKind::Block(
1768 Block {
1769 expr:
1770 Some(Expr {
1771 kind: ExprKind::DropTemps(inner_expr),
1772 ..
1773 }),
1774 ..
1775 },
1776 _,
1777 ) = tcx.hir_body(body).value.kind
1778 {
1779 Some(inner_expr)
1780 } else {
1781 None
1782 }
1783}
1784
1785pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1787 get_async_closure_expr(tcx, body.value)
1788}
1789
1790pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1792 let did = match expr.kind {
1793 ExprKind::Call(path, _) => {
1794 if let ExprKind::Path(ref qpath) = path.kind
1795 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1796 {
1797 Some(did)
1798 } else {
1799 None
1800 }
1801 },
1802 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1803 _ => None,
1804 };
1805
1806 did.is_some_and(|did| find_attr!(cx.tcx.get_all_attrs(did), AttributeKind::MustUse { .. }))
1807}
1808
1809fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1821 let [param] = func.params else {
1822 return false;
1823 };
1824
1825 let mut expr = func.value;
1826 loop {
1827 match expr.kind {
1828 ExprKind::Block(
1829 &Block {
1830 stmts: [],
1831 expr: Some(e),
1832 ..
1833 },
1834 _,
1835 )
1836 | ExprKind::Ret(Some(e)) => expr = e,
1837 ExprKind::Block(
1838 &Block {
1839 stmts: [stmt],
1840 expr: None,
1841 ..
1842 },
1843 _,
1844 ) => {
1845 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1846 && let ExprKind::Ret(Some(ret_val)) = e.kind
1847 {
1848 expr = ret_val;
1849 } else {
1850 return false;
1851 }
1852 },
1853 _ => return is_expr_identity_of_pat(cx, param.pat, expr, true),
1854 }
1855 }
1856}
1857
1858pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1868 if cx
1869 .typeck_results()
1870 .pat_binding_modes()
1871 .get(pat.hir_id)
1872 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..)))
1873 {
1874 return false;
1878 }
1879
1880 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1882
1883 match (pat.kind, expr.kind) {
1884 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1885 expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1886 },
1887 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1888 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1889 },
1890 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1891 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1892 {
1893 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1894 },
1895 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1896 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1897 },
1898 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1899 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1900 {
1901 if let ExprKind::Path(ident) = &ident.kind
1903 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1904 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
1906 {
1907 true
1908 } else {
1909 false
1910 }
1911 },
1912 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
1913 if field_pats.len() == fields.len() =>
1914 {
1915 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1917 && unordered_over(field_pats, fields, |field_pat, field| {
1919 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
1920 })
1921 },
1922 _ => false,
1923 }
1924}
1925
1926pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1931 match expr.kind {
1932 ExprKind::Closure(&Closure { body, fn_decl, .. })
1933 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1934 {
1935 is_body_identity_function(cx, cx.tcx.hir_body(body))
1936 },
1937 ExprKind::Path(QPath::Resolved(_, path))
1938 if path.segments.iter().all(|seg| seg.infer_args)
1939 && let Some(did) = path.res.opt_def_id() =>
1940 {
1941 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1942 },
1943 _ => false,
1944 }
1945}
1946
1947pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1956 match expr.kind {
1957 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1958 _ => expr.basic_res().is_diag_item(cx, sym::convert_identity),
1959 }
1960}
1961
1962pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
1965 let mut child_id = expr.hir_id;
1966 let mut iter = tcx.hir_parent_iter(child_id);
1967 loop {
1968 match iter.next() {
1969 None => break None,
1970 Some((id, Node::Block(_))) => child_id = id,
1971 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
1972 Some((_, Node::Expr(expr))) => match expr.kind {
1973 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
1974 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
1975 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
1976 _ => break Some((Node::Expr(expr), child_id)),
1977 },
1978 Some((_, node)) => break Some((node, child_id)),
1979 }
1980 }
1981}
1982
1983pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1985 !matches!(
1986 get_expr_use_or_unification_node(tcx, expr),
1987 None | Some((
1988 Node::Stmt(Stmt {
1989 kind: StmtKind::Expr(_)
1990 | StmtKind::Semi(_)
1991 | StmtKind::Let(LetStmt {
1992 pat: Pat {
1993 kind: PatKind::Wild,
1994 ..
1995 },
1996 ..
1997 }),
1998 ..
1999 }),
2000 _
2001 ))
2002 )
2003}
2004
2005pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2007 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2008}
2009
2010pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2014 !expr.is_place_expr(|base| {
2015 cx.typeck_results()
2016 .adjustments()
2017 .get(base.hir_id)
2018 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2019 })
2020}
2021
2022pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2023 if !is_no_std_crate(cx) {
2024 Some("std")
2025 } else if !is_no_core_crate(cx) {
2026 Some("core")
2027 } else {
2028 None
2029 }
2030}
2031
2032pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2033 find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoStd(..))
2034}
2035
2036pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2037 find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoCore(..))
2038}
2039
2040pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2050 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2051 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2052 } else {
2053 false
2054 }
2055}
2056
2057pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2067 use rustc_trait_selection::traits;
2068 let predicates = cx
2069 .tcx
2070 .predicates_of(did)
2071 .predicates
2072 .iter()
2073 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2074 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2075}
2076
2077pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2079 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2080}
2081
2082pub fn fn_def_id_with_node_args<'tcx>(
2085 cx: &LateContext<'tcx>,
2086 expr: &Expr<'_>,
2087) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2088 let typeck = cx.typeck_results();
2089 match &expr.kind {
2090 ExprKind::MethodCall(..) => Some((
2091 typeck.type_dependent_def_id(expr.hir_id)?,
2092 typeck.node_args(expr.hir_id),
2093 )),
2094 ExprKind::Call(
2095 Expr {
2096 kind: ExprKind::Path(qpath),
2097 hir_id: path_hir_id,
2098 ..
2099 },
2100 ..,
2101 ) => {
2102 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2105 typeck.qpath_res(qpath, *path_hir_id)
2106 {
2107 Some((id, typeck.node_args(*path_hir_id)))
2108 } else {
2109 None
2110 }
2111 },
2112 _ => None,
2113 }
2114}
2115
2116pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2121 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2122 let expr_kind = expr_type.kind();
2123 let is_primitive = match expr_kind {
2124 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2125 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2126 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2127 is_recursively_primitive_type(*element_type)
2128 } else {
2129 unreachable!()
2130 }
2131 },
2132 _ => false,
2133 };
2134
2135 if is_primitive {
2136 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2139 rustc_ty::Slice(..) => return Some("slice".into()),
2140 rustc_ty::Array(..) => return Some("array".into()),
2141 rustc_ty::Tuple(..) => return Some("tuple".into()),
2142 _ => {
2143 let refs_peeled = expr_type.peel_refs();
2146 return Some(refs_peeled.walk().last().unwrap().to_string());
2147 },
2148 }
2149 }
2150 None
2151}
2152
2153pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2161where
2162 Hash: FnMut(&T) -> u64,
2163 Eq: FnMut(&T, &T) -> bool,
2164{
2165 match exprs {
2166 [a, b] if eq(a, b) => return vec![vec![a, b]],
2167 _ if exprs.len() <= 2 => return vec![],
2168 _ => {},
2169 }
2170
2171 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2172
2173 for expr in exprs {
2174 match buckets.entry(hash(expr)) {
2175 indexmap::map::Entry::Occupied(mut o) => {
2176 let bucket = o.get_mut();
2177 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2178 Some(group) => group.push(expr),
2179 None => bucket.push(vec![expr]),
2180 }
2181 },
2182 indexmap::map::Entry::Vacant(v) => {
2183 v.insert(vec![vec![expr]]);
2184 },
2185 }
2186 }
2187
2188 buckets
2189 .into_values()
2190 .flatten()
2191 .filter(|group| group.len() > 1)
2192 .collect()
2193}
2194
2195pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2198 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2199 if let PatKind::Ref(pat, _, _) = pat.kind {
2200 peel(pat, count + 1)
2201 } else {
2202 (pat, count)
2203 }
2204 }
2205 peel(pat, 0)
2206}
2207
2208pub fn peel_hir_expr_while<'tcx>(
2210 mut expr: &'tcx Expr<'tcx>,
2211 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2212) -> &'tcx Expr<'tcx> {
2213 while let Some(e) = f(expr) {
2214 expr = e;
2215 }
2216 expr
2217}
2218
2219pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2222 let mut remaining = count;
2223 let e = peel_hir_expr_while(expr, |e| match e.kind {
2224 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2225 remaining -= 1;
2226 Some(e)
2227 },
2228 _ => None,
2229 });
2230 (e, count - remaining)
2231}
2232
2233pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2236 let mut count: usize = 0;
2237 let mut curr_expr = expr;
2238 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2239 count = count.wrapping_add(1);
2240 curr_expr = local_expr;
2241 }
2242 (curr_expr, count)
2243}
2244
2245pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2248 let mut count = 0;
2249 let e = peel_hir_expr_while(expr, |e| match e.kind {
2250 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2251 count += 1;
2252 Some(e)
2253 },
2254 _ => None,
2255 });
2256 (e, count)
2257}
2258
2259pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2262 let mut count = 0;
2263 loop {
2264 match &ty.kind {
2265 TyKind::Ref(_, ref_ty) => {
2266 ty = ref_ty.ty;
2267 count += 1;
2268 },
2269 _ => break (ty, count),
2270 }
2271 }
2272}
2273
2274pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
2276 match &ty.kind {
2277 TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty),
2278 _ => ty,
2279 }
2280}
2281
2282pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2285 loop {
2286 match expr.kind {
2287 ExprKind::AddrOf(_, _, e) => expr = e,
2288 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2289 _ => break,
2290 }
2291 }
2292 expr
2293}
2294
2295pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2298 let mut operators = Vec::new();
2299 peel_hir_expr_while(expr, |expr| match expr.kind {
2300 ExprKind::AddrOf(_, _, e) => {
2301 operators.push(expr);
2302 Some(e)
2303 },
2304 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2305 operators.push(expr);
2306 Some(e)
2307 },
2308 _ => None,
2309 });
2310 operators
2311}
2312
2313pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2314 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2315 && let Res::Def(_, def_id) = path.res
2316 {
2317 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2318 }
2319 false
2320}
2321
2322static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2323
2324fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2327 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2328 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2329 let value = map.entry(module);
2330 match value {
2331 Entry::Occupied(entry) => f(entry.get()),
2332 Entry::Vacant(entry) => {
2333 let mut names = Vec::new();
2334 for id in tcx.hir_module_free_items(module) {
2335 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2336 && let item = tcx.hir_item(id)
2337 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2338 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2339 && let Res::Def(DefKind::Struct, _) = path.res
2341 {
2342 let has_test_marker = tcx
2343 .hir_attrs(item.hir_id())
2344 .iter()
2345 .any(|a| a.has_name(sym::rustc_test_marker));
2346 if has_test_marker {
2347 names.push(ident.name);
2348 }
2349 }
2350 }
2351 names.sort_unstable();
2352 f(entry.insert(names))
2353 },
2354 }
2355}
2356
2357pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2361 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2362 let node = tcx.hir_node(id);
2363 once((id, node))
2364 .chain(tcx.hir_parent_iter(id))
2365 .any(|(_id, node)| {
2368 if let Node::Item(item) = node
2369 && let ItemKind::Fn { ident, .. } = item.kind
2370 {
2371 return names.binary_search(&ident.name).is_ok();
2374 }
2375 false
2376 })
2377 })
2378}
2379
2380pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2387 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2388 if let Node::Item(item) = tcx.hir_node(id)
2389 && let ItemKind::Fn { ident, .. } = item.kind
2390 {
2391 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2392 names.binary_search(&ident.name).is_ok()
2393 })
2394 } else {
2395 false
2396 }
2397}
2398
2399pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2404 tcx.hir_attrs(id).iter().any(|attr| {
2405 if attr.has_name(sym::cfg_trace)
2406 && let Some(items) = attr.meta_item_list()
2407 && let [item] = &*items
2408 && item.has_name(sym::test)
2409 {
2410 true
2411 } else {
2412 false
2413 }
2414 })
2415}
2416
2417pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2419 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2420}
2421
2422pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2424 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2425}
2426
2427pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2429 tcx.has_attr(def_id, sym::cfg_trace)
2430 || tcx
2431 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2432 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2433 .any(|attr| attr.has_name(sym::cfg_trace))
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(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny
3538 )
3539 })
3540}
3541
3542pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3544 matches!(
3545 expr.kind,
3546 ExprKind::Closure(Closure {
3547 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3548 CoroutineDesugaring::Async,
3549 CoroutineSource::Block
3550 )),
3551 ..
3552 })
3553 )
3554}
3555
3556pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3558 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3559}