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