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#![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;
53pub mod attrs;
54mod check_proc_macro;
55pub mod comparisons;
56pub mod consts;
57pub mod diagnostics;
58pub mod eager_or_lazy;
59pub mod higher;
60mod hir_utils;
61pub mod macros;
62pub mod mir;
63pub mod msrvs;
64pub mod numeric_literal;
65pub mod paths;
66pub mod qualify_min_const_fn;
67pub mod res;
68pub mod source;
69pub mod str_utils;
70pub mod sugg;
71pub mod sym;
72pub mod ty;
73pub mod usage;
74pub mod visitors;
75
76pub use self::attrs::*;
77pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
78pub use self::hir_utils::{
79 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
80 hash_stmt, is_bool, over,
81};
82
83use core::mem;
84use core::ops::ControlFlow;
85use std::collections::hash_map::Entry;
86use std::iter::{once, repeat_n, zip};
87use std::sync::{Mutex, MutexGuard, OnceLock};
88
89use itertools::Itertools;
90use rustc_abi::Integer;
91use rustc_ast::ast::{self, LitKind, RangeLimits};
92use rustc_ast::join_path_syms;
93use rustc_data_structures::fx::FxHashMap;
94use rustc_data_structures::indexmap;
95use rustc_data_structures::packed::Pu128;
96use rustc_data_structures::unhash::UnindexMap;
97use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
98use rustc_hir::attrs::AttributeKind;
99use rustc_hir::def::{DefKind, Res};
100use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
101use rustc_hir::definitions::{DefPath, DefPathData};
102use rustc_hir::hir_id::{HirIdMap, HirIdSet};
103use rustc_hir::intravisit::{Visitor, walk_expr};
104use rustc_hir::{
105 self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
106 CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs,
107 HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId,
108 OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
109 TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr,
110};
111use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
112use rustc_lint::{LateContext, Level, Lint, LintContext};
113use rustc_middle::hir::nested_filter;
114use rustc_middle::hir::place::PlaceBase;
115use rustc_middle::lint::LevelAndSource;
116use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
117use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, PointerCoercion};
118use rustc_middle::ty::layout::IntegerExt;
119use rustc_middle::ty::{
120 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
121 TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
122};
123use rustc_span::hygiene::{ExpnKind, MacroKind};
124use rustc_span::source_map::SourceMap;
125use rustc_span::symbol::{Ident, Symbol, kw};
126use rustc_span::{InnerSpan, Span};
127use source::{SpanRangeExt, walk_span_to_context};
128use visitors::{Visitable, for_each_unconsumed_temporary};
129
130use crate::ast_utils::unordered_over;
131use crate::consts::{ConstEvalCtxt, Constant};
132use crate::higher::Range;
133use crate::msrvs::Msrv;
134use crate::res::{MaybeDef, MaybeResPath};
135use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
136use crate::visitors::for_each_expr_without_closures;
137
138#[macro_export]
139macro_rules! extract_msrv_attr {
140 () => {
141 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
142 let sess = rustc_lint::LintContext::sess(cx);
143 self.msrv.check_attributes(sess, attrs);
144 }
145
146 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
147 let sess = rustc_lint::LintContext::sess(cx);
148 self.msrv.check_attributes_post(sess, attrs);
149 }
150 };
151}
152
153pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
176 while let Some(init) = expr
177 .res_local_id()
178 .and_then(|id| find_binding_init(cx, id))
179 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
180 {
181 expr = init;
182 }
183 expr
184}
185
186pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
195 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
196 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
197 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
198 {
199 return local.init;
200 }
201 None
202}
203
204pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
208 for (_, node) in cx.tcx.hir_parent_iter(local) {
209 match node {
210 Node::Pat(..) | Node::PatField(..) => {},
211 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
212 _ => return true,
213 }
214 }
215
216 false
217}
218
219pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
230 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
231 cx.enclosing_body.is_some_and(|id| {
232 cx.tcx
233 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
234 .is_some()
235 })
236}
237
238pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
245 use rustc_hir::ConstContext::{Const, ConstFn, Static};
246 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
247 return false;
248 };
249 match ctx {
250 ConstFn => false,
251 Static(_) | Const { inline: _ } => true,
252 }
253}
254
255pub fn is_enum_variant_ctor(
257 cx: &LateContext<'_>,
258 enum_item: Symbol,
259 variant_name: Symbol,
260 ctor_call_id: DefId,
261) -> bool {
262 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
263 return false;
264 };
265
266 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
267 variants
268 .filter(|variant| variant.name == variant_name)
269 .filter_map(|variant| variant.ctor.as_ref())
270 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
271}
272
273pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
275 let did = match cx.tcx.def_kind(did) {
276 DefKind::Ctor(..) => cx.tcx.parent(did),
277 DefKind::Variant => match cx.tcx.opt_parent(did) {
279 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
280 _ => did,
281 },
282 _ => did,
283 };
284
285 cx.tcx.is_diagnostic_item(item, did)
286}
287
288pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
290 let did = match cx.tcx.def_kind(did) {
291 DefKind::Ctor(..) => cx.tcx.parent(did),
292 DefKind::Variant => match cx.tcx.opt_parent(did) {
294 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
295 _ => did,
296 },
297 _ => did,
298 };
299
300 cx.tcx.lang_items().get(item) == Some(did)
301}
302
303pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
305 matches!(
306 expr.kind,
307 ExprKind::Block(
308 Block {
309 stmts: [],
310 expr: None,
311 ..
312 },
313 _
314 ) | ExprKind::Tup([])
315 )
316}
317
318pub fn is_wild(pat: &Pat<'_>) -> bool {
320 matches!(pat.kind, PatKind::Wild)
321}
322
323pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
325 matches!(pat.kind,
326 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
327 if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone))
328}
329
330pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
332 is_none_pattern(cx, arm.pat)
333 && matches!(
334 peel_blocks(arm.body).kind,
335 ExprKind::Path(qpath)
336 if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)
337 )
338}
339
340pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
342 match *qpath {
343 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
344 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
345 _ => false,
346 }
347}
348
349pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
351 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
352 && let ItemKind::Impl(imp) = item.kind
353 {
354 imp.of_trait.is_some()
355 } else {
356 false
357 }
358}
359
360pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
361 match *path {
362 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
363 QPath::TypeRelative(_, seg) => seg,
364 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
365 }
366}
367
368pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
369 last_path_segment(qpath)
370 .args
371 .map_or(&[][..], |a| a.args)
372 .iter()
373 .filter_map(|a| match a {
374 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
375 _ => None,
376 })
377}
378
379pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
384 match expr.kind {
385 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
386 ExprKind::Path(QPath::Resolved(
387 _,
388 Path {
389 res: Res::Local(local), ..
390 },
391 )) => Some(*local),
392 _ => None,
393 }
394}
395
396pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
412 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
413 && let ItemKind::Impl(impl_) = &item.kind
414 && let Some(of_trait) = impl_.of_trait
415 {
416 return Some(&of_trait.trait_ref);
417 }
418 None
419}
420
421fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
429 let mut result = vec![];
430 let root = loop {
431 match e.kind {
432 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
433 result.push(e);
434 e = ep;
435 },
436 _ => break e,
437 }
438 };
439 result.reverse();
440 (result, root)
441}
442
443pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
445 cx.typeck_results()
446 .expr_adjustments(e)
447 .iter()
448 .find_map(|a| match a.kind {
449 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
450 Adjust::Deref(None) => None,
451 _ => Some(None),
452 })
453 .and_then(|x| x)
454}
455
456pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
459 let (s1, r1) = projection_stack(e1);
460 let (s2, r2) = projection_stack(e2);
461 if !eq_expr_value(cx, r1, r2) {
462 return true;
463 }
464 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
465 return false;
466 }
467
468 for (x1, x2) in zip(&s1, &s2) {
469 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
470 return false;
471 }
472
473 match (&x1.kind, &x2.kind) {
474 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
475 if i1 != i2 {
476 return true;
477 }
478 },
479 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
480 if !eq_expr_value(cx, i1, i2) {
481 return false;
482 }
483 },
484 _ => return false,
485 }
486 }
487 false
488}
489
490fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
493 let std_types_symbols = &[
494 sym::Vec,
495 sym::VecDeque,
496 sym::LinkedList,
497 sym::HashMap,
498 sym::BTreeMap,
499 sym::HashSet,
500 sym::BTreeSet,
501 sym::BinaryHeap,
502 ];
503
504 if let QPath::TypeRelative(_, method) = path
505 && method.ident.name == sym::new
506 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
507 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
508 {
509 return Some(adt.did()) == cx.tcx.lang_items().string()
510 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
511 }
512 false
513}
514
515pub fn is_default_equivalent_call(
517 cx: &LateContext<'_>,
518 repl_func: &Expr<'_>,
519 whole_call_expr: Option<&Expr<'_>>,
520) -> bool {
521 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
522 && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx)
523 && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default)
524 || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath))
525 {
526 return true;
527 }
528
529 let Some(e) = whole_call_expr else { return false };
532 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
533 return false;
534 };
535 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
536 return false;
537 };
538 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
539 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
540 cx.tcx.lifetimes.re_erased.into()
541 } else if param.index == 0 && param.name == kw::SelfUpper {
542 ty.into()
543 } else {
544 param.to_error(cx.tcx)
545 }
546 });
547 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
548
549 let Ok(Some(instance)) = instance else { return false };
550 if let rustc_ty::InstanceKind::Item(def) = instance.def
551 && !cx.tcx.is_mir_available(def)
552 {
553 return false;
554 }
555 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
556 return false;
557 };
558 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
559 return false;
560 };
561
562 let body = cx.tcx.instance_mir(instance.def);
568 for block_data in body.basic_blocks.iter() {
569 if block_data.statements.len() == 1
570 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
571 && assign.0.local == RETURN_PLACE
572 && let Rvalue::Aggregate(kind, _places) = &assign.1
573 && let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
574 && let def = cx.tcx.adt_def(did)
575 && let variant = &def.variant(*variant_index)
576 && variant.fields.is_empty()
577 && let Some((_, did)) = variant.ctor
578 && did == repl_def_id
579 {
580 return true;
581 } else if block_data.statements.is_empty()
582 && let Some(term) = &block_data.terminator
583 {
584 match &term.kind {
585 TerminatorKind::Call {
586 func: Operand::Constant(c),
587 ..
588 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
589 && *did == repl_def_id =>
590 {
591 return true;
592 },
593 TerminatorKind::TailCall {
594 func: Operand::Constant(c),
595 ..
596 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
597 && *did == repl_def_id =>
598 {
599 return true;
600 },
601 _ => {},
602 }
603 }
604 }
605 false
606}
607
608pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
612 match &e.kind {
613 ExprKind::Lit(lit) => match lit.node {
614 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
615 LitKind::Str(s, _) => s.is_empty(),
616 _ => false,
617 },
618 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
619 ExprKind::Repeat(x, len) => {
620 if let ConstArgKind::Anon(anon_const) = len.kind
621 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
622 && let LitKind::Int(v, _) = const_lit.node
623 && v <= 32
624 && is_default_equivalent(cx, x)
625 {
626 true
627 } else {
628 false
629 }
630 },
631 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
632 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
633 ExprKind::Path(qpath) => cx
634 .qpath_res(qpath, e.hir_id)
635 .ctor_parent(cx)
636 .is_lang_item(cx, OptionNone),
637 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
638 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
639 _ => false,
640 }
641}
642
643fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
644 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
645 && seg.ident.name == sym::from
646 {
647 match arg.kind {
648 ExprKind::Lit(hir::Lit {
649 node: LitKind::Str(sym, _),
650 ..
651 }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String),
652 ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec),
653 ExprKind::Repeat(_, len) => {
654 if let ConstArgKind::Anon(anon_const) = len.kind
655 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
656 && let LitKind::Int(v, _) = const_lit.node
657 {
658 return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec);
659 }
660 },
661 _ => (),
662 }
663 }
664 false
665}
666
667pub fn can_move_expr_to_closure_no_visit<'tcx>(
699 cx: &LateContext<'tcx>,
700 expr: &'tcx Expr<'_>,
701 loop_ids: &[HirId],
702 ignore_locals: &HirIdSet,
703) -> bool {
704 match expr.kind {
705 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
706 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
707 if loop_ids.contains(&id) =>
708 {
709 true
710 },
711 ExprKind::Break(..)
712 | ExprKind::Continue(_)
713 | ExprKind::Ret(_)
714 | ExprKind::Yield(..)
715 | ExprKind::InlineAsm(_) => false,
716 ExprKind::Field(
719 &Expr {
720 hir_id,
721 kind:
722 ExprKind::Path(QPath::Resolved(
723 _,
724 Path {
725 res: Res::Local(local_id),
726 ..
727 },
728 )),
729 ..
730 },
731 _,
732 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
733 false
735 },
736 _ => true,
737 }
738}
739
740#[derive(Debug, Clone, Copy, PartialEq, Eq)]
742pub enum CaptureKind {
743 Value,
744 Use,
745 Ref(Mutability),
746}
747impl CaptureKind {
748 pub fn is_imm_ref(self) -> bool {
749 self == Self::Ref(Mutability::Not)
750 }
751}
752impl std::ops::BitOr for CaptureKind {
753 type Output = Self;
754 fn bitor(self, rhs: Self) -> Self::Output {
755 match (self, rhs) {
756 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
757 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
758 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
759 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
760 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
761 }
762 }
763}
764impl std::ops::BitOrAssign for CaptureKind {
765 fn bitor_assign(&mut self, rhs: Self) {
766 *self = *self | rhs;
767 }
768}
769
770pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
776 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
777 let mut capture = CaptureKind::Ref(Mutability::Not);
778 pat.each_binding_or_first(&mut |_, id, span, _| match cx
779 .typeck_results()
780 .extract_binding_mode(cx.sess(), id, span)
781 .0
782 {
783 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
784 capture = CaptureKind::Value;
785 },
786 ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
787 capture = CaptureKind::Ref(Mutability::Mut);
788 },
789 _ => (),
790 });
791 capture
792 }
793
794 debug_assert!(matches!(
795 e.kind,
796 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
797 ));
798
799 let mut child_id = e.hir_id;
800 let mut capture = CaptureKind::Value;
801 let mut capture_expr_ty = e;
802
803 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
804 if let [
805 Adjustment {
806 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
807 target,
808 },
809 ref adjust @ ..,
810 ] = *cx
811 .typeck_results()
812 .adjustments()
813 .get(child_id)
814 .map_or(&[][..], |x| &**x)
815 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
816 *adjust.last().map_or(target, |a| a.target).kind()
817 {
818 return CaptureKind::Ref(mutability);
819 }
820
821 match parent {
822 Node::Expr(e) => match e.kind {
823 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
824 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
825 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
826 return CaptureKind::Ref(Mutability::Mut);
827 },
828 ExprKind::Field(..) => {
829 if capture == CaptureKind::Value {
830 capture_expr_ty = e;
831 }
832 },
833 ExprKind::Let(let_expr) => {
834 let mutability = match pat_capture_kind(cx, let_expr.pat) {
835 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
836 CaptureKind::Ref(m) => m,
837 };
838 return CaptureKind::Ref(mutability);
839 },
840 ExprKind::Match(_, arms, _) => {
841 let mut mutability = Mutability::Not;
842 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
843 match capture {
844 CaptureKind::Value | CaptureKind::Use => break,
845 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
846 CaptureKind::Ref(Mutability::Not) => (),
847 }
848 }
849 return CaptureKind::Ref(mutability);
850 },
851 _ => break,
852 },
853 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
854 CaptureKind::Value | CaptureKind::Use => break,
855 capture @ CaptureKind::Ref(_) => return capture,
856 },
857 _ => break,
858 }
859
860 child_id = parent_id;
861 }
862
863 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
864 CaptureKind::Ref(Mutability::Not)
866 } else {
867 capture
868 }
869}
870
871pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
874 struct V<'cx, 'tcx> {
875 cx: &'cx LateContext<'tcx>,
876 loops: Vec<HirId>,
878 locals: HirIdSet,
880 allow_closure: bool,
882 captures: HirIdMap<CaptureKind>,
885 }
886 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
887 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
888 if !self.allow_closure {
889 return;
890 }
891
892 match e.kind {
893 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
894 if !self.locals.contains(&l) {
895 let cap = capture_local_usage(self.cx, e);
896 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
897 }
898 },
899 ExprKind::Closure(closure) => {
900 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
901 let local_id = match capture.place.base {
902 PlaceBase::Local(id) => id,
903 PlaceBase::Upvar(var) => var.var_path.hir_id,
904 _ => continue,
905 };
906 if !self.locals.contains(&local_id) {
907 let capture = match capture.info.capture_kind {
908 UpvarCapture::ByValue => CaptureKind::Value,
909 UpvarCapture::ByUse => CaptureKind::Use,
910 UpvarCapture::ByRef(kind) => match kind {
911 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
912 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
913 CaptureKind::Ref(Mutability::Mut)
914 },
915 },
916 };
917 self.captures
918 .entry(local_id)
919 .and_modify(|e| *e |= capture)
920 .or_insert(capture);
921 }
922 }
923 },
924 ExprKind::Loop(b, ..) => {
925 self.loops.push(e.hir_id);
926 self.visit_block(b);
927 self.loops.pop();
928 },
929 _ => {
930 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
931 walk_expr(self, e);
932 },
933 }
934 }
935
936 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
937 p.each_binding_or_first(&mut |_, id, _, _| {
938 self.locals.insert(id);
939 });
940 }
941 }
942
943 let mut v = V {
944 cx,
945 loops: Vec::new(),
946 locals: HirIdSet::default(),
947 allow_closure: true,
948 captures: HirIdMap::default(),
949 };
950 v.visit_expr(expr);
951 v.allow_closure.then_some(v.captures)
952}
953
954pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
956
957pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
960 let mut method_names = Vec::with_capacity(max_depth);
961 let mut arg_lists = Vec::with_capacity(max_depth);
962 let mut spans = Vec::with_capacity(max_depth);
963
964 let mut current = expr;
965 for _ in 0..max_depth {
966 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
967 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
968 break;
969 }
970 method_names.push(path.ident.name);
971 arg_lists.push((*receiver, &**args));
972 spans.push(path.ident.span);
973 current = receiver;
974 } else {
975 break;
976 }
977 }
978
979 (method_names, arg_lists, spans)
980}
981
982pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
989 let mut current = expr;
990 let mut matched = Vec::with_capacity(methods.len());
991 for method_name in methods.iter().rev() {
992 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
994 if path.ident.name == *method_name {
995 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
996 return None;
997 }
998 matched.push((receiver, args)); current = receiver; } else {
1001 return None;
1002 }
1003 } else {
1004 return None;
1005 }
1006 }
1007 matched.reverse();
1009 Some(matched)
1010}
1011
1012pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1014 cx.tcx
1015 .entry_fn(())
1016 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1017}
1018
1019pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1021 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1022 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1023}
1024
1025pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1027 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1028 match cx.tcx.hir_node_by_def_id(parent_id) {
1029 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1030 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1031 _ => None,
1032 }
1033}
1034
1035pub struct ContainsName<'a, 'tcx> {
1036 pub cx: &'a LateContext<'tcx>,
1037 pub name: Symbol,
1038}
1039
1040impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1041 type Result = ControlFlow<()>;
1042 type NestedFilter = nested_filter::OnlyBodies;
1043
1044 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1045 if self.name == name {
1046 ControlFlow::Break(())
1047 } else {
1048 ControlFlow::Continue(())
1049 }
1050 }
1051
1052 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1053 self.cx.tcx
1054 }
1055}
1056
1057pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1059 let mut cn = ContainsName { cx, name };
1060 cn.visit_expr(expr).is_break()
1061}
1062
1063pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1065 for_each_expr_without_closures(expr, |e| {
1066 if matches!(e.kind, ExprKind::Ret(..)) {
1067 ControlFlow::Break(())
1068 } else {
1069 ControlFlow::Continue(())
1070 }
1071 })
1072 .is_some()
1073}
1074
1075pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1077 get_parent_expr_for_hir(cx, e.hir_id)
1078}
1079
1080pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1083 match cx.tcx.parent_hir_node(hir_id) {
1084 Node::Expr(parent) => Some(parent),
1085 _ => None,
1086 }
1087}
1088
1089pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1091 let enclosing_node = cx
1092 .tcx
1093 .hir_get_enclosing_scope(hir_id)
1094 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1095 enclosing_node.and_then(|node| match node {
1096 Node::Block(block) => Some(block),
1097 Node::Item(&Item {
1098 kind: ItemKind::Fn { body: eid, .. },
1099 ..
1100 })
1101 | Node::ImplItem(&ImplItem {
1102 kind: ImplItemKind::Fn(_, eid),
1103 ..
1104 })
1105 | Node::TraitItem(&TraitItem {
1106 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1107 ..
1108 }) => match cx.tcx.hir_body(eid).value.kind {
1109 ExprKind::Block(block, _) => Some(block),
1110 _ => None,
1111 },
1112 _ => None,
1113 })
1114}
1115
1116pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1118 cx: &LateContext<'tcx>,
1119 expr: &Expr<'_>,
1120) -> Option<&'tcx Expr<'tcx>> {
1121 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1122 match node {
1123 Node::Expr(e) => match e.kind {
1124 ExprKind::Closure { .. }
1125 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1126 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1127
1128 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1130 _ => (),
1131 },
1132 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1133 _ => break,
1134 }
1135 }
1136 None
1137}
1138
1139pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1141 match tcx.hir_parent_iter(id).next() {
1142 Some((
1143 _,
1144 Node::Item(Item {
1145 kind: ItemKind::Impl(imp),
1146 ..
1147 }),
1148 )) => Some(imp),
1149 _ => None,
1150 }
1151}
1152
1153pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1164 while let ExprKind::Block(
1165 Block {
1166 stmts: [],
1167 expr: Some(inner),
1168 rules: BlockCheckMode::DefaultBlock,
1169 ..
1170 },
1171 _,
1172 ) = expr.kind
1173 {
1174 expr = inner;
1175 }
1176 expr
1177}
1178
1179pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1190 while let ExprKind::Block(
1191 Block {
1192 stmts: [],
1193 expr: Some(inner),
1194 rules: BlockCheckMode::DefaultBlock,
1195 ..
1196 }
1197 | Block {
1198 stmts:
1199 [
1200 Stmt {
1201 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1202 ..
1203 },
1204 ],
1205 expr: None,
1206 rules: BlockCheckMode::DefaultBlock,
1207 ..
1208 },
1209 _,
1210 ) = expr.kind
1211 {
1212 expr = inner;
1213 }
1214 expr
1215}
1216
1217pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1219 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1220 match iter.next() {
1221 Some((
1222 _,
1223 Node::Expr(Expr {
1224 kind: ExprKind::If(_, _, Some(else_expr)),
1225 ..
1226 }),
1227 )) => else_expr.hir_id == expr.hir_id,
1228 _ => false,
1229 }
1230}
1231
1232pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1235 let mut child_id = expr.hir_id;
1236 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1237 if let Node::LetStmt(LetStmt {
1238 init: Some(init),
1239 els: Some(els),
1240 ..
1241 }) = node
1242 && (init.hir_id == child_id || els.hir_id == child_id)
1243 {
1244 return true;
1245 }
1246
1247 child_id = parent_id;
1248 }
1249
1250 false
1251}
1252
1253pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1255 let mut child_id = expr.hir_id;
1256 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1257 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1258 && els.hir_id == child_id
1259 {
1260 return true;
1261 }
1262
1263 child_id = parent_id;
1264 }
1265
1266 false
1267}
1268
1269pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1284 let ty = cx.typeck_results().expr_ty(expr);
1285 if let Some(Range { start, end, limits }) = Range::hir(expr) {
1286 let start_is_none_or_min = start.is_none_or(|start| {
1287 if let rustc_ty::Adt(_, subst) = ty.kind()
1288 && let bnd_ty = subst.type_at(0)
1289 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1290 {
1291 start_const.is_numeric_min(cx.tcx, bnd_ty)
1292 } else {
1293 false
1294 }
1295 });
1296 let end_is_none_or_max = end.is_none_or(|end| match limits {
1297 RangeLimits::Closed => {
1298 if let rustc_ty::Adt(_, subst) = ty.kind()
1299 && let bnd_ty = subst.type_at(0)
1300 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1301 {
1302 end_const.is_numeric_max(cx.tcx, bnd_ty)
1303 } else {
1304 false
1305 }
1306 },
1307 RangeLimits::HalfOpen => {
1308 if let Some(container_path) = container_path
1309 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1310 && name.ident.name == sym::len
1311 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1312 {
1313 container_path.res == path.res
1314 } else {
1315 false
1316 }
1317 },
1318 });
1319 return start_is_none_or_min && end_is_none_or_max;
1320 }
1321 false
1322}
1323
1324pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1327 if is_integer_literal(e, value) {
1328 return true;
1329 }
1330 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1331 if let Some(Constant::Int(v)) =
1332 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1333 {
1334 return value == v;
1335 }
1336 false
1337}
1338
1339pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1341 if let ExprKind::Lit(spanned) = expr.kind
1343 && let LitKind::Int(v, _) = spanned.node
1344 {
1345 return v == value;
1346 }
1347 false
1348}
1349
1350pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1352 if let ExprKind::Lit(spanned) = expr.kind
1353 && let LitKind::Float(v, _) = spanned.node
1354 {
1355 v.as_str().parse() == Ok(value)
1356 } else {
1357 false
1358 }
1359}
1360
1361pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1369 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1370}
1371
1372#[must_use]
1376pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1377 loop {
1378 if span.from_expansion() {
1379 let data = span.ctxt().outer_expn_data();
1380 let new_span = data.call_site;
1381
1382 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1383 && mac_name == name
1384 {
1385 return Some(new_span);
1386 }
1387
1388 span = new_span;
1389 } else {
1390 return None;
1391 }
1392 }
1393}
1394
1395#[must_use]
1406pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1407 if span.from_expansion() {
1408 let data = span.ctxt().outer_expn_data();
1409 let new_span = data.call_site;
1410
1411 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1412 && mac_name == name
1413 {
1414 return Some(new_span);
1415 }
1416 }
1417
1418 None
1419}
1420
1421pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1423 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1424 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1425}
1426
1427pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1429 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1430 cx.tcx.instantiate_bound_regions_with_erased(arg)
1431}
1432
1433pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1435 if let ExprKind::Call(fun, _) = expr.kind
1436 && let ExprKind::Path(ref qp) = fun.kind
1437 {
1438 let res = cx.qpath_res(qp, fun.hir_id);
1439 return match res {
1440 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1441 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1442 _ => false,
1443 };
1444 }
1445 false
1446}
1447
1448pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1451 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1452 !matches!(
1453 cx.qpath_res(qpath, id),
1454 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1455 )
1456 }
1457
1458 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1459 i.into_iter().any(|pat| is_refutable(cx, pat))
1460 }
1461
1462 match pat.kind {
1463 PatKind::Missing => unreachable!(),
1464 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1466 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1467 PatKind::Expr(PatExpr {
1468 kind: PatExprKind::Path(qpath),
1469 hir_id,
1470 ..
1471 }) => is_qpath_refutable(cx, qpath, *hir_id),
1472 PatKind::Or(pats) => {
1473 are_refutable(cx, pats)
1475 },
1476 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1477 PatKind::Struct(ref qpath, fields, _) => {
1478 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1479 },
1480 PatKind::TupleStruct(ref qpath, pats, _) => {
1481 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1482 },
1483 PatKind::Slice(head, middle, tail) => {
1484 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1485 rustc_ty::Slice(..) => {
1486 !head.is_empty() || middle.is_none() || !tail.is_empty()
1488 },
1489 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1490 _ => {
1491 true
1493 },
1494 }
1495 },
1496 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1497 }
1498}
1499
1500pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1503 if let PatKind::Or(pats) = pat.kind {
1504 pats.iter().for_each(f);
1505 } else {
1506 f(pat);
1507 }
1508}
1509
1510pub fn is_self(slf: &Param<'_>) -> bool {
1511 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1512 name.name == kw::SelfLower
1513 } else {
1514 false
1515 }
1516}
1517
1518pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1519 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1520 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1521 {
1522 return true;
1523 }
1524 false
1525}
1526
1527pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1528 (0..decl.inputs.len()).map(move |i| &body.params[i])
1529}
1530
1531pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1534 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1535 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1536 && ddpos.as_opt_usize().is_none()
1537 && cx
1538 .qpath_res(path, arm.pat.hir_id)
1539 .ctor_parent(cx)
1540 .is_lang_item(cx, ResultOk)
1541 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1542 && arm.body.res_local_id() == Some(hir_id)
1543 {
1544 return true;
1545 }
1546 false
1547 }
1548
1549 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1550 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1551 cx.qpath_res(path, arm.pat.hir_id)
1552 .ctor_parent(cx)
1553 .is_lang_item(cx, ResultErr)
1554 } else {
1555 false
1556 }
1557 }
1558
1559 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1560 if let MatchSource::TryDesugar(_) = *source {
1562 return Some(expr);
1563 }
1564
1565 if arms.len() == 2
1566 && arms[0].guard.is_none()
1567 && arms[1].guard.is_none()
1568 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1569 {
1570 return Some(expr);
1571 }
1572 }
1573
1574 None
1575}
1576
1577pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1587 let mut suppress_lint = false;
1588
1589 for id in ids {
1590 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1591 if let Some(expectation) = lint_id {
1592 cx.fulfill_expectation(expectation);
1593 }
1594
1595 match level {
1596 Level::Allow | Level::Expect => suppress_lint = true,
1597 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1598 }
1599 }
1600
1601 suppress_lint
1602}
1603
1604pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1612 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1613}
1614
1615pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1616 while let PatKind::Ref(subpat, _) = pat.kind {
1617 pat = subpat;
1618 }
1619 pat
1620}
1621
1622pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1623 Integer::from_int_ty(&tcx, ity).size().bits()
1624}
1625
1626#[expect(clippy::cast_possible_wrap)]
1627pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1629 let amt = 128 - int_bits(tcx, ity);
1630 ((u as i128) << amt) >> amt
1631}
1632
1633#[expect(clippy::cast_sign_loss)]
1634pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1636 let amt = 128 - int_bits(tcx, ity);
1637 ((u as u128) << amt) >> amt
1638}
1639
1640pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1642 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1643 let amt = 128 - bits;
1644 (u << amt) >> amt
1645}
1646
1647pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1648 attrs.iter().any(|attr| attr.has_name(symbol))
1649}
1650
1651pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1652 find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr { .. })
1653}
1654
1655pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1656 let mut prev_enclosing_node = None;
1657 let mut enclosing_node = node;
1658 while Some(enclosing_node) != prev_enclosing_node {
1659 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1660 return true;
1661 }
1662 prev_enclosing_node = Some(enclosing_node);
1663 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1664 }
1665
1666 false
1667}
1668
1669pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1672 tcx.hir_parent_owner_iter(id)
1673 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1674 .any(|(id, _)| {
1675 find_attr!(
1676 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1677 AttributeKind::AutomaticallyDerived(..)
1678 )
1679 })
1680}
1681
1682pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1684 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1687}
1688
1689pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1694 let mut conds = Vec::new();
1695 let mut blocks: Vec<&Block<'_>> = Vec::new();
1696
1697 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1698 conds.push(cond);
1699 if let ExprKind::Block(block, _) = then.kind {
1700 blocks.push(block);
1701 } else {
1702 panic!("ExprKind::If node is not an ExprKind::Block");
1703 }
1704
1705 if let Some(else_expr) = r#else {
1706 expr = else_expr;
1707 } else {
1708 break;
1709 }
1710 }
1711
1712 if !blocks.is_empty()
1714 && let ExprKind::Block(block, _) = expr.kind
1715 {
1716 blocks.push(block);
1717 }
1718
1719 (conds, blocks)
1720}
1721
1722pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1724 if let ExprKind::Closure(&Closure {
1725 body,
1726 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1727 ..
1728 }) = expr.kind
1729 && let ExprKind::Block(
1730 Block {
1731 expr:
1732 Some(Expr {
1733 kind: ExprKind::DropTemps(inner_expr),
1734 ..
1735 }),
1736 ..
1737 },
1738 _,
1739 ) = tcx.hir_body(body).value.kind
1740 {
1741 Some(inner_expr)
1742 } else {
1743 None
1744 }
1745}
1746
1747pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1749 get_async_closure_expr(tcx, body.value)
1750}
1751
1752pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1754 let did = match expr.kind {
1755 ExprKind::Call(path, _) => {
1756 if let ExprKind::Path(ref qpath) = path.kind
1757 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1758 {
1759 Some(did)
1760 } else {
1761 None
1762 }
1763 },
1764 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1765 _ => None,
1766 };
1767
1768 did.is_some_and(|did| find_attr!(cx.tcx.get_all_attrs(did), AttributeKind::MustUse { .. }))
1769}
1770
1771fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1783 let [param] = func.params else {
1784 return false;
1785 };
1786
1787 let mut expr = func.value;
1788 loop {
1789 match expr.kind {
1790 ExprKind::Block(
1791 &Block {
1792 stmts: [],
1793 expr: Some(e),
1794 ..
1795 },
1796 _,
1797 )
1798 | ExprKind::Ret(Some(e)) => expr = e,
1799 ExprKind::Block(
1800 &Block {
1801 stmts: [stmt],
1802 expr: None,
1803 ..
1804 },
1805 _,
1806 ) => {
1807 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1808 && let ExprKind::Ret(Some(ret_val)) = e.kind
1809 {
1810 expr = ret_val;
1811 } else {
1812 return false;
1813 }
1814 },
1815 _ => return is_expr_identity_of_pat(cx, param.pat, expr, true),
1816 }
1817 }
1818}
1819
1820pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1830 if cx
1831 .typeck_results()
1832 .pat_binding_modes()
1833 .get(pat.hir_id)
1834 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
1835 {
1836 return false;
1840 }
1841
1842 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1844
1845 match (pat.kind, expr.kind) {
1846 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1847 expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1848 },
1849 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1850 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1851 },
1852 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1853 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1854 {
1855 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1856 },
1857 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1858 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1859 },
1860 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1861 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1862 {
1863 if let ExprKind::Path(ident) = &ident.kind
1865 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1866 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
1868 {
1869 true
1870 } else {
1871 false
1872 }
1873 },
1874 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
1875 if field_pats.len() == fields.len() =>
1876 {
1877 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1879 && unordered_over(field_pats, fields, |field_pat, field| {
1881 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
1882 })
1883 },
1884 _ => false,
1885 }
1886}
1887
1888pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1893 match expr.kind {
1894 ExprKind::Closure(&Closure { body, fn_decl, .. })
1895 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1896 {
1897 is_body_identity_function(cx, cx.tcx.hir_body(body))
1898 },
1899 ExprKind::Path(QPath::Resolved(_, path))
1900 if path.segments.iter().all(|seg| seg.infer_args)
1901 && let Some(did) = path.res.opt_def_id() =>
1902 {
1903 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1904 },
1905 _ => false,
1906 }
1907}
1908
1909pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1918 match expr.kind {
1919 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1920 _ => expr.basic_res().is_diag_item(cx, sym::convert_identity),
1921 }
1922}
1923
1924pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
1927 let mut child_id = expr.hir_id;
1928 let mut iter = tcx.hir_parent_iter(child_id);
1929 loop {
1930 match iter.next() {
1931 None => break None,
1932 Some((id, Node::Block(_))) => child_id = id,
1933 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
1934 Some((_, Node::Expr(expr))) => match expr.kind {
1935 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
1936 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
1937 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
1938 _ => break Some((Node::Expr(expr), child_id)),
1939 },
1940 Some((_, node)) => break Some((node, child_id)),
1941 }
1942 }
1943}
1944
1945pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1947 !matches!(
1948 get_expr_use_or_unification_node(tcx, expr),
1949 None | Some((
1950 Node::Stmt(Stmt {
1951 kind: StmtKind::Expr(_)
1952 | StmtKind::Semi(_)
1953 | StmtKind::Let(LetStmt {
1954 pat: Pat {
1955 kind: PatKind::Wild,
1956 ..
1957 },
1958 ..
1959 }),
1960 ..
1961 }),
1962 _
1963 ))
1964 )
1965}
1966
1967pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1969 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
1970}
1971
1972pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1976 !expr.is_place_expr(|base| {
1977 cx.typeck_results()
1978 .adjustments()
1979 .get(base.hir_id)
1980 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
1981 })
1982}
1983
1984pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
1985 if !is_no_std_crate(cx) {
1986 Some("std")
1987 } else if !is_no_core_crate(cx) {
1988 Some("core")
1989 } else {
1990 None
1991 }
1992}
1993
1994pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
1995 find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoStd(..))
1996}
1997
1998pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
1999 find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoCore(..))
2000}
2001
2002pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2012 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2013 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2014 } else {
2015 false
2016 }
2017}
2018
2019pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2029 use rustc_trait_selection::traits;
2030 let predicates = cx
2031 .tcx
2032 .predicates_of(did)
2033 .predicates
2034 .iter()
2035 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2036 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2037}
2038
2039pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2041 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2042}
2043
2044pub fn fn_def_id_with_node_args<'tcx>(
2047 cx: &LateContext<'tcx>,
2048 expr: &Expr<'_>,
2049) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2050 let typeck = cx.typeck_results();
2051 match &expr.kind {
2052 ExprKind::MethodCall(..) => Some((
2053 typeck.type_dependent_def_id(expr.hir_id)?,
2054 typeck.node_args(expr.hir_id),
2055 )),
2056 ExprKind::Call(
2057 Expr {
2058 kind: ExprKind::Path(qpath),
2059 hir_id: path_hir_id,
2060 ..
2061 },
2062 ..,
2063 ) => {
2064 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2067 typeck.qpath_res(qpath, *path_hir_id)
2068 {
2069 Some((id, typeck.node_args(*path_hir_id)))
2070 } else {
2071 None
2072 }
2073 },
2074 _ => None,
2075 }
2076}
2077
2078pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2083 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2084 let expr_kind = expr_type.kind();
2085 let is_primitive = match expr_kind {
2086 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2087 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2088 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2089 is_recursively_primitive_type(*element_type)
2090 } else {
2091 unreachable!()
2092 }
2093 },
2094 _ => false,
2095 };
2096
2097 if is_primitive {
2098 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2101 rustc_ty::Slice(..) => return Some("slice".into()),
2102 rustc_ty::Array(..) => return Some("array".into()),
2103 rustc_ty::Tuple(..) => return Some("tuple".into()),
2104 _ => {
2105 let refs_peeled = expr_type.peel_refs();
2108 return Some(refs_peeled.walk().last().unwrap().to_string());
2109 },
2110 }
2111 }
2112 None
2113}
2114
2115pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2123where
2124 Hash: FnMut(&T) -> u64,
2125 Eq: FnMut(&T, &T) -> bool,
2126{
2127 match exprs {
2128 [a, b] if eq(a, b) => return vec![vec![a, b]],
2129 _ if exprs.len() <= 2 => return vec![],
2130 _ => {},
2131 }
2132
2133 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2134
2135 for expr in exprs {
2136 match buckets.entry(hash(expr)) {
2137 indexmap::map::Entry::Occupied(mut o) => {
2138 let bucket = o.get_mut();
2139 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2140 Some(group) => group.push(expr),
2141 None => bucket.push(vec![expr]),
2142 }
2143 },
2144 indexmap::map::Entry::Vacant(v) => {
2145 v.insert(vec![vec![expr]]);
2146 },
2147 }
2148 }
2149
2150 buckets
2151 .into_values()
2152 .flatten()
2153 .filter(|group| group.len() > 1)
2154 .collect()
2155}
2156
2157pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2160 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2161 if let PatKind::Ref(pat, _) = pat.kind {
2162 peel(pat, count + 1)
2163 } else {
2164 (pat, count)
2165 }
2166 }
2167 peel(pat, 0)
2168}
2169
2170pub fn peel_hir_expr_while<'tcx>(
2172 mut expr: &'tcx Expr<'tcx>,
2173 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2174) -> &'tcx Expr<'tcx> {
2175 while let Some(e) = f(expr) {
2176 expr = e;
2177 }
2178 expr
2179}
2180
2181pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2184 let mut remaining = count;
2185 let e = peel_hir_expr_while(expr, |e| match e.kind {
2186 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2187 remaining -= 1;
2188 Some(e)
2189 },
2190 _ => None,
2191 });
2192 (e, count - remaining)
2193}
2194
2195pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2198 let mut count: usize = 0;
2199 let mut curr_expr = expr;
2200 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2201 count = count.wrapping_add(1);
2202 curr_expr = local_expr;
2203 }
2204 (curr_expr, count)
2205}
2206
2207pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2210 let mut count = 0;
2211 let e = peel_hir_expr_while(expr, |e| match e.kind {
2212 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2213 count += 1;
2214 Some(e)
2215 },
2216 _ => None,
2217 });
2218 (e, count)
2219}
2220
2221pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2224 let mut count = 0;
2225 loop {
2226 match &ty.kind {
2227 TyKind::Ref(_, ref_ty) => {
2228 ty = ref_ty.ty;
2229 count += 1;
2230 },
2231 _ => break (ty, count),
2232 }
2233 }
2234}
2235
2236pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
2238 match &ty.kind {
2239 TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty),
2240 _ => ty,
2241 }
2242}
2243
2244pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2247 loop {
2248 match expr.kind {
2249 ExprKind::AddrOf(_, _, e) => expr = e,
2250 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2251 _ => break,
2252 }
2253 }
2254 expr
2255}
2256
2257pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2260 let mut operators = Vec::new();
2261 peel_hir_expr_while(expr, |expr| match expr.kind {
2262 ExprKind::AddrOf(_, _, e) => {
2263 operators.push(expr);
2264 Some(e)
2265 },
2266 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2267 operators.push(expr);
2268 Some(e)
2269 },
2270 _ => None,
2271 });
2272 operators
2273}
2274
2275pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2276 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2277 && let Res::Def(_, def_id) = path.res
2278 {
2279 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2280 }
2281 false
2282}
2283
2284static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2285
2286fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2289 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2290 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2291 let value = map.entry(module);
2292 match value {
2293 Entry::Occupied(entry) => f(entry.get()),
2294 Entry::Vacant(entry) => {
2295 let mut names = Vec::new();
2296 for id in tcx.hir_module_free_items(module) {
2297 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2298 && let item = tcx.hir_item(id)
2299 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2300 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2301 && let Res::Def(DefKind::Struct, _) = path.res
2303 {
2304 let has_test_marker = tcx
2305 .hir_attrs(item.hir_id())
2306 .iter()
2307 .any(|a| a.has_name(sym::rustc_test_marker));
2308 if has_test_marker {
2309 names.push(ident.name);
2310 }
2311 }
2312 }
2313 names.sort_unstable();
2314 f(entry.insert(names))
2315 },
2316 }
2317}
2318
2319pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2323 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2324 let node = tcx.hir_node(id);
2325 once((id, node))
2326 .chain(tcx.hir_parent_iter(id))
2327 .any(|(_id, node)| {
2330 if let Node::Item(item) = node
2331 && let ItemKind::Fn { ident, .. } = item.kind
2332 {
2333 return names.binary_search(&ident.name).is_ok();
2336 }
2337 false
2338 })
2339 })
2340}
2341
2342pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2349 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2350 if let Node::Item(item) = tcx.hir_node(id)
2351 && let ItemKind::Fn { ident, .. } = item.kind
2352 {
2353 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2354 names.binary_search(&ident.name).is_ok()
2355 })
2356 } else {
2357 false
2358 }
2359}
2360
2361pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2366 tcx.hir_attrs(id).iter().any(|attr| {
2367 if attr.has_name(sym::cfg_trace)
2368 && let Some(items) = attr.meta_item_list()
2369 && let [item] = &*items
2370 && item.has_name(sym::test)
2371 {
2372 true
2373 } else {
2374 false
2375 }
2376 })
2377}
2378
2379pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2381 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2382}
2383
2384pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2386 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2387}
2388
2389pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2391 tcx.has_attr(def_id, sym::cfg_trace)
2392 || tcx
2393 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2394 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2395 .any(|attr| attr.has_name(sym::cfg_trace))
2396}
2397
2398pub fn walk_to_expr_usage<'tcx, T>(
2409 cx: &LateContext<'tcx>,
2410 e: &Expr<'tcx>,
2411 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2412) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2413 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2414 let mut child_id = e.hir_id;
2415
2416 while let Some((parent_id, parent)) = iter.next() {
2417 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2418 return Some(ControlFlow::Break(x));
2419 }
2420 let parent_expr = match parent {
2421 Node::Expr(e) => e,
2422 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2423 child_id = parent_id;
2424 continue;
2425 },
2426 Node::Arm(a) if a.body.hir_id == child_id => {
2427 child_id = parent_id;
2428 continue;
2429 },
2430 _ => return Some(ControlFlow::Continue((parent, child_id))),
2431 };
2432 match parent_expr.kind {
2433 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2434 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2435 child_id = id;
2436 iter = cx.tcx.hir_parent_iter(id);
2437 },
2438 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2439 _ => return Some(ControlFlow::Continue((parent, child_id))),
2440 }
2441 }
2442 debug_assert!(false, "no parent node found for `{child_id:?}`");
2443 None
2444}
2445
2446#[derive(Clone, Copy)]
2448pub enum DefinedTy<'tcx> {
2449 Hir(&'tcx hir::Ty<'tcx>),
2451 Mir {
2459 def_site_def_id: Option<DefId>,
2460 ty: Binder<'tcx, Ty<'tcx>>,
2461 },
2462}
2463
2464pub struct ExprUseCtxt<'tcx> {
2466 pub node: Node<'tcx>,
2468 pub child_id: HirId,
2470 pub adjustments: &'tcx [Adjustment<'tcx>],
2472 pub is_ty_unified: bool,
2474 pub moved_before_use: bool,
2476 pub same_ctxt: bool,
2478}
2479impl<'tcx> ExprUseCtxt<'tcx> {
2480 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2481 match self.node {
2482 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2483 Node::ExprField(field) => ExprUseNode::Field(field),
2484
2485 Node::Item(&Item {
2486 kind: ItemKind::Static(..) | ItemKind::Const(..),
2487 owner_id,
2488 ..
2489 })
2490 | Node::TraitItem(&TraitItem {
2491 kind: TraitItemKind::Const(..),
2492 owner_id,
2493 ..
2494 })
2495 | Node::ImplItem(&ImplItem {
2496 kind: ImplItemKind::Const(..),
2497 owner_id,
2498 ..
2499 }) => ExprUseNode::ConstStatic(owner_id),
2500
2501 Node::Item(&Item {
2502 kind: ItemKind::Fn { .. },
2503 owner_id,
2504 ..
2505 })
2506 | Node::TraitItem(&TraitItem {
2507 kind: TraitItemKind::Fn(..),
2508 owner_id,
2509 ..
2510 })
2511 | Node::ImplItem(&ImplItem {
2512 kind: ImplItemKind::Fn(..),
2513 owner_id,
2514 ..
2515 }) => ExprUseNode::Return(owner_id),
2516
2517 Node::Expr(use_expr) => match use_expr.kind {
2518 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2519 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2520 }),
2521
2522 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2523 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2524 Some(i) => ExprUseNode::FnArg(func, i),
2525 None => ExprUseNode::Callee,
2526 },
2527 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2528 use_expr.hir_id,
2529 name.args,
2530 args.iter()
2531 .position(|arg| arg.hir_id == self.child_id)
2532 .map_or(0, |i| i + 1),
2533 ),
2534 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2535 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2536 _ => ExprUseNode::Other,
2537 },
2538 _ => ExprUseNode::Other,
2539 }
2540 }
2541}
2542
2543pub enum ExprUseNode<'tcx> {
2545 LetStmt(&'tcx LetStmt<'tcx>),
2547 ConstStatic(OwnerId),
2549 Return(OwnerId),
2551 Field(&'tcx ExprField<'tcx>),
2553 FnArg(&'tcx Expr<'tcx>, usize),
2555 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2557 Callee,
2559 FieldAccess(Ident),
2561 AddrOf(ast::BorrowKind, Mutability),
2563 Other,
2564}
2565impl<'tcx> ExprUseNode<'tcx> {
2566 pub fn is_return(&self) -> bool {
2568 matches!(self, Self::Return(_))
2569 }
2570
2571 pub fn is_recv(&self) -> bool {
2573 matches!(self, Self::MethodArg(_, _, 0))
2574 }
2575
2576 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2578 match *self {
2579 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2580 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2581 def_site_def_id: Some(id.def_id.to_def_id()),
2582 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2583 }),
2584 Self::Return(id) => {
2585 if let Node::Expr(Expr {
2586 kind: ExprKind::Closure(c),
2587 ..
2588 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2589 {
2590 match c.fn_decl.output {
2591 FnRetTy::DefaultReturn(_) => None,
2592 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2593 }
2594 } else {
2595 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2596 Some(DefinedTy::Mir {
2597 def_site_def_id: Some(id.def_id.to_def_id()),
2598 ty,
2599 })
2600 }
2601 },
2602 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2603 Some(Expr {
2604 hir_id,
2605 kind: ExprKind::Struct(path, ..),
2606 ..
2607 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2608 .and_then(|(adt, variant)| {
2609 variant
2610 .fields
2611 .iter()
2612 .find(|f| f.name == field.ident.name)
2613 .map(|f| (adt, f))
2614 })
2615 .map(|(adt, field_def)| DefinedTy::Mir {
2616 def_site_def_id: Some(adt.did()),
2617 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2618 }),
2619 _ => None,
2620 },
2621 Self::FnArg(callee, i) => {
2622 let sig = expr_sig(cx, callee)?;
2623 let (hir_ty, ty) = sig.input_with_hir(i)?;
2624 Some(match hir_ty {
2625 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2626 None => DefinedTy::Mir {
2627 def_site_def_id: sig.predicates_id(),
2628 ty,
2629 },
2630 })
2631 },
2632 Self::MethodArg(id, _, i) => {
2633 let id = cx.typeck_results().type_dependent_def_id(id)?;
2634 let sig = cx.tcx.fn_sig(id).skip_binder();
2635 Some(DefinedTy::Mir {
2636 def_site_def_id: Some(id),
2637 ty: sig.input(i),
2638 })
2639 },
2640 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2641 }
2642 }
2643}
2644
2645pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2647 let mut adjustments = [].as_slice();
2648 let mut is_ty_unified = false;
2649 let mut moved_before_use = false;
2650 let mut same_ctxt = true;
2651 let ctxt = e.span.ctxt();
2652 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2653 if adjustments.is_empty()
2654 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2655 {
2656 adjustments = cx.typeck_results().expr_adjustments(e);
2657 }
2658 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2659 if let Node::Expr(e) = parent {
2660 match e.kind {
2661 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2662 is_ty_unified = true;
2663 moved_before_use = true;
2664 },
2665 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2666 is_ty_unified = true;
2667 moved_before_use = true;
2668 },
2669 ExprKind::Block(..) => moved_before_use = true,
2670 _ => {},
2671 }
2672 }
2673 ControlFlow::Continue(())
2674 });
2675 match node {
2676 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2677 node,
2678 child_id,
2679 adjustments,
2680 is_ty_unified,
2681 moved_before_use,
2682 same_ctxt,
2683 },
2684 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
2685 None => ExprUseCtxt {
2686 node: Node::Crate(cx.tcx.hir_root_module()),
2687 child_id: HirId::INVALID,
2688 adjustments: &[],
2689 is_ty_unified: true,
2690 moved_before_use: true,
2691 same_ctxt: false,
2692 },
2693 }
2694}
2695
2696pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2698 let mut pos = 0;
2699 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2700 let end = pos + t.len;
2701 let range = pos as usize..end as usize;
2702 let inner = InnerSpan::new(range.start, range.end);
2703 pos = end;
2704 (t.kind, s.get(range).unwrap_or_default(), inner)
2705 })
2706}
2707
2708pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2711 let Ok(snippet) = sm.span_to_snippet(span) else {
2712 return false;
2713 };
2714 return tokenize(&snippet, FrontmatterAllowed::No).any(|token| {
2715 matches!(
2716 token.kind,
2717 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2718 )
2719 });
2720}
2721
2722pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2727 matches!(span.get_source_text(cx), Some(snippet) if tokenize_with_text(&snippet).any(|(token, _, _)|
2728 match token {
2729 TokenKind::Whitespace => false,
2730 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2731 _ => true,
2732 }
2733 ))
2734}
2735pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2739 span_extract_comments(sm, span).join("\n")
2740}
2741
2742pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2746 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2747 tokenize_with_text(&snippet)
2748 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2749 .map(|(_, s, _)| s.to_string())
2750 .collect::<Vec<_>>()
2751}
2752
2753pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2754 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2755}
2756
2757pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2782 cx: &LateContext<'_>,
2783 pat: &'a Pat<'hir>,
2784 else_body: &Expr<'_>,
2785) -> Option<&'a Pat<'hir>> {
2786 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
2787 && cx
2788 .qpath_res(&pat_path, pat.hir_id)
2789 .ctor_parent(cx)
2790 .is_lang_item(cx, OptionSome)
2791 && !is_refutable(cx, inner_pat)
2792 && let else_body = peel_blocks(else_body)
2793 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2794 && let ExprKind::Path(ret_path) = ret_val.kind
2795 && cx
2796 .qpath_res(&ret_path, ret_val.hir_id)
2797 .ctor_parent(cx)
2798 .is_lang_item(cx, OptionNone)
2799 {
2800 Some(inner_pat)
2801 } else {
2802 None
2803 }
2804}
2805
2806macro_rules! op_utils {
2807 ($($name:ident $assign:ident)*) => {
2808 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2810
2811 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2813
2814 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2816 match kind {
2817 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2818 _ => None,
2819 }
2820 }
2821 };
2822}
2823
2824op_utils! {
2825 Add AddAssign
2826 Sub SubAssign
2827 Mul MulAssign
2828 Div DivAssign
2829 Rem RemAssign
2830 BitXor BitXorAssign
2831 BitAnd BitAndAssign
2832 BitOr BitOrAssign
2833 Shl ShlAssign
2834 Shr ShrAssign
2835}
2836
2837pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2840 match *pat {
2841 PatKind::Wild => true,
2842 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2843 !visitors::is_local_used(cx, body, id)
2844 },
2845 _ => false,
2846 }
2847}
2848
2849#[derive(Clone, Copy)]
2850pub enum RequiresSemi {
2851 Yes,
2852 No,
2853}
2854impl RequiresSemi {
2855 pub fn requires_semi(self) -> bool {
2856 matches!(self, Self::Yes)
2857 }
2858}
2859
2860#[expect(clippy::too_many_lines)]
2863pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2864 struct BreakTarget {
2865 id: HirId,
2866 unused: bool,
2867 }
2868
2869 struct V<'cx, 'tcx> {
2870 cx: &'cx LateContext<'tcx>,
2871 break_targets: Vec<BreakTarget>,
2872 break_targets_for_result_ty: u32,
2873 in_final_expr: bool,
2874 requires_semi: bool,
2875 is_never: bool,
2876 }
2877
2878 impl V<'_, '_> {
2879 fn push_break_target(&mut self, id: HirId) {
2880 self.break_targets.push(BreakTarget { id, unused: true });
2881 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2882 }
2883 }
2884
2885 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
2886 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
2887 if self.is_never && self.break_targets.is_empty() {
2904 if self.in_final_expr && !self.requires_semi {
2905 match e.kind {
2908 ExprKind::DropTemps(e) => self.visit_expr(e),
2909 ExprKind::If(_, then, Some(else_)) => {
2910 self.visit_expr(then);
2911 self.visit_expr(else_);
2912 },
2913 ExprKind::Match(_, arms, _) => {
2914 for arm in arms {
2915 self.visit_expr(arm.body);
2916 }
2917 },
2918 ExprKind::Loop(b, ..) => {
2919 self.push_break_target(e.hir_id);
2920 self.in_final_expr = false;
2921 self.visit_block(b);
2922 self.break_targets.pop();
2923 },
2924 ExprKind::Block(b, _) => {
2925 if b.targeted_by_break {
2926 self.push_break_target(b.hir_id);
2927 self.visit_block(b);
2928 self.break_targets.pop();
2929 } else {
2930 self.visit_block(b);
2931 }
2932 },
2933 _ => {
2934 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
2935 },
2936 }
2937 }
2938 return;
2939 }
2940 match e.kind {
2941 ExprKind::DropTemps(e) => self.visit_expr(e),
2942 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
2943 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
2944 self.in_final_expr = false;
2945 self.visit_expr(e);
2946 self.is_never = true;
2947 },
2948 ExprKind::Break(dest, e) => {
2949 if let Some(e) = e {
2950 self.in_final_expr = false;
2951 self.visit_expr(e);
2952 }
2953 if let Ok(id) = dest.target_id
2954 && let Some((i, target)) = self
2955 .break_targets
2956 .iter_mut()
2957 .enumerate()
2958 .find(|(_, target)| target.id == id)
2959 {
2960 target.unused &= self.is_never;
2961 if i < self.break_targets_for_result_ty as usize {
2962 self.requires_semi = true;
2963 }
2964 }
2965 self.is_never = true;
2966 },
2967 ExprKind::If(cond, then, else_) => {
2968 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
2969 self.visit_expr(cond);
2970 self.in_final_expr = in_final_expr;
2971
2972 if self.is_never {
2973 self.visit_expr(then);
2974 if let Some(else_) = else_ {
2975 self.visit_expr(else_);
2976 }
2977 } else {
2978 self.visit_expr(then);
2979 let is_never = mem::replace(&mut self.is_never, false);
2980 if let Some(else_) = else_ {
2981 self.visit_expr(else_);
2982 self.is_never &= is_never;
2983 }
2984 }
2985 },
2986 ExprKind::Match(scrutinee, arms, _) => {
2987 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
2988 self.visit_expr(scrutinee);
2989 self.in_final_expr = in_final_expr;
2990
2991 if self.is_never {
2992 for arm in arms {
2993 self.visit_arm(arm);
2994 }
2995 } else {
2996 let mut is_never = true;
2997 for arm in arms {
2998 self.is_never = false;
2999 if let Some(guard) = arm.guard {
3000 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3001 self.visit_expr(guard);
3002 self.in_final_expr = in_final_expr;
3003 self.is_never = false;
3005 }
3006 self.visit_expr(arm.body);
3007 is_never &= self.is_never;
3008 }
3009 self.is_never = is_never;
3010 }
3011 },
3012 ExprKind::Loop(b, _, _, _) => {
3013 self.push_break_target(e.hir_id);
3014 self.in_final_expr = false;
3015 self.visit_block(b);
3016 self.is_never = self.break_targets.pop().unwrap().unused;
3017 },
3018 ExprKind::Block(b, _) => {
3019 if b.targeted_by_break {
3020 self.push_break_target(b.hir_id);
3021 self.visit_block(b);
3022 self.is_never &= self.break_targets.pop().unwrap().unused;
3023 } else {
3024 self.visit_block(b);
3025 }
3026 },
3027 _ => {
3028 self.in_final_expr = false;
3029 walk_expr(self, e);
3030 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3031 },
3032 }
3033 }
3034
3035 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3036 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3037 for s in b.stmts {
3038 self.visit_stmt(s);
3039 }
3040 self.in_final_expr = in_final_expr;
3041 if let Some(e) = b.expr {
3042 self.visit_expr(e);
3043 }
3044 }
3045
3046 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3047 if let Some(e) = l.init {
3048 self.visit_expr(e);
3049 }
3050 if let Some(else_) = l.els {
3051 let is_never = self.is_never;
3052 self.visit_block(else_);
3053 self.is_never = is_never;
3054 }
3055 }
3056
3057 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3058 if let Some(guard) = arm.guard {
3059 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3060 self.visit_expr(guard);
3061 self.in_final_expr = in_final_expr;
3062 }
3063 self.visit_expr(arm.body);
3064 }
3065 }
3066
3067 if cx.typeck_results().expr_ty(e).is_never() {
3068 Some(RequiresSemi::No)
3069 } else if let ExprKind::Block(b, _) = e.kind
3070 && !b.targeted_by_break
3071 && b.expr.is_none()
3072 {
3073 None
3075 } else {
3076 let mut v = V {
3077 cx,
3078 break_targets: Vec::new(),
3079 break_targets_for_result_ty: 0,
3080 in_final_expr: true,
3081 requires_semi: false,
3082 is_never: false,
3083 };
3084 v.visit_expr(e);
3085 v.is_never
3086 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3087 RequiresSemi::Yes
3088 } else {
3089 RequiresSemi::No
3090 })
3091 }
3092}
3093
3094pub fn get_path_from_caller_to_method_type<'tcx>(
3100 tcx: TyCtxt<'tcx>,
3101 from: LocalDefId,
3102 method: DefId,
3103 args: GenericArgsRef<'tcx>,
3104) -> String {
3105 let assoc_item = tcx.associated_item(method);
3106 let def_id = assoc_item.container_id(tcx);
3107 match assoc_item.container {
3108 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3109 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3110 let ty = tcx.type_of(def_id).instantiate_identity();
3111 get_path_to_ty(tcx, from, ty, args)
3112 },
3113 }
3114}
3115
3116fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3117 match ty.kind() {
3118 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3119 rustc_ty::Array(..)
3121 | rustc_ty::Dynamic(..)
3122 | rustc_ty::Never
3123 | rustc_ty::RawPtr(_, _)
3124 | rustc_ty::Ref(..)
3125 | rustc_ty::Slice(_)
3126 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3127 _ => ty.to_string(),
3128 }
3129}
3130
3131fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3133 if callee.is_local() {
3135 let callee_path = tcx.def_path(callee);
3136 let caller_path = tcx.def_path(from.to_def_id());
3137 maybe_get_relative_path(&caller_path, &callee_path, 2)
3138 } else {
3139 tcx.def_path_str(callee)
3140 }
3141}
3142
3143fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3156 use itertools::EitherOrBoth::{Both, Left, Right};
3157
3158 let unique_parts = to
3160 .data
3161 .iter()
3162 .zip_longest(from.data.iter())
3163 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3164 .map(|el| match el {
3165 Both(l, r) => Both(l.data, r.data),
3166 Left(l) => Left(l.data),
3167 Right(r) => Right(r.data),
3168 });
3169
3170 let mut go_up_by = 0;
3172 let mut path = Vec::new();
3173 for el in unique_parts {
3174 match el {
3175 Both(l, r) => {
3176 if let DefPathData::TypeNs(sym) = l {
3186 path.push(sym);
3187 }
3188 if let DefPathData::TypeNs(_) = r {
3189 go_up_by += 1;
3190 }
3191 },
3192 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3197 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3202 _ => {},
3203 }
3204 }
3205
3206 if go_up_by > max_super {
3207 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3209 if let DefPathData::TypeNs(sym) = el.data {
3210 Some(sym)
3211 } else {
3212 None
3213 }
3214 })))
3215 } else {
3216 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3217 }
3218}
3219
3220pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3223 matches!(
3224 cx.tcx.parent_hir_node(id),
3225 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3226 )
3227}
3228
3229pub fn is_block_like(expr: &Expr<'_>) -> bool {
3232 matches!(
3233 expr.kind,
3234 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3235 )
3236}
3237
3238pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3240 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3241 match expr.kind {
3242 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3243 _ if is_block_like(expr) => is_operand,
3244 _ => false,
3245 }
3246 }
3247
3248 contains_block(expr, false)
3249}
3250
3251pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3253 if let Some(parent_expr) = get_parent_expr(cx, expr)
3254 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3255 && receiver.hir_id == expr.hir_id
3256 {
3257 return true;
3258 }
3259 false
3260}
3261
3262pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3265 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3266 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3267 && temporary_ty
3268 .walk()
3269 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3270 {
3271 ControlFlow::Break(())
3272 } else {
3273 ControlFlow::Continue(())
3274 }
3275 })
3276 .is_break()
3277}
3278
3279pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3290 let expr_ty_is_adjusted = cx
3291 .typeck_results()
3292 .expr_adjustments(expr)
3293 .iter()
3294 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3296 if expr_ty_is_adjusted {
3297 return true;
3298 }
3299
3300 match expr.kind {
3303 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3304 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3305
3306 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3307 return false;
3308 }
3309
3310 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3311 let mut args_with_ty_param = {
3312 fn_sig
3313 .inputs()
3314 .skip_binder()
3315 .iter()
3316 .skip(self_arg_count)
3317 .zip(args)
3318 .filter_map(|(arg_ty, arg)| {
3319 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3320 Some(arg)
3321 } else {
3322 None
3323 }
3324 })
3325 };
3326 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3327 },
3328 ExprKind::Struct(qpath, _, _) => {
3330 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3331 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3332 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3333 return true;
3335 };
3336 v_def
3337 .fields
3338 .iter()
3339 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3340 } else {
3341 false
3342 }
3343 },
3344 ExprKind::Block(
3346 &Block {
3347 expr: Some(ret_expr), ..
3348 },
3349 _,
3350 )
3351 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3352
3353 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3355 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3357 ExprKind::If(_, then, maybe_else) => {
3359 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3360 },
3361 ExprKind::Match(_, arms, _) => arms
3362 .iter()
3363 .map(|arm| arm.body)
3364 .any(|body| expr_requires_coercion(cx, body)),
3365 _ => false,
3366 }
3367}
3368
3369pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3372 if let Some(hir_id) = expr.res_local_id()
3373 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3374 {
3375 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3376 } else if let ExprKind::Path(p) = &expr.kind
3377 && let Some(mutability) = cx
3378 .qpath_res(p, expr.hir_id)
3379 .opt_def_id()
3380 .and_then(|id| cx.tcx.static_mutability(id))
3381 {
3382 mutability == Mutability::Mut
3383 } else if let ExprKind::Field(parent, _) = expr.kind {
3384 is_mutable(cx, parent)
3385 } else {
3386 true
3387 }
3388}
3389
3390pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3393 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3394 return hir_ty;
3395 };
3396 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3397 && let Some(segment) = path.segments.last()
3398 && segment.ident.name == sym::Option
3399 && let Res::Def(DefKind::Enum, def_id) = segment.res
3400 && def_id == option_def_id
3401 && let [GenericArg::Type(arg_ty)] = segment.args().args
3402 {
3403 hir_ty = arg_ty.as_unambig_ty();
3404 }
3405 hir_ty
3406}
3407
3408pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3411 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3412 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3413 && let ctxt = expr.span.ctxt()
3414 && for_each_expr_without_closures(into_future_arg, |e| {
3415 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3416 })
3417 .is_none()
3418 {
3419 Some(into_future_arg)
3420 } else {
3421 None
3422 }
3423}
3424
3425pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3427 if let ExprKind::Call(fn_expr, []) = &expr.kind
3428 && let ExprKind::Path(qpath) = &fn_expr.kind
3429 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3430 {
3431 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3432 } else {
3433 false
3434 }
3435}
3436
3437pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3454 let enclosing_body_owner = cx
3455 .tcx
3456 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3457 let mut prev_id = expr.hir_id;
3458 let mut skip_until_id = None;
3459 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3460 if hir_id == enclosing_body_owner {
3461 return true;
3462 }
3463 if let Some(id) = skip_until_id {
3464 prev_id = hir_id;
3465 if id == hir_id {
3466 skip_until_id = None;
3467 }
3468 continue;
3469 }
3470 match node {
3471 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3472 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3473 Node::Expr(expr) => match expr.kind {
3474 ExprKind::Ret(_) => return true,
3475 ExprKind::If(_, then, opt_else)
3476 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3477 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3478 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3479 ExprKind::Break(
3480 Destination {
3481 target_id: Ok(target_id),
3482 ..
3483 },
3484 _,
3485 ) => skip_until_id = Some(target_id),
3486 _ => break,
3487 },
3488 _ => break,
3489 }
3490 prev_id = hir_id;
3491 }
3492
3493 false
3496}
3497
3498pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3501 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3502 matches!(
3503 adj.kind,
3504 Adjust::Deref(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny
3505 )
3506 })
3507}
3508
3509pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3511 matches!(
3512 expr.kind,
3513 ExprKind::Closure(Closure {
3514 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3515 CoroutineDesugaring::Async,
3516 CoroutineSource::Block
3517 )),
3518 ..
3519 })
3520 )
3521}
3522
3523pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3525 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3526}