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