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::{LitIntType, 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, CfgEntry};
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_integer_literal_untyped(expr: &Expr<'_>) -> bool {
1390 if let ExprKind::Lit(spanned) = expr.kind
1391 && let LitKind::Int(_, suffix) = spanned.node
1392 {
1393 return suffix == LitIntType::Unsuffixed;
1394 }
1395
1396 false
1397}
1398
1399pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1401 if let ExprKind::Lit(spanned) = expr.kind
1402 && let LitKind::Float(v, _) = spanned.node
1403 {
1404 v.as_str().parse() == Ok(value)
1405 } else {
1406 false
1407 }
1408}
1409
1410pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1418 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1419}
1420
1421#[must_use]
1425pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1426 loop {
1427 if span.from_expansion() {
1428 let data = span.ctxt().outer_expn_data();
1429 let new_span = data.call_site;
1430
1431 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1432 && mac_name == name
1433 {
1434 return Some(new_span);
1435 }
1436
1437 span = new_span;
1438 } else {
1439 return None;
1440 }
1441 }
1442}
1443
1444#[must_use]
1455pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1456 if span.from_expansion() {
1457 let data = span.ctxt().outer_expn_data();
1458 let new_span = data.call_site;
1459
1460 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1461 && mac_name == name
1462 {
1463 return Some(new_span);
1464 }
1465 }
1466
1467 None
1468}
1469
1470pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1472 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1473 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1474}
1475
1476pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1478 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1479 cx.tcx.instantiate_bound_regions_with_erased(arg)
1480}
1481
1482pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1484 if let ExprKind::Call(fun, _) = expr.kind
1485 && let ExprKind::Path(ref qp) = fun.kind
1486 {
1487 let res = cx.qpath_res(qp, fun.hir_id);
1488 return match res {
1489 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1490 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1491 _ => false,
1492 };
1493 }
1494 false
1495}
1496
1497pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1500 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1501 !matches!(
1502 cx.qpath_res(qpath, id),
1503 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1504 )
1505 }
1506
1507 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1508 i.into_iter().any(|pat| is_refutable(cx, pat))
1509 }
1510
1511 match pat.kind {
1512 PatKind::Missing => unreachable!(),
1513 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1515 PatKind::Box(pat) | PatKind::Ref(pat, _, _) => is_refutable(cx, pat),
1516 PatKind::Expr(PatExpr {
1517 kind: PatExprKind::Path(qpath),
1518 hir_id,
1519 ..
1520 }) => is_qpath_refutable(cx, qpath, *hir_id),
1521 PatKind::Or(pats) => {
1522 are_refutable(cx, pats)
1524 },
1525 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1526 PatKind::Struct(ref qpath, fields, _) => {
1527 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1528 },
1529 PatKind::TupleStruct(ref qpath, pats, _) => {
1530 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1531 },
1532 PatKind::Slice(head, middle, tail) => {
1533 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1534 rustc_ty::Slice(..) => {
1535 !head.is_empty() || middle.is_none() || !tail.is_empty()
1537 },
1538 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1539 _ => {
1540 true
1542 },
1543 }
1544 },
1545 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1546 }
1547}
1548
1549pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1552 if let PatKind::Or(pats) = pat.kind {
1553 pats.iter().for_each(f);
1554 } else {
1555 f(pat);
1556 }
1557}
1558
1559pub fn is_self(slf: &Param<'_>) -> bool {
1560 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1561 name.name == kw::SelfLower
1562 } else {
1563 false
1564 }
1565}
1566
1567pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1568 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1569 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1570 {
1571 return true;
1572 }
1573 false
1574}
1575
1576pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1577 (0..decl.inputs.len()).map(move |i| &body.params[i])
1578}
1579
1580pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1583 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1584 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1585 && ddpos.as_opt_usize().is_none()
1586 && cx
1587 .qpath_res(path, arm.pat.hir_id)
1588 .ctor_parent(cx)
1589 .is_lang_item(cx, ResultOk)
1590 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1591 && arm.body.res_local_id() == Some(hir_id)
1592 {
1593 return true;
1594 }
1595 false
1596 }
1597
1598 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1599 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1600 cx.qpath_res(path, arm.pat.hir_id)
1601 .ctor_parent(cx)
1602 .is_lang_item(cx, ResultErr)
1603 } else {
1604 false
1605 }
1606 }
1607
1608 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1609 if let MatchSource::TryDesugar(_) = *source {
1611 return Some(expr);
1612 }
1613
1614 if arms.len() == 2
1615 && arms[0].guard.is_none()
1616 && arms[1].guard.is_none()
1617 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1618 {
1619 return Some(expr);
1620 }
1621 }
1622
1623 None
1624}
1625
1626pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1636 let mut suppress_lint = false;
1637
1638 for id in ids {
1639 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1640 if let Some(expectation) = lint_id {
1641 cx.fulfill_expectation(expectation);
1642 }
1643
1644 match level {
1645 Level::Allow | Level::Expect => suppress_lint = true,
1646 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1647 }
1648 }
1649
1650 suppress_lint
1651}
1652
1653pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1661 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1662}
1663
1664pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1665 while let PatKind::Ref(subpat, _, _) = pat.kind {
1666 pat = subpat;
1667 }
1668 pat
1669}
1670
1671pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1672 Integer::from_int_ty(&tcx, ity).size().bits()
1673}
1674
1675#[expect(clippy::cast_possible_wrap)]
1676pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1678 let amt = 128 - int_bits(tcx, ity);
1679 ((u as i128) << amt) >> amt
1680}
1681
1682#[expect(clippy::cast_sign_loss)]
1683pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1685 let amt = 128 - int_bits(tcx, ity);
1686 ((u as u128) << amt) >> amt
1687}
1688
1689pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1691 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1692 let amt = 128 - bits;
1693 (u << amt) >> amt
1694}
1695
1696pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1697 attrs.iter().any(|attr| attr.has_name(symbol))
1698}
1699
1700pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1701 find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr { .. })
1702}
1703
1704pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1705 let mut prev_enclosing_node = None;
1706 let mut enclosing_node = node;
1707 while Some(enclosing_node) != prev_enclosing_node {
1708 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1709 return true;
1710 }
1711 prev_enclosing_node = Some(enclosing_node);
1712 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1713 }
1714
1715 false
1716}
1717
1718pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1721 tcx.hir_parent_owner_iter(id)
1722 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1723 .any(|(id, _)| {
1724 find_attr!(
1725 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1726 AttributeKind::AutomaticallyDerived(..)
1727 )
1728 })
1729}
1730
1731pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1733 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1736}
1737
1738pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1743 let mut conds = Vec::new();
1744 let mut blocks: Vec<&Block<'_>> = Vec::new();
1745
1746 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1747 conds.push(cond);
1748 if let ExprKind::Block(block, _) = then.kind {
1749 blocks.push(block);
1750 } else {
1751 panic!("ExprKind::If node is not an ExprKind::Block");
1752 }
1753
1754 if let Some(else_expr) = r#else {
1755 expr = else_expr;
1756 } else {
1757 break;
1758 }
1759 }
1760
1761 if !blocks.is_empty()
1763 && let ExprKind::Block(block, _) = expr.kind
1764 {
1765 blocks.push(block);
1766 }
1767
1768 (conds, blocks)
1769}
1770
1771pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1773 if let ExprKind::Closure(&Closure {
1774 body,
1775 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1776 ..
1777 }) = expr.kind
1778 && let ExprKind::Block(
1779 Block {
1780 expr:
1781 Some(Expr {
1782 kind: ExprKind::DropTemps(inner_expr),
1783 ..
1784 }),
1785 ..
1786 },
1787 _,
1788 ) = tcx.hir_body(body).value.kind
1789 {
1790 Some(inner_expr)
1791 } else {
1792 None
1793 }
1794}
1795
1796pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1798 get_async_closure_expr(tcx, body.value)
1799}
1800
1801pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1803 let did = match expr.kind {
1804 ExprKind::Call(path, _) => {
1805 if let ExprKind::Path(ref qpath) = path.kind
1806 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1807 {
1808 Some(did)
1809 } else {
1810 None
1811 }
1812 },
1813 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1814 _ => None,
1815 };
1816
1817 did.is_some_and(|did| find_attr!(cx.tcx.get_all_attrs(did), AttributeKind::MustUse { .. }))
1818}
1819
1820fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1832 let [param] = func.params else {
1833 return false;
1834 };
1835
1836 let mut expr = func.value;
1837 loop {
1838 match expr.kind {
1839 ExprKind::Block(
1840 &Block {
1841 stmts: [],
1842 expr: Some(e),
1843 ..
1844 },
1845 _,
1846 )
1847 | ExprKind::Ret(Some(e)) => expr = e,
1848 ExprKind::Block(
1849 &Block {
1850 stmts: [stmt],
1851 expr: None,
1852 ..
1853 },
1854 _,
1855 ) => {
1856 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1857 && let ExprKind::Ret(Some(ret_val)) = e.kind
1858 {
1859 expr = ret_val;
1860 } else {
1861 return false;
1862 }
1863 },
1864 _ => return is_expr_identity_of_pat(cx, param.pat, expr, true),
1865 }
1866 }
1867}
1868
1869pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1879 if cx
1880 .typeck_results()
1881 .pat_binding_modes()
1882 .get(pat.hir_id)
1883 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..)))
1884 {
1885 return false;
1889 }
1890
1891 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1893
1894 match (pat.kind, expr.kind) {
1895 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1896 expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1897 },
1898 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1899 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1900 },
1901 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1902 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1903 {
1904 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1905 },
1906 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1907 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1908 },
1909 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1910 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1911 {
1912 if let ExprKind::Path(ident) = &ident.kind
1914 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1915 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
1917 {
1918 true
1919 } else {
1920 false
1921 }
1922 },
1923 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
1924 if field_pats.len() == fields.len() =>
1925 {
1926 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1928 && unordered_over(field_pats, fields, |field_pat, field| {
1930 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
1931 })
1932 },
1933 _ => false,
1934 }
1935}
1936
1937pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1942 match expr.kind {
1943 ExprKind::Closure(&Closure { body, fn_decl, .. })
1944 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1945 {
1946 is_body_identity_function(cx, cx.tcx.hir_body(body))
1947 },
1948 ExprKind::Path(QPath::Resolved(_, path))
1949 if path.segments.iter().all(|seg| seg.infer_args)
1950 && let Some(did) = path.res.opt_def_id() =>
1951 {
1952 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1953 },
1954 _ => false,
1955 }
1956}
1957
1958pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1967 match expr.kind {
1968 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1969 _ => expr.basic_res().is_diag_item(cx, sym::convert_identity),
1970 }
1971}
1972
1973pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
1976 let mut child_id = expr.hir_id;
1977 let mut iter = tcx.hir_parent_iter(child_id);
1978 loop {
1979 match iter.next() {
1980 None => break None,
1981 Some((id, Node::Block(_))) => child_id = id,
1982 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
1983 Some((_, Node::Expr(expr))) => match expr.kind {
1984 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
1985 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
1986 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
1987 _ => break Some((Node::Expr(expr), child_id)),
1988 },
1989 Some((_, node)) => break Some((node, child_id)),
1990 }
1991 }
1992}
1993
1994pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1996 !matches!(
1997 get_expr_use_or_unification_node(tcx, expr),
1998 None | Some((
1999 Node::Stmt(Stmt {
2000 kind: StmtKind::Expr(_)
2001 | StmtKind::Semi(_)
2002 | StmtKind::Let(LetStmt {
2003 pat: Pat {
2004 kind: PatKind::Wild,
2005 ..
2006 },
2007 ..
2008 }),
2009 ..
2010 }),
2011 _
2012 ))
2013 )
2014}
2015
2016pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2018 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2019}
2020
2021pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2025 !expr.is_place_expr(|base| {
2026 cx.typeck_results()
2027 .adjustments()
2028 .get(base.hir_id)
2029 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2030 })
2031}
2032
2033pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2034 if !is_no_std_crate(cx) {
2035 Some("std")
2036 } else if !is_no_core_crate(cx) {
2037 Some("core")
2038 } else {
2039 None
2040 }
2041}
2042
2043pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2044 find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoStd(..))
2045}
2046
2047pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2048 find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoCore(..))
2049}
2050
2051pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2061 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2062 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2063 } else {
2064 false
2065 }
2066}
2067
2068pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2078 use rustc_trait_selection::traits;
2079 let predicates = cx
2080 .tcx
2081 .predicates_of(did)
2082 .predicates
2083 .iter()
2084 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2085 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2086}
2087
2088pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2090 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2091}
2092
2093pub fn fn_def_id_with_node_args<'tcx>(
2096 cx: &LateContext<'tcx>,
2097 expr: &Expr<'_>,
2098) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2099 let typeck = cx.typeck_results();
2100 match &expr.kind {
2101 ExprKind::MethodCall(..) => Some((
2102 typeck.type_dependent_def_id(expr.hir_id)?,
2103 typeck.node_args(expr.hir_id),
2104 )),
2105 ExprKind::Call(
2106 Expr {
2107 kind: ExprKind::Path(qpath),
2108 hir_id: path_hir_id,
2109 ..
2110 },
2111 ..,
2112 ) => {
2113 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2116 typeck.qpath_res(qpath, *path_hir_id)
2117 {
2118 Some((id, typeck.node_args(*path_hir_id)))
2119 } else {
2120 None
2121 }
2122 },
2123 _ => None,
2124 }
2125}
2126
2127pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2132 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2133 let expr_kind = expr_type.kind();
2134 let is_primitive = match expr_kind {
2135 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2136 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2137 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2138 is_recursively_primitive_type(*element_type)
2139 } else {
2140 unreachable!()
2141 }
2142 },
2143 _ => false,
2144 };
2145
2146 if is_primitive {
2147 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2150 rustc_ty::Slice(..) => return Some("slice".into()),
2151 rustc_ty::Array(..) => return Some("array".into()),
2152 rustc_ty::Tuple(..) => return Some("tuple".into()),
2153 _ => {
2154 let refs_peeled = expr_type.peel_refs();
2157 return Some(refs_peeled.walk().last().unwrap().to_string());
2158 },
2159 }
2160 }
2161 None
2162}
2163
2164pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2172where
2173 Hash: FnMut(&T) -> u64,
2174 Eq: FnMut(&T, &T) -> bool,
2175{
2176 match exprs {
2177 [a, b] if eq(a, b) => return vec![vec![a, b]],
2178 _ if exprs.len() <= 2 => return vec![],
2179 _ => {},
2180 }
2181
2182 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2183
2184 for expr in exprs {
2185 match buckets.entry(hash(expr)) {
2186 indexmap::map::Entry::Occupied(mut o) => {
2187 let bucket = o.get_mut();
2188 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2189 Some(group) => group.push(expr),
2190 None => bucket.push(vec![expr]),
2191 }
2192 },
2193 indexmap::map::Entry::Vacant(v) => {
2194 v.insert(vec![vec![expr]]);
2195 },
2196 }
2197 }
2198
2199 buckets
2200 .into_values()
2201 .flatten()
2202 .filter(|group| group.len() > 1)
2203 .collect()
2204}
2205
2206pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2209 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2210 if let PatKind::Ref(pat, _, _) = pat.kind {
2211 peel(pat, count + 1)
2212 } else {
2213 (pat, count)
2214 }
2215 }
2216 peel(pat, 0)
2217}
2218
2219pub fn peel_hir_expr_while<'tcx>(
2221 mut expr: &'tcx Expr<'tcx>,
2222 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2223) -> &'tcx Expr<'tcx> {
2224 while let Some(e) = f(expr) {
2225 expr = e;
2226 }
2227 expr
2228}
2229
2230pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2233 let mut remaining = count;
2234 let e = peel_hir_expr_while(expr, |e| match e.kind {
2235 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2236 remaining -= 1;
2237 Some(e)
2238 },
2239 _ => None,
2240 });
2241 (e, count - remaining)
2242}
2243
2244pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2247 let mut count: usize = 0;
2248 let mut curr_expr = expr;
2249 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2250 count = count.wrapping_add(1);
2251 curr_expr = local_expr;
2252 }
2253 (curr_expr, count)
2254}
2255
2256pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2259 let mut count = 0;
2260 let e = peel_hir_expr_while(expr, |e| match e.kind {
2261 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2262 count += 1;
2263 Some(e)
2264 },
2265 _ => None,
2266 });
2267 (e, count)
2268}
2269
2270pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2273 let mut count = 0;
2274 loop {
2275 match &ty.kind {
2276 TyKind::Ref(_, ref_ty) => {
2277 ty = ref_ty.ty;
2278 count += 1;
2279 },
2280 _ => break (ty, count),
2281 }
2282 }
2283}
2284
2285pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
2287 match &ty.kind {
2288 TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty),
2289 _ => ty,
2290 }
2291}
2292
2293pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2296 loop {
2297 match expr.kind {
2298 ExprKind::AddrOf(_, _, e) => expr = e,
2299 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2300 _ => break,
2301 }
2302 }
2303 expr
2304}
2305
2306pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2309 let mut operators = Vec::new();
2310 peel_hir_expr_while(expr, |expr| match expr.kind {
2311 ExprKind::AddrOf(_, _, e) => {
2312 operators.push(expr);
2313 Some(e)
2314 },
2315 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2316 operators.push(expr);
2317 Some(e)
2318 },
2319 _ => None,
2320 });
2321 operators
2322}
2323
2324pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2325 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2326 && let Res::Def(_, def_id) = path.res
2327 {
2328 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2329 }
2330 false
2331}
2332
2333static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2334
2335fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2338 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2339 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2340 let value = map.entry(module);
2341 match value {
2342 Entry::Occupied(entry) => f(entry.get()),
2343 Entry::Vacant(entry) => {
2344 let mut names = Vec::new();
2345 for id in tcx.hir_module_free_items(module) {
2346 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2347 && let item = tcx.hir_item(id)
2348 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2349 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2350 && let Res::Def(DefKind::Struct, _) = path.res
2352 {
2353 let has_test_marker = tcx
2354 .hir_attrs(item.hir_id())
2355 .iter()
2356 .any(|a| a.has_name(sym::rustc_test_marker));
2357 if has_test_marker {
2358 names.push(ident.name);
2359 }
2360 }
2361 }
2362 names.sort_unstable();
2363 f(entry.insert(names))
2364 },
2365 }
2366}
2367
2368pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2372 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2373 let node = tcx.hir_node(id);
2374 once((id, node))
2375 .chain(tcx.hir_parent_iter(id))
2376 .any(|(_id, node)| {
2379 if let Node::Item(item) = node
2380 && let ItemKind::Fn { ident, .. } = item.kind
2381 {
2382 return names.binary_search(&ident.name).is_ok();
2385 }
2386 false
2387 })
2388 })
2389}
2390
2391pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2398 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2399 if let Node::Item(item) = tcx.hir_node(id)
2400 && let ItemKind::Fn { ident, .. } = item.kind
2401 {
2402 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2403 names.binary_search(&ident.name).is_ok()
2404 })
2405 } else {
2406 false
2407 }
2408}
2409
2410pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2415 if let Some(cfgs) = find_attr!(tcx.hir_attrs(id), AttributeKind::CfgTrace(cfgs) => cfgs)
2416 && cfgs
2417 .iter()
2418 .any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. }))
2419 {
2420 true
2421 } else {
2422 false
2423 }
2424}
2425
2426pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2428 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2429}
2430
2431pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2433 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2434}
2435
2436pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2438 find_attr!(tcx.get_all_attrs(def_id), AttributeKind::CfgTrace(..))
2439 || find_attr!(
2440 tcx.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2441 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)),
2442 AttributeKind::CfgTrace(..)
2443 )
2444}
2445
2446pub fn walk_to_expr_usage<'tcx, T>(
2457 cx: &LateContext<'tcx>,
2458 e: &Expr<'tcx>,
2459 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2460) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2461 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2462 let mut child_id = e.hir_id;
2463
2464 while let Some((parent_id, parent)) = iter.next() {
2465 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2466 return Some(ControlFlow::Break(x));
2467 }
2468 let parent_expr = match parent {
2469 Node::Expr(e) => e,
2470 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2471 child_id = parent_id;
2472 continue;
2473 },
2474 Node::Arm(a) if a.body.hir_id == child_id => {
2475 child_id = parent_id;
2476 continue;
2477 },
2478 _ => return Some(ControlFlow::Continue((parent, child_id))),
2479 };
2480 match parent_expr.kind {
2481 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2482 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2483 child_id = id;
2484 iter = cx.tcx.hir_parent_iter(id);
2485 },
2486 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2487 _ => return Some(ControlFlow::Continue((parent, child_id))),
2488 }
2489 }
2490 debug_assert!(false, "no parent node found for `{child_id:?}`");
2491 None
2492}
2493
2494#[derive(Clone, Copy)]
2496pub enum DefinedTy<'tcx> {
2497 Hir(&'tcx hir::Ty<'tcx>),
2499 Mir {
2507 def_site_def_id: Option<DefId>,
2508 ty: Binder<'tcx, Ty<'tcx>>,
2509 },
2510}
2511
2512pub struct ExprUseCtxt<'tcx> {
2514 pub node: Node<'tcx>,
2516 pub child_id: HirId,
2518 pub adjustments: &'tcx [Adjustment<'tcx>],
2520 pub is_ty_unified: bool,
2522 pub moved_before_use: bool,
2524 pub same_ctxt: bool,
2526}
2527impl<'tcx> ExprUseCtxt<'tcx> {
2528 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2529 match self.node {
2530 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2531 Node::ExprField(field) => ExprUseNode::Field(field),
2532
2533 Node::Item(&Item {
2534 kind: ItemKind::Static(..) | ItemKind::Const(..),
2535 owner_id,
2536 ..
2537 })
2538 | Node::TraitItem(&TraitItem {
2539 kind: TraitItemKind::Const(..),
2540 owner_id,
2541 ..
2542 })
2543 | Node::ImplItem(&ImplItem {
2544 kind: ImplItemKind::Const(..),
2545 owner_id,
2546 ..
2547 }) => ExprUseNode::ConstStatic(owner_id),
2548
2549 Node::Item(&Item {
2550 kind: ItemKind::Fn { .. },
2551 owner_id,
2552 ..
2553 })
2554 | Node::TraitItem(&TraitItem {
2555 kind: TraitItemKind::Fn(..),
2556 owner_id,
2557 ..
2558 })
2559 | Node::ImplItem(&ImplItem {
2560 kind: ImplItemKind::Fn(..),
2561 owner_id,
2562 ..
2563 }) => ExprUseNode::Return(owner_id),
2564
2565 Node::Expr(use_expr) => match use_expr.kind {
2566 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2567 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2568 }),
2569
2570 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2571 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2572 Some(i) => ExprUseNode::FnArg(func, i),
2573 None => ExprUseNode::Callee,
2574 },
2575 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2576 use_expr.hir_id,
2577 name.args,
2578 args.iter()
2579 .position(|arg| arg.hir_id == self.child_id)
2580 .map_or(0, |i| i + 1),
2581 ),
2582 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2583 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2584 _ => ExprUseNode::Other,
2585 },
2586 _ => ExprUseNode::Other,
2587 }
2588 }
2589}
2590
2591pub enum ExprUseNode<'tcx> {
2593 LetStmt(&'tcx LetStmt<'tcx>),
2595 ConstStatic(OwnerId),
2597 Return(OwnerId),
2599 Field(&'tcx ExprField<'tcx>),
2601 FnArg(&'tcx Expr<'tcx>, usize),
2603 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2605 Callee,
2607 FieldAccess(Ident),
2609 AddrOf(ast::BorrowKind, Mutability),
2611 Other,
2612}
2613impl<'tcx> ExprUseNode<'tcx> {
2614 pub fn is_return(&self) -> bool {
2616 matches!(self, Self::Return(_))
2617 }
2618
2619 pub fn is_recv(&self) -> bool {
2621 matches!(self, Self::MethodArg(_, _, 0))
2622 }
2623
2624 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2626 match *self {
2627 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2628 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2629 def_site_def_id: Some(id.def_id.to_def_id()),
2630 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2631 }),
2632 Self::Return(id) => {
2633 if let Node::Expr(Expr {
2634 kind: ExprKind::Closure(c),
2635 ..
2636 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2637 {
2638 match c.fn_decl.output {
2639 FnRetTy::DefaultReturn(_) => None,
2640 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2641 }
2642 } else {
2643 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2644 Some(DefinedTy::Mir {
2645 def_site_def_id: Some(id.def_id.to_def_id()),
2646 ty,
2647 })
2648 }
2649 },
2650 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2651 Some(Expr {
2652 hir_id,
2653 kind: ExprKind::Struct(path, ..),
2654 ..
2655 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2656 .and_then(|(adt, variant)| {
2657 variant
2658 .fields
2659 .iter()
2660 .find(|f| f.name == field.ident.name)
2661 .map(|f| (adt, f))
2662 })
2663 .map(|(adt, field_def)| DefinedTy::Mir {
2664 def_site_def_id: Some(adt.did()),
2665 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2666 }),
2667 _ => None,
2668 },
2669 Self::FnArg(callee, i) => {
2670 let sig = expr_sig(cx, callee)?;
2671 let (hir_ty, ty) = sig.input_with_hir(i)?;
2672 Some(match hir_ty {
2673 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2674 None => DefinedTy::Mir {
2675 def_site_def_id: sig.predicates_id(),
2676 ty,
2677 },
2678 })
2679 },
2680 Self::MethodArg(id, _, i) => {
2681 let id = cx.typeck_results().type_dependent_def_id(id)?;
2682 let sig = cx.tcx.fn_sig(id).skip_binder();
2683 Some(DefinedTy::Mir {
2684 def_site_def_id: Some(id),
2685 ty: sig.input(i),
2686 })
2687 },
2688 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2689 }
2690 }
2691}
2692
2693pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2695 let mut adjustments = [].as_slice();
2696 let mut is_ty_unified = false;
2697 let mut moved_before_use = false;
2698 let mut same_ctxt = true;
2699 let ctxt = e.span.ctxt();
2700 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2701 if adjustments.is_empty()
2702 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2703 {
2704 adjustments = cx.typeck_results().expr_adjustments(e);
2705 }
2706 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2707 if let Node::Expr(e) = parent {
2708 match e.kind {
2709 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2710 is_ty_unified = true;
2711 moved_before_use = true;
2712 },
2713 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2714 is_ty_unified = true;
2715 moved_before_use = true;
2716 },
2717 ExprKind::Block(..) => moved_before_use = true,
2718 _ => {},
2719 }
2720 }
2721 ControlFlow::Continue(())
2722 });
2723 match node {
2724 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2725 node,
2726 child_id,
2727 adjustments,
2728 is_ty_unified,
2729 moved_before_use,
2730 same_ctxt,
2731 },
2732 None => ExprUseCtxt {
2733 node: Node::Crate(cx.tcx.hir_root_module()),
2734 child_id: HirId::INVALID,
2735 adjustments: &[],
2736 is_ty_unified: true,
2737 moved_before_use: true,
2738 same_ctxt: false,
2739 },
2740 }
2741}
2742
2743pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2745 let mut pos = 0;
2746 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2747 let end = pos + t.len;
2748 let range = pos as usize..end as usize;
2749 let inner = InnerSpan::new(range.start, range.end);
2750 pos = end;
2751 (t.kind, s.get(range).unwrap_or_default(), inner)
2752 })
2753}
2754
2755pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2758 let Ok(snippet) = sm.span_to_snippet(span) else {
2759 return false;
2760 };
2761 return tokenize(&snippet, FrontmatterAllowed::No).any(|token| {
2762 matches!(
2763 token.kind,
2764 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2765 )
2766 });
2767}
2768
2769pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2774 matches!(span.get_source_text(cx), Some(snippet) if tokenize_with_text(&snippet).any(|(token, _, _)|
2775 match token {
2776 TokenKind::Whitespace => false,
2777 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2778 _ => true,
2779 }
2780 ))
2781}
2782pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2786 span_extract_comments(sm, span).join("\n")
2787}
2788
2789pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2793 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2794 tokenize_with_text(&snippet)
2795 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2796 .map(|(_, s, _)| s.to_string())
2797 .collect::<Vec<_>>()
2798}
2799
2800pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2801 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2802}
2803
2804pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2829 cx: &LateContext<'_>,
2830 pat: &'a Pat<'hir>,
2831 else_body: &Expr<'_>,
2832) -> Option<&'a Pat<'hir>> {
2833 if let Some([inner_pat]) = as_some_pattern(cx, pat)
2834 && !is_refutable(cx, inner_pat)
2835 && let else_body = peel_blocks(else_body)
2836 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2837 && let ExprKind::Path(ret_path) = ret_val.kind
2838 && cx
2839 .qpath_res(&ret_path, ret_val.hir_id)
2840 .ctor_parent(cx)
2841 .is_lang_item(cx, OptionNone)
2842 {
2843 Some(inner_pat)
2844 } else {
2845 None
2846 }
2847}
2848
2849macro_rules! op_utils {
2850 ($($name:ident $assign:ident)*) => {
2851 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2853
2854 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2856
2857 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2859 match kind {
2860 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2861 _ => None,
2862 }
2863 }
2864 };
2865}
2866
2867op_utils! {
2868 Add AddAssign
2869 Sub SubAssign
2870 Mul MulAssign
2871 Div DivAssign
2872 Rem RemAssign
2873 BitXor BitXorAssign
2874 BitAnd BitAndAssign
2875 BitOr BitOrAssign
2876 Shl ShlAssign
2877 Shr ShrAssign
2878}
2879
2880pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2883 match *pat {
2884 PatKind::Wild => true,
2885 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2886 !visitors::is_local_used(cx, body, id)
2887 },
2888 _ => false,
2889 }
2890}
2891
2892#[derive(Clone, Copy)]
2893pub enum RequiresSemi {
2894 Yes,
2895 No,
2896}
2897impl RequiresSemi {
2898 pub fn requires_semi(self) -> bool {
2899 matches!(self, Self::Yes)
2900 }
2901}
2902
2903#[expect(clippy::too_many_lines)]
2906pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2907 struct BreakTarget {
2908 id: HirId,
2909 unused: bool,
2910 }
2911
2912 struct V<'cx, 'tcx> {
2913 cx: &'cx LateContext<'tcx>,
2914 break_targets: Vec<BreakTarget>,
2915 break_targets_for_result_ty: u32,
2916 in_final_expr: bool,
2917 requires_semi: bool,
2918 is_never: bool,
2919 }
2920
2921 impl V<'_, '_> {
2922 fn push_break_target(&mut self, id: HirId) {
2923 self.break_targets.push(BreakTarget { id, unused: true });
2924 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2925 }
2926 }
2927
2928 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
2929 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
2930 if self.is_never && self.break_targets.is_empty() {
2947 if self.in_final_expr && !self.requires_semi {
2948 match e.kind {
2951 ExprKind::DropTemps(e) => self.visit_expr(e),
2952 ExprKind::If(_, then, Some(else_)) => {
2953 self.visit_expr(then);
2954 self.visit_expr(else_);
2955 },
2956 ExprKind::Match(_, arms, _) => {
2957 for arm in arms {
2958 self.visit_expr(arm.body);
2959 }
2960 },
2961 ExprKind::Loop(b, ..) => {
2962 self.push_break_target(e.hir_id);
2963 self.in_final_expr = false;
2964 self.visit_block(b);
2965 self.break_targets.pop();
2966 },
2967 ExprKind::Block(b, _) => {
2968 if b.targeted_by_break {
2969 self.push_break_target(b.hir_id);
2970 self.visit_block(b);
2971 self.break_targets.pop();
2972 } else {
2973 self.visit_block(b);
2974 }
2975 },
2976 _ => {
2977 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
2978 },
2979 }
2980 }
2981 return;
2982 }
2983 match e.kind {
2984 ExprKind::DropTemps(e) => self.visit_expr(e),
2985 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
2986 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
2987 self.in_final_expr = false;
2988 self.visit_expr(e);
2989 self.is_never = true;
2990 },
2991 ExprKind::Break(dest, e) => {
2992 if let Some(e) = e {
2993 self.in_final_expr = false;
2994 self.visit_expr(e);
2995 }
2996 if let Ok(id) = dest.target_id
2997 && let Some((i, target)) = self
2998 .break_targets
2999 .iter_mut()
3000 .enumerate()
3001 .find(|(_, target)| target.id == id)
3002 {
3003 target.unused &= self.is_never;
3004 if i < self.break_targets_for_result_ty as usize {
3005 self.requires_semi = true;
3006 }
3007 }
3008 self.is_never = true;
3009 },
3010 ExprKind::If(cond, then, else_) => {
3011 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3012 self.visit_expr(cond);
3013 self.in_final_expr = in_final_expr;
3014
3015 if self.is_never {
3016 self.visit_expr(then);
3017 if let Some(else_) = else_ {
3018 self.visit_expr(else_);
3019 }
3020 } else {
3021 self.visit_expr(then);
3022 let is_never = mem::replace(&mut self.is_never, false);
3023 if let Some(else_) = else_ {
3024 self.visit_expr(else_);
3025 self.is_never &= is_never;
3026 }
3027 }
3028 },
3029 ExprKind::Match(scrutinee, arms, _) => {
3030 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3031 self.visit_expr(scrutinee);
3032 self.in_final_expr = in_final_expr;
3033
3034 if self.is_never {
3035 for arm in arms {
3036 self.visit_arm(arm);
3037 }
3038 } else {
3039 let mut is_never = true;
3040 for arm in arms {
3041 self.is_never = false;
3042 if let Some(guard) = arm.guard {
3043 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3044 self.visit_expr(guard);
3045 self.in_final_expr = in_final_expr;
3046 self.is_never = false;
3048 }
3049 self.visit_expr(arm.body);
3050 is_never &= self.is_never;
3051 }
3052 self.is_never = is_never;
3053 }
3054 },
3055 ExprKind::Loop(b, _, _, _) => {
3056 self.push_break_target(e.hir_id);
3057 self.in_final_expr = false;
3058 self.visit_block(b);
3059 self.is_never = self.break_targets.pop().unwrap().unused;
3060 },
3061 ExprKind::Block(b, _) => {
3062 if b.targeted_by_break {
3063 self.push_break_target(b.hir_id);
3064 self.visit_block(b);
3065 self.is_never &= self.break_targets.pop().unwrap().unused;
3066 } else {
3067 self.visit_block(b);
3068 }
3069 },
3070 _ => {
3071 self.in_final_expr = false;
3072 walk_expr(self, e);
3073 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3074 },
3075 }
3076 }
3077
3078 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3079 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3080 for s in b.stmts {
3081 self.visit_stmt(s);
3082 }
3083 self.in_final_expr = in_final_expr;
3084 if let Some(e) = b.expr {
3085 self.visit_expr(e);
3086 }
3087 }
3088
3089 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3090 if let Some(e) = l.init {
3091 self.visit_expr(e);
3092 }
3093 if let Some(else_) = l.els {
3094 let is_never = self.is_never;
3095 self.visit_block(else_);
3096 self.is_never = is_never;
3097 }
3098 }
3099
3100 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3101 if let Some(guard) = arm.guard {
3102 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3103 self.visit_expr(guard);
3104 self.in_final_expr = in_final_expr;
3105 }
3106 self.visit_expr(arm.body);
3107 }
3108 }
3109
3110 if cx.typeck_results().expr_ty(e).is_never() {
3111 Some(RequiresSemi::No)
3112 } else if let ExprKind::Block(b, _) = e.kind
3113 && !b.targeted_by_break
3114 && b.expr.is_none()
3115 {
3116 None
3118 } else {
3119 let mut v = V {
3120 cx,
3121 break_targets: Vec::new(),
3122 break_targets_for_result_ty: 0,
3123 in_final_expr: true,
3124 requires_semi: false,
3125 is_never: false,
3126 };
3127 v.visit_expr(e);
3128 v.is_never
3129 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3130 RequiresSemi::Yes
3131 } else {
3132 RequiresSemi::No
3133 })
3134 }
3135}
3136
3137pub fn get_path_from_caller_to_method_type<'tcx>(
3143 tcx: TyCtxt<'tcx>,
3144 from: LocalDefId,
3145 method: DefId,
3146 args: GenericArgsRef<'tcx>,
3147) -> String {
3148 let assoc_item = tcx.associated_item(method);
3149 let def_id = assoc_item.container_id(tcx);
3150 match assoc_item.container {
3151 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3152 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3153 let ty = tcx.type_of(def_id).instantiate_identity();
3154 get_path_to_ty(tcx, from, ty, args)
3155 },
3156 }
3157}
3158
3159fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3160 match ty.kind() {
3161 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3162 rustc_ty::Array(..)
3164 | rustc_ty::Dynamic(..)
3165 | rustc_ty::Never
3166 | rustc_ty::RawPtr(_, _)
3167 | rustc_ty::Ref(..)
3168 | rustc_ty::Slice(_)
3169 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3170 _ => ty.to_string(),
3171 }
3172}
3173
3174fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3176 if callee.is_local() {
3178 let callee_path = tcx.def_path(callee);
3179 let caller_path = tcx.def_path(from.to_def_id());
3180 maybe_get_relative_path(&caller_path, &callee_path, 2)
3181 } else {
3182 tcx.def_path_str(callee)
3183 }
3184}
3185
3186fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3199 use itertools::EitherOrBoth::{Both, Left, Right};
3200
3201 let unique_parts = to
3203 .data
3204 .iter()
3205 .zip_longest(from.data.iter())
3206 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3207 .map(|el| match el {
3208 Both(l, r) => Both(l.data, r.data),
3209 Left(l) => Left(l.data),
3210 Right(r) => Right(r.data),
3211 });
3212
3213 let mut go_up_by = 0;
3215 let mut path = Vec::new();
3216 for el in unique_parts {
3217 match el {
3218 Both(l, r) => {
3219 if let DefPathData::TypeNs(sym) = l {
3229 path.push(sym);
3230 }
3231 if let DefPathData::TypeNs(_) = r {
3232 go_up_by += 1;
3233 }
3234 },
3235 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3240 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3245 _ => {},
3246 }
3247 }
3248
3249 if go_up_by > max_super {
3250 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3252 if let DefPathData::TypeNs(sym) = el.data {
3253 Some(sym)
3254 } else {
3255 None
3256 }
3257 })))
3258 } else {
3259 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3260 }
3261}
3262
3263pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3266 matches!(
3267 cx.tcx.parent_hir_node(id),
3268 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3269 )
3270}
3271
3272pub fn is_block_like(expr: &Expr<'_>) -> bool {
3275 matches!(
3276 expr.kind,
3277 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3278 )
3279}
3280
3281pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3283 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3284 match expr.kind {
3285 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3286 _ if is_block_like(expr) => is_operand,
3287 _ => false,
3288 }
3289 }
3290
3291 contains_block(expr, false)
3292}
3293
3294pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3296 if let Some(parent_expr) = get_parent_expr(cx, expr)
3297 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3298 && receiver.hir_id == expr.hir_id
3299 {
3300 return true;
3301 }
3302 false
3303}
3304
3305pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3308 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3309 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3310 && temporary_ty
3311 .walk()
3312 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3313 {
3314 ControlFlow::Break(())
3315 } else {
3316 ControlFlow::Continue(())
3317 }
3318 })
3319 .is_break()
3320}
3321
3322pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3333 let expr_ty_is_adjusted = cx
3334 .typeck_results()
3335 .expr_adjustments(expr)
3336 .iter()
3337 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3339 if expr_ty_is_adjusted {
3340 return true;
3341 }
3342
3343 match expr.kind {
3346 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3347 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3348
3349 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3350 return false;
3351 }
3352
3353 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3354 let mut args_with_ty_param = {
3355 fn_sig
3356 .inputs()
3357 .skip_binder()
3358 .iter()
3359 .skip(self_arg_count)
3360 .zip(args)
3361 .filter_map(|(arg_ty, arg)| {
3362 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3363 Some(arg)
3364 } else {
3365 None
3366 }
3367 })
3368 };
3369 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3370 },
3371 ExprKind::Struct(qpath, _, _) => {
3373 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3374 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3375 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3376 return true;
3378 };
3379 v_def
3380 .fields
3381 .iter()
3382 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3383 } else {
3384 false
3385 }
3386 },
3387 ExprKind::Block(
3389 &Block {
3390 expr: Some(ret_expr), ..
3391 },
3392 _,
3393 )
3394 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3395
3396 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3398 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3400 ExprKind::If(_, then, maybe_else) => {
3402 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3403 },
3404 ExprKind::Match(_, arms, _) => arms
3405 .iter()
3406 .map(|arm| arm.body)
3407 .any(|body| expr_requires_coercion(cx, body)),
3408 _ => false,
3409 }
3410}
3411
3412pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3415 if let Some(hir_id) = expr.res_local_id()
3416 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3417 {
3418 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3419 } else if let ExprKind::Path(p) = &expr.kind
3420 && let Some(mutability) = cx
3421 .qpath_res(p, expr.hir_id)
3422 .opt_def_id()
3423 .and_then(|id| cx.tcx.static_mutability(id))
3424 {
3425 mutability == Mutability::Mut
3426 } else if let ExprKind::Field(parent, _) = expr.kind {
3427 is_mutable(cx, parent)
3428 } else {
3429 true
3430 }
3431}
3432
3433pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3436 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3437 return hir_ty;
3438 };
3439 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3440 && let Some(segment) = path.segments.last()
3441 && segment.ident.name == sym::Option
3442 && let Res::Def(DefKind::Enum, def_id) = segment.res
3443 && def_id == option_def_id
3444 && let [GenericArg::Type(arg_ty)] = segment.args().args
3445 {
3446 hir_ty = arg_ty.as_unambig_ty();
3447 }
3448 hir_ty
3449}
3450
3451pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3454 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3455 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3456 && let ctxt = expr.span.ctxt()
3457 && for_each_expr_without_closures(into_future_arg, |e| {
3458 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3459 })
3460 .is_none()
3461 {
3462 Some(into_future_arg)
3463 } else {
3464 None
3465 }
3466}
3467
3468pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3470 if let ExprKind::Call(fn_expr, []) = &expr.kind
3471 && let ExprKind::Path(qpath) = &fn_expr.kind
3472 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3473 {
3474 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3475 } else {
3476 false
3477 }
3478}
3479
3480pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3497 let enclosing_body_owner = cx
3498 .tcx
3499 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3500 let mut prev_id = expr.hir_id;
3501 let mut skip_until_id = None;
3502 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3503 if hir_id == enclosing_body_owner {
3504 return true;
3505 }
3506 if let Some(id) = skip_until_id {
3507 prev_id = hir_id;
3508 if id == hir_id {
3509 skip_until_id = None;
3510 }
3511 continue;
3512 }
3513 match node {
3514 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3515 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3516 Node::Expr(expr) => match expr.kind {
3517 ExprKind::Ret(_) => return true,
3518 ExprKind::If(_, then, opt_else)
3519 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3520 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3521 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3522 ExprKind::Break(
3523 Destination {
3524 target_id: Ok(target_id),
3525 ..
3526 },
3527 _,
3528 ) => skip_until_id = Some(target_id),
3529 _ => break,
3530 },
3531 _ => break,
3532 }
3533 prev_id = hir_id;
3534 }
3535
3536 false
3539}
3540
3541pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3544 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3545 matches!(
3546 adj.kind,
3547 Adjust::Deref(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny
3548 )
3549 })
3550}
3551
3552pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3554 matches!(
3555 expr.kind,
3556 ExprKind::Closure(Closure {
3557 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3558 CoroutineDesugaring::Async,
3559 CoroutineSource::Block
3560 )),
3561 ..
3562 })
3563 )
3564}
3565
3566pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3568 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3569}