1#![feature(box_patterns)]
2#![feature(if_let_guard)]
3#![feature(macro_metavar_expr)]
4#![feature(never_type)]
5#![feature(rustc_private)]
6#![feature(assert_matches)]
7#![feature(unwrap_infallible)]
8#![feature(array_windows)]
9#![recursion_limit = "512"]
10#![allow(
11 clippy::missing_errors_doc,
12 clippy::missing_panics_doc,
13 clippy::must_use_candidate,
14 rustc::diagnostic_outside_of_impl,
15 rustc::untranslatable_diagnostic
16)]
17#![warn(
18 trivial_casts,
19 trivial_numeric_casts,
20 rust_2018_idioms,
21 unused_lifetimes,
22 unused_qualifications,
23 rustc::internal
24)]
25
26extern crate indexmap;
29extern crate rustc_abi;
30extern crate rustc_ast;
31extern crate rustc_attr_data_structures;
32extern crate rustc_attr_parsing;
33extern crate rustc_const_eval;
34extern crate rustc_data_structures;
35#[allow(unused_extern_crates)]
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;
51extern crate smallvec;
52
53pub mod ast_utils;
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 ptr;
68pub mod qualify_min_const_fn;
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, hash_expr, hash_stmt, is_bool, over,
81};
82
83use core::mem;
84use core::ops::ControlFlow;
85use std::collections::hash_map::Entry;
86use std::iter::{once, repeat_n};
87use std::sync::{Mutex, MutexGuard, OnceLock};
88
89use itertools::Itertools;
90use rustc_abi::Integer;
91use rustc_ast::ast::{self, LitKind, RangeLimits};
92use rustc_attr_data_structures::{AttributeKind, find_attr};
93use rustc_data_structures::fx::FxHashMap;
94use rustc_data_structures::packed::Pu128;
95use rustc_data_structures::unhash::UnindexMap;
96use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
97use rustc_hir::def::{DefKind, Res};
98use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
99use rustc_hir::definitions::{DefPath, DefPathData};
100use rustc_hir::hir_id::{HirIdMap, HirIdSet};
101use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
102use rustc_hir::{
103 self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
104 CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl,
105 ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
106 Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
107 TraitItemKind, TraitRef, TyKind, UnOp, def,
108};
109use rustc_lexer::{TokenKind, tokenize};
110use rustc_lint::{LateContext, Level, Lint, LintContext};
111use rustc_middle::hir::nested_filter;
112use rustc_middle::hir::place::PlaceBase;
113use rustc_middle::lint::LevelAndSource;
114use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
115use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
116use rustc_middle::ty::layout::IntegerExt;
117use rustc_middle::ty::{
118 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
119 TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
120};
121use rustc_span::hygiene::{ExpnKind, MacroKind};
122use rustc_span::source_map::SourceMap;
123use rustc_span::symbol::{Ident, Symbol, kw};
124use rustc_span::{InnerSpan, Span};
125use source::walk_span_to_context;
126use visitors::{Visitable, for_each_unconsumed_temporary};
127
128use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
129use crate::higher::Range;
130use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
131use crate::visitors::for_each_expr_without_closures;
132
133#[macro_export]
134macro_rules! extract_msrv_attr {
135 () => {
136 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
137 let sess = rustc_lint::LintContext::sess(cx);
138 self.msrv.check_attributes(sess, attrs);
139 }
140
141 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
142 let sess = rustc_lint::LintContext::sess(cx);
143 self.msrv.check_attributes_post(sess, attrs);
144 }
145 };
146}
147
148pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
171 while let Some(init) = path_to_local(expr)
172 .and_then(|id| find_binding_init(cx, id))
173 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
174 {
175 expr = init;
176 }
177 expr
178}
179
180pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
189 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
190 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
191 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
192 {
193 return local.init;
194 }
195 None
196}
197
198pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
202 for (_, node) in cx.tcx.hir_parent_iter(local) {
203 match node {
204 Node::Pat(..) | Node::PatField(..) => {},
205 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
206 _ => return true,
207 }
208 }
209
210 false
211}
212
213pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
224 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
225 cx.enclosing_body.is_some_and(|id| {
226 cx.tcx
227 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
228 .is_some()
229 })
230}
231
232pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
239 use rustc_hir::ConstContext::{Const, ConstFn, Static};
240 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
241 return false;
242 };
243 match ctx {
244 ConstFn => false,
245 Static(_) | Const { inline: _ } => true,
246 }
247}
248
249pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
252 if let Res::Def(DefKind::Ctor(..), id) = res
253 && let Some(lang_id) = cx.tcx.lang_items().get(lang_item)
254 && let Some(id) = cx.tcx.opt_parent(id)
255 {
256 id == lang_id
257 } else {
258 false
259 }
260}
261
262pub fn is_enum_variant_ctor(
264 cx: &LateContext<'_>,
265 enum_item: Symbol,
266 variant_name: Symbol,
267 ctor_call_id: DefId,
268) -> bool {
269 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
270 return false;
271 };
272
273 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
274 variants
275 .filter(|variant| variant.name == variant_name)
276 .filter_map(|variant| variant.ctor.as_ref())
277 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
278}
279
280pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
282 let did = match cx.tcx.def_kind(did) {
283 DefKind::Ctor(..) => cx.tcx.parent(did),
284 DefKind::Variant => match cx.tcx.opt_parent(did) {
286 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
287 _ => did,
288 },
289 _ => did,
290 };
291
292 cx.tcx.is_diagnostic_item(item, did)
293}
294
295pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
297 let did = match cx.tcx.def_kind(did) {
298 DefKind::Ctor(..) => cx.tcx.parent(did),
299 DefKind::Variant => match cx.tcx.opt_parent(did) {
301 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
302 _ => did,
303 },
304 _ => did,
305 };
306
307 cx.tcx.lang_items().get(item) == Some(did)
308}
309
310pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
311 matches!(
312 expr.kind,
313 ExprKind::Block(
314 Block {
315 stmts: [],
316 expr: None,
317 ..
318 },
319 _
320 ) | ExprKind::Tup([])
321 )
322}
323
324pub fn is_wild(pat: &Pat<'_>) -> bool {
326 matches!(pat.kind, PatKind::Wild)
327}
328
329pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
331 matches!(
332 arm.pat.kind,
333 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
334 if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone)
335 )
336}
337
338pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
340 match *qpath {
341 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
342 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
343 _ => false,
344 }
345}
346
347pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
349 if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
350 cx.tcx.trait_of_item(method_id).is_none()
351 } else {
352 false
353 }
354}
355
356pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
358 if let Some(impl_did) = cx.tcx.impl_of_method(def_id)
359 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
360 {
361 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
362 }
363 false
364}
365
366pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
368 if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
369 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
370 }
371 false
372}
373
374pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
376 cx.typeck_results()
377 .type_dependent_def_id(expr.hir_id)
378 .is_some_and(|did| is_diag_trait_item(cx, did, diag_item))
379}
380
381pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
383 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
384 && let ItemKind::Impl(imp) = item.kind
385 {
386 imp.of_trait.is_some()
387 } else {
388 false
389 }
390}
391
392pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
402 if let ExprKind::Path(ref qpath) = expr.kind {
403 cx.qpath_res(qpath, expr.hir_id)
404 .opt_def_id()
405 .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item))
406 } else {
407 false
408 }
409}
410
411pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
412 match *path {
413 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
414 QPath::TypeRelative(_, seg) => seg,
415 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
416 }
417}
418
419pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
420 last_path_segment(qpath)
421 .args
422 .map_or(&[][..], |a| a.args)
423 .iter()
424 .filter_map(|a| match a {
425 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
426 _ => None,
427 })
428}
429
430pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
433 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id))
434}
435
436pub fn is_path_diagnostic_item<'tcx>(
439 cx: &LateContext<'_>,
440 maybe_path: &impl MaybePath<'tcx>,
441 diag_item: Symbol,
442) -> bool {
443 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
444}
445
446pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
448 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
449 && let Res::Local(id) = path.res
450 {
451 return Some(id);
452 }
453 None
454}
455
456pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
459 path_to_local(expr) == Some(id)
460}
461
462pub trait MaybePath<'hir> {
463 fn hir_id(&self) -> HirId;
464 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
465}
466
467macro_rules! maybe_path {
468 ($ty:ident, $kind:ident) => {
469 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
470 fn hir_id(&self) -> HirId {
471 self.hir_id
472 }
473 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
474 match &self.kind {
475 hir::$kind::Path(qpath) => Some(qpath),
476 _ => None,
477 }
478 }
479 }
480 };
481}
482maybe_path!(Expr, ExprKind);
483impl<'hir> MaybePath<'hir> for Pat<'hir> {
484 fn hir_id(&self) -> HirId {
485 self.hir_id
486 }
487 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
488 match &self.kind {
489 PatKind::Expr(PatExpr {
490 kind: PatExprKind::Path(qpath),
491 ..
492 }) => Some(qpath),
493 _ => None,
494 }
495 }
496}
497maybe_path!(Ty, TyKind);
498
499pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
501 match maybe_path.qpath_opt() {
502 None => Res::Err,
503 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
504 }
505}
506
507pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
509 path_res(cx, maybe_path).opt_def_id()
510}
511
512pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
528 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
529 && let ItemKind::Impl(impl_) = &item.kind
530 {
531 return impl_.of_trait.as_ref();
532 }
533 None
534}
535
536fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
544 let mut result = vec![];
545 let root = loop {
546 match e.kind {
547 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
548 result.push(e);
549 e = ep;
550 },
551 _ => break e,
552 }
553 };
554 result.reverse();
555 (result, root)
556}
557
558pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
560 cx.typeck_results()
561 .expr_adjustments(e)
562 .iter()
563 .find_map(|a| match a.kind {
564 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
565 Adjust::Deref(None) => None,
566 _ => Some(None),
567 })
568 .and_then(|x| x)
569}
570
571pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
574 let (s1, r1) = projection_stack(e1);
575 let (s2, r2) = projection_stack(e2);
576 if !eq_expr_value(cx, r1, r2) {
577 return true;
578 }
579 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
580 return false;
581 }
582
583 for (x1, x2) in s1.iter().zip(s2.iter()) {
584 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
585 return false;
586 }
587
588 match (&x1.kind, &x2.kind) {
589 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
590 if i1 != i2 {
591 return true;
592 }
593 },
594 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
595 if !eq_expr_value(cx, i1, i2) {
596 return false;
597 }
598 },
599 _ => return false,
600 }
601 }
602 false
603}
604
605fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
608 let std_types_symbols = &[
609 sym::Vec,
610 sym::VecDeque,
611 sym::LinkedList,
612 sym::HashMap,
613 sym::BTreeMap,
614 sym::HashSet,
615 sym::BTreeSet,
616 sym::BinaryHeap,
617 ];
618
619 if let QPath::TypeRelative(_, method) = path
620 && method.ident.name == sym::new
621 && let Some(impl_did) = cx.tcx.impl_of_method(def_id)
622 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
623 {
624 return std_types_symbols.iter().any(|&symbol| {
625 cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
626 });
627 }
628 false
629}
630
631pub fn is_default_equivalent_call(
633 cx: &LateContext<'_>,
634 repl_func: &Expr<'_>,
635 whole_call_expr: Option<&Expr<'_>>,
636) -> bool {
637 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
638 && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
639 && (is_diag_trait_item(cx, repl_def_id, sym::Default)
640 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath))
641 {
642 return true;
643 }
644
645 let Some(e) = whole_call_expr else { return false };
648 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
649 return false;
650 };
651 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
652 return false;
653 };
654 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
655 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
656 cx.tcx.lifetimes.re_erased.into()
657 } else if param.index == 0 && param.name == kw::SelfUpper {
658 ty.into()
659 } else {
660 param.to_error(cx.tcx)
661 }
662 });
663 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
664
665 let Ok(Some(instance)) = instance else { return false };
666 if let rustc_ty::InstanceKind::Item(def) = instance.def
667 && !cx.tcx.is_mir_available(def)
668 {
669 return false;
670 }
671 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
672 return false;
673 };
674 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
675 return false;
676 };
677
678 let body = cx.tcx.instance_mir(instance.def);
684 for block_data in body.basic_blocks.iter() {
685 if block_data.statements.len() == 1
686 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
687 && assign.0.local == RETURN_PLACE
688 && let Rvalue::Aggregate(kind, _places) = &assign.1
689 && let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
690 && let def = cx.tcx.adt_def(did)
691 && let variant = &def.variant(*variant_index)
692 && variant.fields.is_empty()
693 && let Some((_, did)) = variant.ctor
694 && did == repl_def_id
695 {
696 return true;
697 } else if block_data.statements.is_empty()
698 && let Some(term) = &block_data.terminator
699 {
700 match &term.kind {
701 TerminatorKind::Call {
702 func: Operand::Constant(c),
703 ..
704 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
705 && *did == repl_def_id =>
706 {
707 return true;
708 },
709 TerminatorKind::TailCall {
710 func: Operand::Constant(c),
711 ..
712 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
713 && *did == repl_def_id =>
714 {
715 return true;
716 },
717 _ => {},
718 }
719 }
720 }
721 false
722}
723
724pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
728 match &e.kind {
729 ExprKind::Lit(lit) => match lit.node {
730 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
731 LitKind::Str(s, _) => s.is_empty(),
732 _ => false,
733 },
734 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
735 ExprKind::Repeat(x, len) => {
736 if let ConstArgKind::Anon(anon_const) = len.kind
737 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
738 && let LitKind::Int(v, _) = const_lit.node
739 && v <= 32
740 && is_default_equivalent(cx, x)
741 {
742 true
743 } else {
744 false
745 }
746 },
747 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
748 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
749 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
750 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
751 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
752 _ => false,
753 }
754}
755
756fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
757 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
758 && seg.ident.name == sym::from
759 {
760 match arg.kind {
761 ExprKind::Lit(hir::Lit {
762 node: LitKind::Str(sym, _),
763 ..
764 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
765 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
766 ExprKind::Repeat(_, len) => {
767 if let ConstArgKind::Anon(anon_const) = len.kind
768 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
769 && let LitKind::Int(v, _) = const_lit.node
770 {
771 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
772 }
773 },
774 _ => (),
775 }
776 }
777 false
778}
779
780pub fn can_move_expr_to_closure_no_visit<'tcx>(
812 cx: &LateContext<'tcx>,
813 expr: &'tcx Expr<'_>,
814 loop_ids: &[HirId],
815 ignore_locals: &HirIdSet,
816) -> bool {
817 match expr.kind {
818 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
819 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
820 if loop_ids.contains(&id) =>
821 {
822 true
823 },
824 ExprKind::Break(..)
825 | ExprKind::Continue(_)
826 | ExprKind::Ret(_)
827 | ExprKind::Yield(..)
828 | ExprKind::InlineAsm(_) => false,
829 ExprKind::Field(
832 &Expr {
833 hir_id,
834 kind:
835 ExprKind::Path(QPath::Resolved(
836 _,
837 Path {
838 res: Res::Local(local_id),
839 ..
840 },
841 )),
842 ..
843 },
844 _,
845 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
846 false
848 },
849 _ => true,
850 }
851}
852
853#[derive(Debug, Clone, Copy, PartialEq, Eq)]
855pub enum CaptureKind {
856 Value,
857 Use,
858 Ref(Mutability),
859}
860impl CaptureKind {
861 pub fn is_imm_ref(self) -> bool {
862 self == Self::Ref(Mutability::Not)
863 }
864}
865impl std::ops::BitOr for CaptureKind {
866 type Output = Self;
867 fn bitor(self, rhs: Self) -> Self::Output {
868 match (self, rhs) {
869 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
870 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
871 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
872 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
873 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
874 }
875 }
876}
877impl std::ops::BitOrAssign for CaptureKind {
878 fn bitor_assign(&mut self, rhs: Self) {
879 *self = *self | rhs;
880 }
881}
882
883pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
889 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
890 let mut capture = CaptureKind::Ref(Mutability::Not);
891 pat.each_binding_or_first(&mut |_, id, span, _| match cx
892 .typeck_results()
893 .extract_binding_mode(cx.sess(), id, span)
894 .0
895 {
896 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
897 capture = CaptureKind::Value;
898 },
899 ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
900 capture = CaptureKind::Ref(Mutability::Mut);
901 },
902 _ => (),
903 });
904 capture
905 }
906
907 debug_assert!(matches!(
908 e.kind,
909 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
910 ));
911
912 let mut child_id = e.hir_id;
913 let mut capture = CaptureKind::Value;
914 let mut capture_expr_ty = e;
915
916 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
917 if let [
918 Adjustment {
919 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
920 target,
921 },
922 ref adjust @ ..,
923 ] = *cx
924 .typeck_results()
925 .adjustments()
926 .get(child_id)
927 .map_or(&[][..], |x| &**x)
928 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
929 *adjust.last().map_or(target, |a| a.target).kind()
930 {
931 return CaptureKind::Ref(mutability);
932 }
933
934 match parent {
935 Node::Expr(e) => match e.kind {
936 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
937 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
938 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
939 return CaptureKind::Ref(Mutability::Mut);
940 },
941 ExprKind::Field(..) => {
942 if capture == CaptureKind::Value {
943 capture_expr_ty = e;
944 }
945 },
946 ExprKind::Let(let_expr) => {
947 let mutability = match pat_capture_kind(cx, let_expr.pat) {
948 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
949 CaptureKind::Ref(m) => m,
950 };
951 return CaptureKind::Ref(mutability);
952 },
953 ExprKind::Match(_, arms, _) => {
954 let mut mutability = Mutability::Not;
955 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
956 match capture {
957 CaptureKind::Value | CaptureKind::Use => break,
958 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
959 CaptureKind::Ref(Mutability::Not) => (),
960 }
961 }
962 return CaptureKind::Ref(mutability);
963 },
964 _ => break,
965 },
966 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
967 CaptureKind::Value | CaptureKind::Use => break,
968 capture @ CaptureKind::Ref(_) => return capture,
969 },
970 _ => break,
971 }
972
973 child_id = parent_id;
974 }
975
976 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
977 CaptureKind::Ref(Mutability::Not)
979 } else {
980 capture
981 }
982}
983
984pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
987 struct V<'cx, 'tcx> {
988 cx: &'cx LateContext<'tcx>,
989 loops: Vec<HirId>,
991 locals: HirIdSet,
993 allow_closure: bool,
995 captures: HirIdMap<CaptureKind>,
998 }
999 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1000 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1001 if !self.allow_closure {
1002 return;
1003 }
1004
1005 match e.kind {
1006 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1007 if !self.locals.contains(&l) {
1008 let cap = capture_local_usage(self.cx, e);
1009 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1010 }
1011 },
1012 ExprKind::Closure(closure) => {
1013 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
1014 let local_id = match capture.place.base {
1015 PlaceBase::Local(id) => id,
1016 PlaceBase::Upvar(var) => var.var_path.hir_id,
1017 _ => continue,
1018 };
1019 if !self.locals.contains(&local_id) {
1020 let capture = match capture.info.capture_kind {
1021 UpvarCapture::ByValue => CaptureKind::Value,
1022 UpvarCapture::ByUse => CaptureKind::Use,
1023 UpvarCapture::ByRef(kind) => match kind {
1024 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
1025 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
1026 CaptureKind::Ref(Mutability::Mut)
1027 },
1028 },
1029 };
1030 self.captures
1031 .entry(local_id)
1032 .and_modify(|e| *e |= capture)
1033 .or_insert(capture);
1034 }
1035 }
1036 },
1037 ExprKind::Loop(b, ..) => {
1038 self.loops.push(e.hir_id);
1039 self.visit_block(b);
1040 self.loops.pop();
1041 },
1042 _ => {
1043 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1044 walk_expr(self, e);
1045 },
1046 }
1047 }
1048
1049 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1050 p.each_binding_or_first(&mut |_, id, _, _| {
1051 self.locals.insert(id);
1052 });
1053 }
1054 }
1055
1056 let mut v = V {
1057 cx,
1058 loops: Vec::new(),
1059 locals: HirIdSet::default(),
1060 allow_closure: true,
1061 captures: HirIdMap::default(),
1062 };
1063 v.visit_expr(expr);
1064 v.allow_closure.then_some(v.captures)
1065}
1066
1067pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1069
1070pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1073 let mut method_names = Vec::with_capacity(max_depth);
1074 let mut arg_lists = Vec::with_capacity(max_depth);
1075 let mut spans = Vec::with_capacity(max_depth);
1076
1077 let mut current = expr;
1078 for _ in 0..max_depth {
1079 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1080 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1081 break;
1082 }
1083 method_names.push(path.ident.name);
1084 arg_lists.push((*receiver, &**args));
1085 spans.push(path.ident.span);
1086 current = receiver;
1087 } else {
1088 break;
1089 }
1090 }
1091
1092 (method_names, arg_lists, spans)
1093}
1094
1095pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1102 let mut current = expr;
1103 let mut matched = Vec::with_capacity(methods.len());
1104 for method_name in methods.iter().rev() {
1105 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1107 if path.ident.name == *method_name {
1108 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1109 return None;
1110 }
1111 matched.push((receiver, args)); current = receiver; } else {
1114 return None;
1115 }
1116 } else {
1117 return None;
1118 }
1119 }
1120 matched.reverse();
1122 Some(matched)
1123}
1124
1125pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1127 cx.tcx
1128 .entry_fn(())
1129 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1130}
1131
1132pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1134 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1135 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1136}
1137
1138pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1140 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1141 match cx.tcx.hir_node_by_def_id(parent_id) {
1142 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1143 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1144 _ => None,
1145 }
1146}
1147
1148pub struct ContainsName<'a, 'tcx> {
1149 pub cx: &'a LateContext<'tcx>,
1150 pub name: Symbol,
1151}
1152
1153impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1154 type Result = ControlFlow<()>;
1155 type NestedFilter = nested_filter::OnlyBodies;
1156
1157 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1158 if self.name == name {
1159 ControlFlow::Break(())
1160 } else {
1161 ControlFlow::Continue(())
1162 }
1163 }
1164
1165 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1166 self.cx.tcx
1167 }
1168}
1169
1170pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1172 let mut cn = ContainsName { cx, name };
1173 cn.visit_expr(expr).is_break()
1174}
1175
1176pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1178 for_each_expr_without_closures(expr, |e| {
1179 if matches!(e.kind, ExprKind::Ret(..)) {
1180 ControlFlow::Break(())
1181 } else {
1182 ControlFlow::Continue(())
1183 }
1184 })
1185 .is_some()
1186}
1187
1188pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1190 get_parent_expr_for_hir(cx, e.hir_id)
1191}
1192
1193pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1196 match cx.tcx.parent_hir_node(hir_id) {
1197 Node::Expr(parent) => Some(parent),
1198 _ => None,
1199 }
1200}
1201
1202pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1204 let enclosing_node = cx
1205 .tcx
1206 .hir_get_enclosing_scope(hir_id)
1207 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1208 enclosing_node.and_then(|node| match node {
1209 Node::Block(block) => Some(block),
1210 Node::Item(&Item {
1211 kind: ItemKind::Fn { body: eid, .. },
1212 ..
1213 })
1214 | Node::ImplItem(&ImplItem {
1215 kind: ImplItemKind::Fn(_, eid),
1216 ..
1217 })
1218 | Node::TraitItem(&TraitItem {
1219 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1220 ..
1221 }) => match cx.tcx.hir_body(eid).value.kind {
1222 ExprKind::Block(block, _) => Some(block),
1223 _ => None,
1224 },
1225 _ => None,
1226 })
1227}
1228
1229pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1231 cx: &LateContext<'tcx>,
1232 expr: &Expr<'_>,
1233) -> Option<&'tcx Expr<'tcx>> {
1234 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1235 match node {
1236 Node::Expr(e) => match e.kind {
1237 ExprKind::Closure { .. }
1238 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1239 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1240
1241 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1243 _ => (),
1244 },
1245 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1246 _ => break,
1247 }
1248 }
1249 None
1250}
1251
1252pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1254 match tcx.hir_parent_iter(id).next() {
1255 Some((
1256 _,
1257 Node::Item(Item {
1258 kind: ItemKind::Impl(imp),
1259 ..
1260 }),
1261 )) => Some(imp),
1262 _ => None,
1263 }
1264}
1265
1266pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1277 while let ExprKind::Block(
1278 Block {
1279 stmts: [],
1280 expr: Some(inner),
1281 rules: BlockCheckMode::DefaultBlock,
1282 ..
1283 },
1284 _,
1285 ) = expr.kind
1286 {
1287 expr = inner;
1288 }
1289 expr
1290}
1291
1292pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1303 while let ExprKind::Block(
1304 Block {
1305 stmts: [],
1306 expr: Some(inner),
1307 rules: BlockCheckMode::DefaultBlock,
1308 ..
1309 }
1310 | Block {
1311 stmts:
1312 [
1313 Stmt {
1314 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1315 ..
1316 },
1317 ],
1318 expr: None,
1319 rules: BlockCheckMode::DefaultBlock,
1320 ..
1321 },
1322 _,
1323 ) = expr.kind
1324 {
1325 expr = inner;
1326 }
1327 expr
1328}
1329
1330pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1332 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1333 match iter.next() {
1334 Some((
1335 _,
1336 Node::Expr(Expr {
1337 kind: ExprKind::If(_, _, Some(else_expr)),
1338 ..
1339 }),
1340 )) => else_expr.hir_id == expr.hir_id,
1341 _ => false,
1342 }
1343}
1344
1345pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1348 let mut child_id = expr.hir_id;
1349 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1350 if let Node::LetStmt(LetStmt {
1351 init: Some(init),
1352 els: Some(els),
1353 ..
1354 }) = node
1355 && (init.hir_id == child_id || els.hir_id == child_id)
1356 {
1357 return true;
1358 }
1359
1360 child_id = parent_id;
1361 }
1362
1363 false
1364}
1365
1366pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1368 let mut child_id = expr.hir_id;
1369 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1370 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1371 && els.hir_id == child_id
1372 {
1373 return true;
1374 }
1375
1376 child_id = parent_id;
1377 }
1378
1379 false
1380}
1381
1382pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1397 let ty = cx.typeck_results().expr_ty(expr);
1398 if let Some(Range { start, end, limits }) = Range::hir(expr) {
1399 let start_is_none_or_min = start.is_none_or(|start| {
1400 if let rustc_ty::Adt(_, subst) = ty.kind()
1401 && let bnd_ty = subst.type_at(0)
1402 && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx)
1403 && let Some(min_const) = mir_to_const(cx.tcx, min_const)
1404 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1405 {
1406 start_const == min_const
1407 } else {
1408 false
1409 }
1410 });
1411 let end_is_none_or_max = end.is_none_or(|end| match limits {
1412 RangeLimits::Closed => {
1413 if let rustc_ty::Adt(_, subst) = ty.kind()
1414 && let bnd_ty = subst.type_at(0)
1415 && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx)
1416 && let Some(max_const) = mir_to_const(cx.tcx, max_const)
1417 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1418 {
1419 end_const == max_const
1420 } else {
1421 false
1422 }
1423 },
1424 RangeLimits::HalfOpen => {
1425 if let Some(container_path) = container_path
1426 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1427 && name.ident.name == sym::len
1428 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1429 {
1430 container_path.res == path.res
1431 } else {
1432 false
1433 }
1434 },
1435 });
1436 return start_is_none_or_min && end_is_none_or_max;
1437 }
1438 false
1439}
1440
1441pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1444 if is_integer_literal(e, value) {
1445 return true;
1446 }
1447 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1448 if let Some(Constant::Int(v)) =
1449 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1450 {
1451 return value == v;
1452 }
1453 false
1454}
1455
1456pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1458 if let ExprKind::Lit(spanned) = expr.kind
1460 && let LitKind::Int(v, _) = spanned.node
1461 {
1462 return v == value;
1463 }
1464 false
1465}
1466
1467pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1469 if let ExprKind::Lit(spanned) = expr.kind
1470 && let LitKind::Float(v, _) = spanned.node
1471 {
1472 v.as_str().parse() == Ok(value)
1473 } else {
1474 false
1475 }
1476}
1477
1478pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1486 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1487}
1488
1489#[must_use]
1493pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1494 loop {
1495 if span.from_expansion() {
1496 let data = span.ctxt().outer_expn_data();
1497 let new_span = data.call_site;
1498
1499 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1500 && mac_name == name
1501 {
1502 return Some(new_span);
1503 }
1504
1505 span = new_span;
1506 } else {
1507 return None;
1508 }
1509 }
1510}
1511
1512#[must_use]
1523pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1524 if span.from_expansion() {
1525 let data = span.ctxt().outer_expn_data();
1526 let new_span = data.call_site;
1527
1528 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1529 && mac_name == name
1530 {
1531 return Some(new_span);
1532 }
1533 }
1534
1535 None
1536}
1537
1538pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1540 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1541 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1542}
1543
1544pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1546 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1547 cx.tcx.instantiate_bound_regions_with_erased(arg)
1548}
1549
1550pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1552 if let ExprKind::Call(fun, _) = expr.kind
1553 && let ExprKind::Path(ref qp) = fun.kind
1554 {
1555 let res = cx.qpath_res(qp, fun.hir_id);
1556 return match res {
1557 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1558 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1559 _ => false,
1560 };
1561 }
1562 false
1563}
1564
1565pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1568 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1569 !matches!(
1570 cx.qpath_res(qpath, id),
1571 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1572 )
1573 }
1574
1575 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1576 i.into_iter().any(|pat| is_refutable(cx, pat))
1577 }
1578
1579 match pat.kind {
1580 PatKind::Missing => unreachable!(),
1581 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1583 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1584 PatKind::Expr(PatExpr {
1585 kind: PatExprKind::Path(qpath),
1586 hir_id,
1587 ..
1588 }) => is_qpath_refutable(cx, qpath, *hir_id),
1589 PatKind::Or(pats) => {
1590 are_refutable(cx, pats)
1592 },
1593 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1594 PatKind::Struct(ref qpath, fields, _) => {
1595 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1596 },
1597 PatKind::TupleStruct(ref qpath, pats, _) => {
1598 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1599 },
1600 PatKind::Slice(head, middle, tail) => {
1601 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1602 rustc_ty::Slice(..) => {
1603 !head.is_empty() || middle.is_none() || !tail.is_empty()
1605 },
1606 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1607 _ => {
1608 true
1610 },
1611 }
1612 },
1613 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1614 }
1615}
1616
1617pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1620 if let PatKind::Or(pats) = pat.kind {
1621 pats.iter().for_each(f);
1622 } else {
1623 f(pat);
1624 }
1625}
1626
1627pub fn is_self(slf: &Param<'_>) -> bool {
1628 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1629 name.name == kw::SelfLower
1630 } else {
1631 false
1632 }
1633}
1634
1635pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1636 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1637 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1638 {
1639 return true;
1640 }
1641 false
1642}
1643
1644pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1645 (0..decl.inputs.len()).map(move |i| &body.params[i])
1646}
1647
1648pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1651 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1652 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1653 && ddpos.as_opt_usize().is_none()
1654 && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk)
1655 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1656 && path_to_local_id(arm.body, hir_id)
1657 {
1658 return true;
1659 }
1660 false
1661 }
1662
1663 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1664 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1665 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1666 } else {
1667 false
1668 }
1669 }
1670
1671 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1672 if let MatchSource::TryDesugar(_) = *source {
1674 return Some(expr);
1675 }
1676
1677 if arms.len() == 2
1678 && arms[0].guard.is_none()
1679 && arms[1].guard.is_none()
1680 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1681 {
1682 return Some(expr);
1683 }
1684 }
1685
1686 None
1687}
1688
1689pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1699 let mut suppress_lint = false;
1700
1701 for id in ids {
1702 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1703 if let Some(expectation) = lint_id {
1704 cx.fulfill_expectation(expectation);
1705 }
1706
1707 match level {
1708 Level::Allow | Level::Expect => suppress_lint = true,
1709 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1710 }
1711 }
1712
1713 suppress_lint
1714}
1715
1716pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1724 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1725}
1726
1727pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1728 while let PatKind::Ref(subpat, _) = pat.kind {
1729 pat = subpat;
1730 }
1731 pat
1732}
1733
1734pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1735 Integer::from_int_ty(&tcx, ity).size().bits()
1736}
1737
1738#[expect(clippy::cast_possible_wrap)]
1739pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1741 let amt = 128 - int_bits(tcx, ity);
1742 ((u as i128) << amt) >> amt
1743}
1744
1745#[expect(clippy::cast_sign_loss)]
1746pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1748 let amt = 128 - int_bits(tcx, ity);
1749 ((u as u128) << amt) >> amt
1750}
1751
1752pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1754 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1755 let amt = 128 - bits;
1756 (u << amt) >> amt
1757}
1758
1759pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1760 attrs.iter().any(|attr| attr.has_name(symbol))
1761}
1762
1763pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1764 find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr(..))
1765}
1766
1767pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1768 let mut prev_enclosing_node = None;
1769 let mut enclosing_node = node;
1770 while Some(enclosing_node) != prev_enclosing_node {
1771 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1772 return true;
1773 }
1774 prev_enclosing_node = Some(enclosing_node);
1775 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1776 }
1777
1778 false
1779}
1780
1781pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1784 tcx.hir_parent_owner_iter(id)
1785 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1786 .any(|(id, _)| {
1787 has_attr(
1788 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1789 sym::automatically_derived,
1790 )
1791 })
1792}
1793
1794pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1796 let path = cx.get_def_path(did);
1797 path.first().is_some_and(|s| *s == sym::libc) && path.last().copied() == Some(name)
1800}
1801
1802pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1807 let mut conds = Vec::new();
1808 let mut blocks: Vec<&Block<'_>> = Vec::new();
1809
1810 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1811 conds.push(cond);
1812 if let ExprKind::Block(block, _) = then.kind {
1813 blocks.push(block);
1814 } else {
1815 panic!("ExprKind::If node is not an ExprKind::Block");
1816 }
1817
1818 if let Some(else_expr) = r#else {
1819 expr = else_expr;
1820 } else {
1821 break;
1822 }
1823 }
1824
1825 if !blocks.is_empty()
1827 && let ExprKind::Block(block, _) = expr.kind
1828 {
1829 blocks.push(block);
1830 }
1831
1832 (conds, blocks)
1833}
1834
1835pub fn is_async_fn(kind: FnKind<'_>) -> bool {
1837 match kind {
1838 FnKind::ItemFn(_, _, header) => header.asyncness.is_async(),
1839 FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
1840 FnKind::Closure => false,
1841 }
1842}
1843
1844pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1846 if let ExprKind::Closure(&Closure {
1847 body,
1848 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1849 ..
1850 }) = expr.kind
1851 && let ExprKind::Block(
1852 Block {
1853 expr:
1854 Some(Expr {
1855 kind: ExprKind::DropTemps(inner_expr),
1856 ..
1857 }),
1858 ..
1859 },
1860 _,
1861 ) = tcx.hir_body(body).value.kind
1862 {
1863 Some(inner_expr)
1864 } else {
1865 None
1866 }
1867}
1868
1869pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1871 get_async_closure_expr(tcx, body.value)
1872}
1873
1874pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1876 let did = match expr.kind {
1877 ExprKind::Call(path, _) => {
1878 if let ExprKind::Path(ref qpath) = path.kind
1879 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1880 {
1881 Some(did)
1882 } else {
1883 None
1884 }
1885 },
1886 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1887 _ => None,
1888 };
1889
1890 did.is_some_and(|did| cx.tcx.has_attr(did, sym::must_use))
1891}
1892
1893fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1902 fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
1903 if cx
1904 .typeck_results()
1905 .pat_binding_modes()
1906 .get(pat.hir_id)
1907 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
1908 {
1909 return false;
1913 }
1914
1915 match (pat.kind, expr.kind) {
1916 (PatKind::Binding(_, id, _, _), _) => {
1917 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1918 },
1919 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1920 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1921 {
1922 pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr))
1923 },
1924 _ => false,
1925 }
1926 }
1927
1928 let [param] = func.params else {
1929 return false;
1930 };
1931
1932 let mut expr = func.value;
1933 loop {
1934 match expr.kind {
1935 ExprKind::Block(
1936 &Block {
1937 stmts: [],
1938 expr: Some(e),
1939 ..
1940 },
1941 _,
1942 )
1943 | ExprKind::Ret(Some(e)) => expr = e,
1944 ExprKind::Block(
1945 &Block {
1946 stmts: [stmt],
1947 expr: None,
1948 ..
1949 },
1950 _,
1951 ) => {
1952 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1953 && let ExprKind::Ret(Some(ret_val)) = e.kind
1954 {
1955 expr = ret_val;
1956 } else {
1957 return false;
1958 }
1959 },
1960 _ => return check_pat(cx, param.pat, expr),
1961 }
1962 }
1963}
1964
1965pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1970 match expr.kind {
1971 ExprKind::Closure(&Closure { body, fn_decl, .. })
1972 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1973 {
1974 is_body_identity_function(cx, cx.tcx.hir_body(body))
1975 },
1976 ExprKind::Path(QPath::Resolved(_, path))
1977 if path.segments.iter().all(|seg| seg.infer_args)
1978 && let Some(did) = path.res.opt_def_id() =>
1979 {
1980 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1981 },
1982 _ => false,
1983 }
1984}
1985
1986pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1995 match expr.kind {
1996 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1997 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
1998 }
1999}
2000
2001pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2004 let mut child_id = expr.hir_id;
2005 let mut iter = tcx.hir_parent_iter(child_id);
2006 loop {
2007 match iter.next() {
2008 None => break None,
2009 Some((id, Node::Block(_))) => child_id = id,
2010 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2011 Some((_, Node::Expr(expr))) => match expr.kind {
2012 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2013 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2014 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2015 _ => break Some((Node::Expr(expr), child_id)),
2016 },
2017 Some((_, node)) => break Some((node, child_id)),
2018 }
2019 }
2020}
2021
2022pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2024 !matches!(
2025 get_expr_use_or_unification_node(tcx, expr),
2026 None | Some((
2027 Node::Stmt(Stmt {
2028 kind: StmtKind::Expr(_)
2029 | StmtKind::Semi(_)
2030 | StmtKind::Let(LetStmt {
2031 pat: Pat {
2032 kind: PatKind::Wild,
2033 ..
2034 },
2035 ..
2036 }),
2037 ..
2038 }),
2039 _
2040 ))
2041 )
2042}
2043
2044pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2046 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2047}
2048
2049pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2053 !expr.is_place_expr(|base| {
2054 cx.typeck_results()
2055 .adjustments()
2056 .get(base.hir_id)
2057 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2058 })
2059}
2060
2061pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2062 if !is_no_std_crate(cx) {
2063 Some("std")
2064 } else if !is_no_core_crate(cx) {
2065 Some("core")
2066 } else {
2067 None
2068 }
2069}
2070
2071pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2072 cx.tcx
2073 .hir_attrs(hir::CRATE_HIR_ID)
2074 .iter()
2075 .any(|attr| attr.has_name(sym::no_std))
2076}
2077
2078pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2079 cx.tcx
2080 .hir_attrs(hir::CRATE_HIR_ID)
2081 .iter()
2082 .any(|attr| attr.has_name(sym::no_core))
2083}
2084
2085pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2095 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2096 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2097 } else {
2098 false
2099 }
2100}
2101
2102pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2112 use rustc_trait_selection::traits;
2113 let predicates = cx
2114 .tcx
2115 .predicates_of(did)
2116 .predicates
2117 .iter()
2118 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2119 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2120}
2121
2122pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2124 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2125}
2126
2127pub fn fn_def_id_with_node_args<'tcx>(
2130 cx: &LateContext<'tcx>,
2131 expr: &Expr<'_>,
2132) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2133 let typeck = cx.typeck_results();
2134 match &expr.kind {
2135 ExprKind::MethodCall(..) => Some((
2136 typeck.type_dependent_def_id(expr.hir_id)?,
2137 typeck.node_args(expr.hir_id),
2138 )),
2139 ExprKind::Call(
2140 Expr {
2141 kind: ExprKind::Path(qpath),
2142 hir_id: path_hir_id,
2143 ..
2144 },
2145 ..,
2146 ) => {
2147 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2150 typeck.qpath_res(qpath, *path_hir_id)
2151 {
2152 Some((id, typeck.node_args(*path_hir_id)))
2153 } else {
2154 None
2155 }
2156 },
2157 _ => None,
2158 }
2159}
2160
2161pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2166 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2167 let expr_kind = expr_type.kind();
2168 let is_primitive = match expr_kind {
2169 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2170 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2171 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2172 is_recursively_primitive_type(*element_type)
2173 } else {
2174 unreachable!()
2175 }
2176 },
2177 _ => false,
2178 };
2179
2180 if is_primitive {
2181 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2184 rustc_ty::Slice(..) => return Some("slice".into()),
2185 rustc_ty::Array(..) => return Some("array".into()),
2186 rustc_ty::Tuple(..) => return Some("tuple".into()),
2187 _ => {
2188 let refs_peeled = expr_type.peel_refs();
2191 return Some(refs_peeled.walk().last().unwrap().to_string());
2192 },
2193 }
2194 }
2195 None
2196}
2197
2198pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2206where
2207 Hash: FnMut(&T) -> u64,
2208 Eq: FnMut(&T, &T) -> bool,
2209{
2210 match exprs {
2211 [a, b] if eq(a, b) => return vec![vec![a, b]],
2212 _ if exprs.len() <= 2 => return vec![],
2213 _ => {},
2214 }
2215
2216 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2217
2218 for expr in exprs {
2219 match buckets.entry(hash(expr)) {
2220 indexmap::map::Entry::Occupied(mut o) => {
2221 let bucket = o.get_mut();
2222 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2223 Some(group) => group.push(expr),
2224 None => bucket.push(vec![expr]),
2225 }
2226 },
2227 indexmap::map::Entry::Vacant(v) => {
2228 v.insert(vec![vec![expr]]);
2229 },
2230 }
2231 }
2232
2233 buckets
2234 .into_values()
2235 .flatten()
2236 .filter(|group| group.len() > 1)
2237 .collect()
2238}
2239
2240pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2243 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2244 if let PatKind::Ref(pat, _) = pat.kind {
2245 peel(pat, count + 1)
2246 } else {
2247 (pat, count)
2248 }
2249 }
2250 peel(pat, 0)
2251}
2252
2253pub fn peel_hir_expr_while<'tcx>(
2255 mut expr: &'tcx Expr<'tcx>,
2256 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2257) -> &'tcx Expr<'tcx> {
2258 while let Some(e) = f(expr) {
2259 expr = e;
2260 }
2261 expr
2262}
2263
2264pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2267 let mut remaining = count;
2268 let e = peel_hir_expr_while(expr, |e| match e.kind {
2269 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2270 remaining -= 1;
2271 Some(e)
2272 },
2273 _ => None,
2274 });
2275 (e, count - remaining)
2276}
2277
2278pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2281 let mut count: usize = 0;
2282 let mut curr_expr = expr;
2283 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2284 count = count.wrapping_add(1);
2285 curr_expr = local_expr;
2286 }
2287 (curr_expr, count)
2288}
2289
2290pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2293 let mut count = 0;
2294 let e = peel_hir_expr_while(expr, |e| match e.kind {
2295 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2296 count += 1;
2297 Some(e)
2298 },
2299 _ => None,
2300 });
2301 (e, count)
2302}
2303
2304pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2307 let mut count = 0;
2308 loop {
2309 match &ty.kind {
2310 TyKind::Ref(_, ref_ty) => {
2311 ty = ref_ty.ty;
2312 count += 1;
2313 },
2314 _ => break (ty, count),
2315 }
2316 }
2317}
2318
2319pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2322 let mut count = 0;
2323 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2324 ty = *dest_ty;
2325 count += 1;
2326 }
2327 (ty, count)
2328}
2329
2330pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2333 loop {
2334 match expr.kind {
2335 ExprKind::AddrOf(_, _, e) => expr = e,
2336 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2337 _ => break,
2338 }
2339 }
2340 expr
2341}
2342
2343pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2344 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2345 && let Res::Def(_, def_id) = path.res
2346 {
2347 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2348 }
2349 false
2350}
2351
2352static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2353
2354fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2357 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2358 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2359 let value = map.entry(module);
2360 match value {
2361 Entry::Occupied(entry) => f(entry.get()),
2362 Entry::Vacant(entry) => {
2363 let mut names = Vec::new();
2364 for id in tcx.hir_module_free_items(module) {
2365 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2366 && let item = tcx.hir_item(id)
2367 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2368 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2369 && let Res::Def(DefKind::Struct, _) = path.res
2371 {
2372 let has_test_marker = tcx
2373 .hir_attrs(item.hir_id())
2374 .iter()
2375 .any(|a| a.has_name(sym::rustc_test_marker));
2376 if has_test_marker {
2377 names.push(ident.name);
2378 }
2379 }
2380 }
2381 names.sort_unstable();
2382 f(entry.insert(names))
2383 },
2384 }
2385}
2386
2387pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2391 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2392 let node = tcx.hir_node(id);
2393 once((id, node))
2394 .chain(tcx.hir_parent_iter(id))
2395 .any(|(_id, node)| {
2398 if let Node::Item(item) = node
2399 && let ItemKind::Fn { ident, .. } = item.kind
2400 {
2401 return names.binary_search(&ident.name).is_ok();
2404 }
2405 false
2406 })
2407 })
2408}
2409
2410pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2417 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2418 if let Node::Item(item) = tcx.hir_node(id)
2419 && let ItemKind::Fn { ident, .. } = item.kind
2420 {
2421 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2422 names.binary_search(&ident.name).is_ok()
2423 })
2424 } else {
2425 false
2426 }
2427}
2428
2429pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2434 tcx.hir_attrs(id).iter().any(|attr| {
2435 if attr.has_name(sym::cfg_trace)
2436 && let Some(items) = attr.meta_item_list()
2437 && let [item] = &*items
2438 && item.has_name(sym::test)
2439 {
2440 true
2441 } else {
2442 false
2443 }
2444 })
2445}
2446
2447pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2449 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2450}
2451
2452pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2454 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2455}
2456
2457pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2459 tcx.has_attr(def_id, sym::cfg_trace)
2460 || tcx
2461 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2462 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2463 .any(|attr| attr.has_name(sym::cfg_trace))
2464}
2465
2466pub fn walk_to_expr_usage<'tcx, T>(
2477 cx: &LateContext<'tcx>,
2478 e: &Expr<'tcx>,
2479 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2480) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2481 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2482 let mut child_id = e.hir_id;
2483
2484 while let Some((parent_id, parent)) = iter.next() {
2485 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2486 return Some(ControlFlow::Break(x));
2487 }
2488 let parent_expr = match parent {
2489 Node::Expr(e) => e,
2490 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2491 child_id = parent_id;
2492 continue;
2493 },
2494 Node::Arm(a) if a.body.hir_id == child_id => {
2495 child_id = parent_id;
2496 continue;
2497 },
2498 _ => return Some(ControlFlow::Continue((parent, child_id))),
2499 };
2500 match parent_expr.kind {
2501 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2502 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2503 child_id = id;
2504 iter = cx.tcx.hir_parent_iter(id);
2505 },
2506 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2507 _ => return Some(ControlFlow::Continue((parent, child_id))),
2508 }
2509 }
2510 debug_assert!(false, "no parent node found for `{child_id:?}`");
2511 None
2512}
2513
2514#[derive(Clone, Copy)]
2516pub enum DefinedTy<'tcx> {
2517 Hir(&'tcx hir::Ty<'tcx>),
2519 Mir {
2527 def_site_def_id: Option<DefId>,
2528 ty: Binder<'tcx, Ty<'tcx>>,
2529 },
2530}
2531
2532pub struct ExprUseCtxt<'tcx> {
2534 pub node: Node<'tcx>,
2536 pub child_id: HirId,
2538 pub adjustments: &'tcx [Adjustment<'tcx>],
2540 pub is_ty_unified: bool,
2542 pub moved_before_use: bool,
2544 pub same_ctxt: bool,
2546}
2547impl<'tcx> ExprUseCtxt<'tcx> {
2548 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2549 match self.node {
2550 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2551 Node::ExprField(field) => ExprUseNode::Field(field),
2552
2553 Node::Item(&Item {
2554 kind: ItemKind::Static(..) | ItemKind::Const(..),
2555 owner_id,
2556 ..
2557 })
2558 | Node::TraitItem(&TraitItem {
2559 kind: TraitItemKind::Const(..),
2560 owner_id,
2561 ..
2562 })
2563 | Node::ImplItem(&ImplItem {
2564 kind: ImplItemKind::Const(..),
2565 owner_id,
2566 ..
2567 }) => ExprUseNode::ConstStatic(owner_id),
2568
2569 Node::Item(&Item {
2570 kind: ItemKind::Fn { .. },
2571 owner_id,
2572 ..
2573 })
2574 | Node::TraitItem(&TraitItem {
2575 kind: TraitItemKind::Fn(..),
2576 owner_id,
2577 ..
2578 })
2579 | Node::ImplItem(&ImplItem {
2580 kind: ImplItemKind::Fn(..),
2581 owner_id,
2582 ..
2583 }) => ExprUseNode::Return(owner_id),
2584
2585 Node::Expr(use_expr) => match use_expr.kind {
2586 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2587 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2588 }),
2589
2590 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2591 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2592 Some(i) => ExprUseNode::FnArg(func, i),
2593 None => ExprUseNode::Callee,
2594 },
2595 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2596 use_expr.hir_id,
2597 name.args,
2598 args.iter()
2599 .position(|arg| arg.hir_id == self.child_id)
2600 .map_or(0, |i| i + 1),
2601 ),
2602 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2603 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2604 _ => ExprUseNode::Other,
2605 },
2606 _ => ExprUseNode::Other,
2607 }
2608 }
2609}
2610
2611pub enum ExprUseNode<'tcx> {
2613 LetStmt(&'tcx LetStmt<'tcx>),
2615 ConstStatic(OwnerId),
2617 Return(OwnerId),
2619 Field(&'tcx ExprField<'tcx>),
2621 FnArg(&'tcx Expr<'tcx>, usize),
2623 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2625 Callee,
2627 FieldAccess(Ident),
2629 AddrOf(ast::BorrowKind, Mutability),
2631 Other,
2632}
2633impl<'tcx> ExprUseNode<'tcx> {
2634 pub fn is_return(&self) -> bool {
2636 matches!(self, Self::Return(_))
2637 }
2638
2639 pub fn is_recv(&self) -> bool {
2641 matches!(self, Self::MethodArg(_, _, 0))
2642 }
2643
2644 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2646 match *self {
2647 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2648 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2649 def_site_def_id: Some(id.def_id.to_def_id()),
2650 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2651 }),
2652 Self::Return(id) => {
2653 if let Node::Expr(Expr {
2654 kind: ExprKind::Closure(c),
2655 ..
2656 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2657 {
2658 match c.fn_decl.output {
2659 FnRetTy::DefaultReturn(_) => None,
2660 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2661 }
2662 } else {
2663 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2664 Some(DefinedTy::Mir {
2665 def_site_def_id: Some(id.def_id.to_def_id()),
2666 ty,
2667 })
2668 }
2669 },
2670 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2671 Some(Expr {
2672 hir_id,
2673 kind: ExprKind::Struct(path, ..),
2674 ..
2675 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2676 .and_then(|(adt, variant)| {
2677 variant
2678 .fields
2679 .iter()
2680 .find(|f| f.name == field.ident.name)
2681 .map(|f| (adt, f))
2682 })
2683 .map(|(adt, field_def)| DefinedTy::Mir {
2684 def_site_def_id: Some(adt.did()),
2685 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2686 }),
2687 _ => None,
2688 },
2689 Self::FnArg(callee, i) => {
2690 let sig = expr_sig(cx, callee)?;
2691 let (hir_ty, ty) = sig.input_with_hir(i)?;
2692 Some(match hir_ty {
2693 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2694 None => DefinedTy::Mir {
2695 def_site_def_id: sig.predicates_id(),
2696 ty,
2697 },
2698 })
2699 },
2700 Self::MethodArg(id, _, i) => {
2701 let id = cx.typeck_results().type_dependent_def_id(id)?;
2702 let sig = cx.tcx.fn_sig(id).skip_binder();
2703 Some(DefinedTy::Mir {
2704 def_site_def_id: Some(id),
2705 ty: sig.input(i),
2706 })
2707 },
2708 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2709 }
2710 }
2711}
2712
2713pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2715 let mut adjustments = [].as_slice();
2716 let mut is_ty_unified = false;
2717 let mut moved_before_use = false;
2718 let mut same_ctxt = true;
2719 let ctxt = e.span.ctxt();
2720 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2721 if adjustments.is_empty()
2722 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2723 {
2724 adjustments = cx.typeck_results().expr_adjustments(e);
2725 }
2726 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2727 if let Node::Expr(e) = parent {
2728 match e.kind {
2729 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2730 is_ty_unified = true;
2731 moved_before_use = true;
2732 },
2733 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2734 is_ty_unified = true;
2735 moved_before_use = true;
2736 },
2737 ExprKind::Block(..) => moved_before_use = true,
2738 _ => {},
2739 }
2740 }
2741 ControlFlow::Continue(())
2742 });
2743 match node {
2744 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2745 node,
2746 child_id,
2747 adjustments,
2748 is_ty_unified,
2749 moved_before_use,
2750 same_ctxt,
2751 },
2752 #[allow(unreachable_patterns)]
2753 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
2754 None => ExprUseCtxt {
2755 node: Node::Crate(cx.tcx.hir_root_module()),
2756 child_id: HirId::INVALID,
2757 adjustments: &[],
2758 is_ty_unified: true,
2759 moved_before_use: true,
2760 same_ctxt: false,
2761 },
2762 }
2763}
2764
2765pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2767 let mut pos = 0;
2768 tokenize(s).map(move |t| {
2769 let end = pos + t.len;
2770 let range = pos as usize..end as usize;
2771 let inner = InnerSpan::new(range.start, range.end);
2772 pos = end;
2773 (t.kind, s.get(range).unwrap_or_default(), inner)
2774 })
2775}
2776
2777pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2780 let Ok(snippet) = sm.span_to_snippet(span) else {
2781 return false;
2782 };
2783 return tokenize(&snippet).any(|token| {
2784 matches!(
2785 token.kind,
2786 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2787 )
2788 });
2789}
2790
2791pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2795 span_extract_comments(sm, span).join("\n")
2796}
2797
2798pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2802 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2803 tokenize_with_text(&snippet)
2804 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2805 .map(|(_, s, _)| s.to_string())
2806 .collect::<Vec<_>>()
2807}
2808
2809pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2810 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2811}
2812
2813pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2838 cx: &LateContext<'_>,
2839 pat: &'a Pat<'hir>,
2840 else_body: &Expr<'_>,
2841) -> Option<&'a Pat<'hir>> {
2842 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
2843 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
2844 && !is_refutable(cx, inner_pat)
2845 && let else_body = peel_blocks(else_body)
2846 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2847 && let ExprKind::Path(ret_path) = ret_val.kind
2848 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
2849 {
2850 Some(inner_pat)
2851 } else {
2852 None
2853 }
2854}
2855
2856macro_rules! op_utils {
2857 ($($name:ident $assign:ident)*) => {
2858 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2860
2861 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2863
2864 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2866 match kind {
2867 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2868 _ => None,
2869 }
2870 }
2871 };
2872}
2873
2874op_utils! {
2875 Add AddAssign
2876 Sub SubAssign
2877 Mul MulAssign
2878 Div DivAssign
2879 Rem RemAssign
2880 BitXor BitXorAssign
2881 BitAnd BitAndAssign
2882 BitOr BitOrAssign
2883 Shl ShlAssign
2884 Shr ShrAssign
2885}
2886
2887pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2890 match *pat {
2891 PatKind::Wild => true,
2892 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2893 !visitors::is_local_used(cx, body, id)
2894 },
2895 _ => false,
2896 }
2897}
2898
2899#[derive(Clone, Copy)]
2900pub enum RequiresSemi {
2901 Yes,
2902 No,
2903}
2904impl RequiresSemi {
2905 pub fn requires_semi(self) -> bool {
2906 matches!(self, Self::Yes)
2907 }
2908}
2909
2910#[expect(clippy::too_many_lines)]
2913pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2914 struct BreakTarget {
2915 id: HirId,
2916 unused: bool,
2917 }
2918
2919 struct V<'cx, 'tcx> {
2920 cx: &'cx LateContext<'tcx>,
2921 break_targets: Vec<BreakTarget>,
2922 break_targets_for_result_ty: u32,
2923 in_final_expr: bool,
2924 requires_semi: bool,
2925 is_never: bool,
2926 }
2927
2928 impl V<'_, '_> {
2929 fn push_break_target(&mut self, id: HirId) {
2930 self.break_targets.push(BreakTarget { id, unused: true });
2931 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2932 }
2933 }
2934
2935 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
2936 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
2937 if self.is_never && self.break_targets.is_empty() {
2954 if self.in_final_expr && !self.requires_semi {
2955 match e.kind {
2958 ExprKind::DropTemps(e) => self.visit_expr(e),
2959 ExprKind::If(_, then, Some(else_)) => {
2960 self.visit_expr(then);
2961 self.visit_expr(else_);
2962 },
2963 ExprKind::Match(_, arms, _) => {
2964 for arm in arms {
2965 self.visit_expr(arm.body);
2966 }
2967 },
2968 ExprKind::Loop(b, ..) => {
2969 self.push_break_target(e.hir_id);
2970 self.in_final_expr = false;
2971 self.visit_block(b);
2972 self.break_targets.pop();
2973 },
2974 ExprKind::Block(b, _) => {
2975 if b.targeted_by_break {
2976 self.push_break_target(b.hir_id);
2977 self.visit_block(b);
2978 self.break_targets.pop();
2979 } else {
2980 self.visit_block(b);
2981 }
2982 },
2983 _ => {
2984 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
2985 },
2986 }
2987 }
2988 return;
2989 }
2990 match e.kind {
2991 ExprKind::DropTemps(e) => self.visit_expr(e),
2992 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
2993 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
2994 self.in_final_expr = false;
2995 self.visit_expr(e);
2996 self.is_never = true;
2997 },
2998 ExprKind::Break(dest, e) => {
2999 if let Some(e) = e {
3000 self.in_final_expr = false;
3001 self.visit_expr(e);
3002 }
3003 if let Ok(id) = dest.target_id
3004 && let Some((i, target)) = self
3005 .break_targets
3006 .iter_mut()
3007 .enumerate()
3008 .find(|(_, target)| target.id == id)
3009 {
3010 target.unused &= self.is_never;
3011 if i < self.break_targets_for_result_ty as usize {
3012 self.requires_semi = true;
3013 }
3014 }
3015 self.is_never = true;
3016 },
3017 ExprKind::If(cond, then, else_) => {
3018 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3019 self.visit_expr(cond);
3020 self.in_final_expr = in_final_expr;
3021
3022 if self.is_never {
3023 self.visit_expr(then);
3024 if let Some(else_) = else_ {
3025 self.visit_expr(else_);
3026 }
3027 } else {
3028 self.visit_expr(then);
3029 let is_never = mem::replace(&mut self.is_never, false);
3030 if let Some(else_) = else_ {
3031 self.visit_expr(else_);
3032 self.is_never &= is_never;
3033 }
3034 }
3035 },
3036 ExprKind::Match(scrutinee, arms, _) => {
3037 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3038 self.visit_expr(scrutinee);
3039 self.in_final_expr = in_final_expr;
3040
3041 if self.is_never {
3042 for arm in arms {
3043 self.visit_arm(arm);
3044 }
3045 } else {
3046 let mut is_never = true;
3047 for arm in arms {
3048 self.is_never = false;
3049 if let Some(guard) = arm.guard {
3050 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3051 self.visit_expr(guard);
3052 self.in_final_expr = in_final_expr;
3053 self.is_never = false;
3055 }
3056 self.visit_expr(arm.body);
3057 is_never &= self.is_never;
3058 }
3059 self.is_never = is_never;
3060 }
3061 },
3062 ExprKind::Loop(b, _, _, _) => {
3063 self.push_break_target(e.hir_id);
3064 self.in_final_expr = false;
3065 self.visit_block(b);
3066 self.is_never = self.break_targets.pop().unwrap().unused;
3067 },
3068 ExprKind::Block(b, _) => {
3069 if b.targeted_by_break {
3070 self.push_break_target(b.hir_id);
3071 self.visit_block(b);
3072 self.is_never &= self.break_targets.pop().unwrap().unused;
3073 } else {
3074 self.visit_block(b);
3075 }
3076 },
3077 _ => {
3078 self.in_final_expr = false;
3079 walk_expr(self, e);
3080 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3081 },
3082 }
3083 }
3084
3085 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3086 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3087 for s in b.stmts {
3088 self.visit_stmt(s);
3089 }
3090 self.in_final_expr = in_final_expr;
3091 if let Some(e) = b.expr {
3092 self.visit_expr(e);
3093 }
3094 }
3095
3096 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3097 if let Some(e) = l.init {
3098 self.visit_expr(e);
3099 }
3100 if let Some(else_) = l.els {
3101 let is_never = self.is_never;
3102 self.visit_block(else_);
3103 self.is_never = is_never;
3104 }
3105 }
3106
3107 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3108 if let Some(guard) = arm.guard {
3109 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3110 self.visit_expr(guard);
3111 self.in_final_expr = in_final_expr;
3112 }
3113 self.visit_expr(arm.body);
3114 }
3115 }
3116
3117 if cx.typeck_results().expr_ty(e).is_never() {
3118 Some(RequiresSemi::No)
3119 } else if let ExprKind::Block(b, _) = e.kind
3120 && !b.targeted_by_break
3121 && b.expr.is_none()
3122 {
3123 None
3125 } else {
3126 let mut v = V {
3127 cx,
3128 break_targets: Vec::new(),
3129 break_targets_for_result_ty: 0,
3130 in_final_expr: true,
3131 requires_semi: false,
3132 is_never: false,
3133 };
3134 v.visit_expr(e);
3135 v.is_never
3136 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3137 RequiresSemi::Yes
3138 } else {
3139 RequiresSemi::No
3140 })
3141 }
3142}
3143
3144pub fn get_path_from_caller_to_method_type<'tcx>(
3150 tcx: TyCtxt<'tcx>,
3151 from: LocalDefId,
3152 method: DefId,
3153 args: GenericArgsRef<'tcx>,
3154) -> String {
3155 let assoc_item = tcx.associated_item(method);
3156 let def_id = assoc_item.container_id(tcx);
3157 match assoc_item.container {
3158 rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
3159 rustc_ty::AssocItemContainer::Impl => {
3160 let ty = tcx.type_of(def_id).instantiate_identity();
3161 get_path_to_ty(tcx, from, ty, args)
3162 },
3163 }
3164}
3165
3166fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3167 match ty.kind() {
3168 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3169 rustc_ty::Array(..)
3171 | rustc_ty::Dynamic(..)
3172 | rustc_ty::Never
3173 | rustc_ty::RawPtr(_, _)
3174 | rustc_ty::Ref(..)
3175 | rustc_ty::Slice(_)
3176 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3177 _ => ty.to_string(),
3178 }
3179}
3180
3181fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3183 if callee.is_local() {
3185 let callee_path = tcx.def_path(callee);
3186 let caller_path = tcx.def_path(from.to_def_id());
3187 maybe_get_relative_path(&caller_path, &callee_path, 2)
3188 } else {
3189 tcx.def_path_str(callee)
3190 }
3191}
3192
3193fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3206 use itertools::EitherOrBoth::{Both, Left, Right};
3207
3208 let unique_parts = to
3210 .data
3211 .iter()
3212 .zip_longest(from.data.iter())
3213 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3214 .map(|el| match el {
3215 Both(l, r) => Both(l.data, r.data),
3216 Left(l) => Left(l.data),
3217 Right(r) => Right(r.data),
3218 });
3219
3220 let mut go_up_by = 0;
3222 let mut path = Vec::new();
3223 for el in unique_parts {
3224 match el {
3225 Both(l, r) => {
3226 if let DefPathData::TypeNs(s) = l {
3236 path.push(s.to_string());
3237 }
3238 if let DefPathData::TypeNs(_) = r {
3239 go_up_by += 1;
3240 }
3241 },
3242 Left(DefPathData::TypeNs(sym)) => path.push(sym.to_string()),
3247 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3252 _ => {},
3253 }
3254 }
3255
3256 if go_up_by > max_super {
3257 once(String::from("crate"))
3259 .chain(to.data.iter().filter_map(|el| {
3260 if let DefPathData::TypeNs(sym) = el.data {
3261 Some(sym.to_string())
3262 } else {
3263 None
3264 }
3265 }))
3266 .join("::")
3267 } else {
3268 repeat_n(String::from("super"), go_up_by).chain(path).join("::")
3269 }
3270}
3271
3272pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3275 matches!(
3276 cx.tcx.parent_hir_node(id),
3277 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3278 )
3279}
3280
3281pub fn is_block_like(expr: &Expr<'_>) -> bool {
3284 matches!(
3285 expr.kind,
3286 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3287 )
3288}
3289
3290pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3292 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3293 match expr.kind {
3294 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3295 _ if is_block_like(expr) => is_operand,
3296 _ => false,
3297 }
3298 }
3299
3300 contains_block(expr, false)
3301}
3302
3303pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3305 if let Some(parent_expr) = get_parent_expr(cx, expr)
3306 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3307 && receiver.hir_id == expr.hir_id
3308 {
3309 return true;
3310 }
3311 false
3312}
3313
3314pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3317 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3318 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3319 && temporary_ty
3320 .walk()
3321 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3322 {
3323 ControlFlow::Break(())
3324 } else {
3325 ControlFlow::Continue(())
3326 }
3327 })
3328 .is_break()
3329}
3330
3331pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3342 let expr_ty_is_adjusted = cx
3343 .typeck_results()
3344 .expr_adjustments(expr)
3345 .iter()
3346 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3348 if expr_ty_is_adjusted {
3349 return true;
3350 }
3351
3352 match expr.kind {
3355 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3356 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3357
3358 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3359 return false;
3360 }
3361
3362 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3363 let mut args_with_ty_param = {
3364 fn_sig
3365 .inputs()
3366 .skip_binder()
3367 .iter()
3368 .skip(self_arg_count)
3369 .zip(args)
3370 .filter_map(|(arg_ty, arg)| {
3371 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3372 Some(arg)
3373 } else {
3374 None
3375 }
3376 })
3377 };
3378 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3379 },
3380 ExprKind::Struct(qpath, _, _) => {
3382 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3383 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3384 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3385 return true;
3387 };
3388 v_def
3389 .fields
3390 .iter()
3391 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3392 } else {
3393 false
3394 }
3395 },
3396 ExprKind::Block(
3398 &Block {
3399 expr: Some(ret_expr), ..
3400 },
3401 _,
3402 )
3403 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3404
3405 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3407 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3409 ExprKind::If(_, then, maybe_else) => {
3411 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3412 },
3413 ExprKind::Match(_, arms, _) => arms
3414 .iter()
3415 .map(|arm| arm.body)
3416 .any(|body| expr_requires_coercion(cx, body)),
3417 _ => false,
3418 }
3419}
3420
3421pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3424 if let Some(hir_id) = path_to_local(expr)
3425 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3426 {
3427 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3428 } else if let ExprKind::Path(p) = &expr.kind
3429 && let Some(mutability) = cx
3430 .qpath_res(p, expr.hir_id)
3431 .opt_def_id()
3432 .and_then(|id| cx.tcx.static_mutability(id))
3433 {
3434 mutability == Mutability::Mut
3435 } else if let ExprKind::Field(parent, _) = expr.kind {
3436 is_mutable(cx, parent)
3437 } else {
3438 true
3439 }
3440}
3441
3442pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3445 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3446 return hir_ty;
3447 };
3448 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3449 && let Some(segment) = path.segments.last()
3450 && segment.ident.name == sym::Option
3451 && let Res::Def(DefKind::Enum, def_id) = segment.res
3452 && def_id == option_def_id
3453 && let [GenericArg::Type(arg_ty)] = segment.args().args
3454 {
3455 hir_ty = arg_ty.as_unambig_ty();
3456 }
3457 hir_ty
3458}
3459
3460pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3463 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3464 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3465 && let ctxt = expr.span.ctxt()
3466 && for_each_expr_without_closures(into_future_arg, |e| {
3467 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3468 })
3469 .is_none()
3470 {
3471 Some(into_future_arg)
3472 } else {
3473 None
3474 }
3475}