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