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