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 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1799}
1800
1801pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1806 let mut conds = Vec::new();
1807 let mut blocks: Vec<&Block<'_>> = Vec::new();
1808
1809 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1810 conds.push(cond);
1811 if let ExprKind::Block(block, _) = then.kind {
1812 blocks.push(block);
1813 } else {
1814 panic!("ExprKind::If node is not an ExprKind::Block");
1815 }
1816
1817 if let Some(else_expr) = r#else {
1818 expr = else_expr;
1819 } else {
1820 break;
1821 }
1822 }
1823
1824 if !blocks.is_empty()
1826 && let ExprKind::Block(block, _) = expr.kind
1827 {
1828 blocks.push(block);
1829 }
1830
1831 (conds, blocks)
1832}
1833
1834pub fn is_async_fn(kind: FnKind<'_>) -> bool {
1836 match kind {
1837 FnKind::ItemFn(_, _, header) => header.asyncness.is_async(),
1838 FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
1839 FnKind::Closure => false,
1840 }
1841}
1842
1843pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1845 if let ExprKind::Closure(&Closure {
1846 body,
1847 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1848 ..
1849 }) = expr.kind
1850 && let ExprKind::Block(
1851 Block {
1852 expr:
1853 Some(Expr {
1854 kind: ExprKind::DropTemps(inner_expr),
1855 ..
1856 }),
1857 ..
1858 },
1859 _,
1860 ) = tcx.hir_body(body).value.kind
1861 {
1862 Some(inner_expr)
1863 } else {
1864 None
1865 }
1866}
1867
1868pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1870 get_async_closure_expr(tcx, body.value)
1871}
1872
1873pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1875 let did = match expr.kind {
1876 ExprKind::Call(path, _) => {
1877 if let ExprKind::Path(ref qpath) = path.kind
1878 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1879 {
1880 Some(did)
1881 } else {
1882 None
1883 }
1884 },
1885 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1886 _ => None,
1887 };
1888
1889 did.is_some_and(|did| find_attr!(
1890 cx.tcx.get_all_attrs(did),
1891 AttributeKind::MustUse { ..}
1892 ))
1893}
1894
1895fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1904 fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
1905 if cx
1906 .typeck_results()
1907 .pat_binding_modes()
1908 .get(pat.hir_id)
1909 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
1910 {
1911 return false;
1915 }
1916
1917 match (pat.kind, expr.kind) {
1918 (PatKind::Binding(_, id, _, _), _) => {
1919 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1920 },
1921 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1922 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1923 {
1924 pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr))
1925 },
1926 _ => false,
1927 }
1928 }
1929
1930 let [param] = func.params else {
1931 return false;
1932 };
1933
1934 let mut expr = func.value;
1935 loop {
1936 match expr.kind {
1937 ExprKind::Block(
1938 &Block {
1939 stmts: [],
1940 expr: Some(e),
1941 ..
1942 },
1943 _,
1944 )
1945 | ExprKind::Ret(Some(e)) => expr = e,
1946 ExprKind::Block(
1947 &Block {
1948 stmts: [stmt],
1949 expr: None,
1950 ..
1951 },
1952 _,
1953 ) => {
1954 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1955 && let ExprKind::Ret(Some(ret_val)) = e.kind
1956 {
1957 expr = ret_val;
1958 } else {
1959 return false;
1960 }
1961 },
1962 _ => return check_pat(cx, param.pat, expr),
1963 }
1964 }
1965}
1966
1967pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1972 match expr.kind {
1973 ExprKind::Closure(&Closure { body, fn_decl, .. })
1974 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1975 {
1976 is_body_identity_function(cx, cx.tcx.hir_body(body))
1977 },
1978 ExprKind::Path(QPath::Resolved(_, path))
1979 if path.segments.iter().all(|seg| seg.infer_args)
1980 && let Some(did) = path.res.opt_def_id() =>
1981 {
1982 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1983 },
1984 _ => false,
1985 }
1986}
1987
1988pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1997 match expr.kind {
1998 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1999 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
2000 }
2001}
2002
2003pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2006 let mut child_id = expr.hir_id;
2007 let mut iter = tcx.hir_parent_iter(child_id);
2008 loop {
2009 match iter.next() {
2010 None => break None,
2011 Some((id, Node::Block(_))) => child_id = id,
2012 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2013 Some((_, Node::Expr(expr))) => match expr.kind {
2014 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2015 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2016 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2017 _ => break Some((Node::Expr(expr), child_id)),
2018 },
2019 Some((_, node)) => break Some((node, child_id)),
2020 }
2021 }
2022}
2023
2024pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2026 !matches!(
2027 get_expr_use_or_unification_node(tcx, expr),
2028 None | Some((
2029 Node::Stmt(Stmt {
2030 kind: StmtKind::Expr(_)
2031 | StmtKind::Semi(_)
2032 | StmtKind::Let(LetStmt {
2033 pat: Pat {
2034 kind: PatKind::Wild,
2035 ..
2036 },
2037 ..
2038 }),
2039 ..
2040 }),
2041 _
2042 ))
2043 )
2044}
2045
2046pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2048 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2049}
2050
2051pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2055 !expr.is_place_expr(|base| {
2056 cx.typeck_results()
2057 .adjustments()
2058 .get(base.hir_id)
2059 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2060 })
2061}
2062
2063pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2064 if !is_no_std_crate(cx) {
2065 Some("std")
2066 } else if !is_no_core_crate(cx) {
2067 Some("core")
2068 } else {
2069 None
2070 }
2071}
2072
2073pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2074 cx.tcx
2075 .hir_attrs(hir::CRATE_HIR_ID)
2076 .iter()
2077 .any(|attr| attr.has_name(sym::no_std))
2078}
2079
2080pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2081 cx.tcx
2082 .hir_attrs(hir::CRATE_HIR_ID)
2083 .iter()
2084 .any(|attr| attr.has_name(sym::no_core))
2085}
2086
2087pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2097 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2098 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2099 } else {
2100 false
2101 }
2102}
2103
2104pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2114 use rustc_trait_selection::traits;
2115 let predicates = cx
2116 .tcx
2117 .predicates_of(did)
2118 .predicates
2119 .iter()
2120 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2121 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2122}
2123
2124pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2126 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2127}
2128
2129pub fn fn_def_id_with_node_args<'tcx>(
2132 cx: &LateContext<'tcx>,
2133 expr: &Expr<'_>,
2134) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2135 let typeck = cx.typeck_results();
2136 match &expr.kind {
2137 ExprKind::MethodCall(..) => Some((
2138 typeck.type_dependent_def_id(expr.hir_id)?,
2139 typeck.node_args(expr.hir_id),
2140 )),
2141 ExprKind::Call(
2142 Expr {
2143 kind: ExprKind::Path(qpath),
2144 hir_id: path_hir_id,
2145 ..
2146 },
2147 ..,
2148 ) => {
2149 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2152 typeck.qpath_res(qpath, *path_hir_id)
2153 {
2154 Some((id, typeck.node_args(*path_hir_id)))
2155 } else {
2156 None
2157 }
2158 },
2159 _ => None,
2160 }
2161}
2162
2163pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2168 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2169 let expr_kind = expr_type.kind();
2170 let is_primitive = match expr_kind {
2171 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2172 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2173 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2174 is_recursively_primitive_type(*element_type)
2175 } else {
2176 unreachable!()
2177 }
2178 },
2179 _ => false,
2180 };
2181
2182 if is_primitive {
2183 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2186 rustc_ty::Slice(..) => return Some("slice".into()),
2187 rustc_ty::Array(..) => return Some("array".into()),
2188 rustc_ty::Tuple(..) => return Some("tuple".into()),
2189 _ => {
2190 let refs_peeled = expr_type.peel_refs();
2193 return Some(refs_peeled.walk().last().unwrap().to_string());
2194 },
2195 }
2196 }
2197 None
2198}
2199
2200pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2208where
2209 Hash: FnMut(&T) -> u64,
2210 Eq: FnMut(&T, &T) -> bool,
2211{
2212 match exprs {
2213 [a, b] if eq(a, b) => return vec![vec![a, b]],
2214 _ if exprs.len() <= 2 => return vec![],
2215 _ => {},
2216 }
2217
2218 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2219
2220 for expr in exprs {
2221 match buckets.entry(hash(expr)) {
2222 indexmap::map::Entry::Occupied(mut o) => {
2223 let bucket = o.get_mut();
2224 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2225 Some(group) => group.push(expr),
2226 None => bucket.push(vec![expr]),
2227 }
2228 },
2229 indexmap::map::Entry::Vacant(v) => {
2230 v.insert(vec![vec![expr]]);
2231 },
2232 }
2233 }
2234
2235 buckets
2236 .into_values()
2237 .flatten()
2238 .filter(|group| group.len() > 1)
2239 .collect()
2240}
2241
2242pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2245 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2246 if let PatKind::Ref(pat, _) = pat.kind {
2247 peel(pat, count + 1)
2248 } else {
2249 (pat, count)
2250 }
2251 }
2252 peel(pat, 0)
2253}
2254
2255pub fn peel_hir_expr_while<'tcx>(
2257 mut expr: &'tcx Expr<'tcx>,
2258 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2259) -> &'tcx Expr<'tcx> {
2260 while let Some(e) = f(expr) {
2261 expr = e;
2262 }
2263 expr
2264}
2265
2266pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2269 let mut remaining = count;
2270 let e = peel_hir_expr_while(expr, |e| match e.kind {
2271 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2272 remaining -= 1;
2273 Some(e)
2274 },
2275 _ => None,
2276 });
2277 (e, count - remaining)
2278}
2279
2280pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2283 let mut count: usize = 0;
2284 let mut curr_expr = expr;
2285 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2286 count = count.wrapping_add(1);
2287 curr_expr = local_expr;
2288 }
2289 (curr_expr, count)
2290}
2291
2292pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2295 let mut count = 0;
2296 let e = peel_hir_expr_while(expr, |e| match e.kind {
2297 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2298 count += 1;
2299 Some(e)
2300 },
2301 _ => None,
2302 });
2303 (e, count)
2304}
2305
2306pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2309 let mut count = 0;
2310 loop {
2311 match &ty.kind {
2312 TyKind::Ref(_, ref_ty) => {
2313 ty = ref_ty.ty;
2314 count += 1;
2315 },
2316 _ => break (ty, count),
2317 }
2318 }
2319}
2320
2321pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2324 let mut count = 0;
2325 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2326 ty = *dest_ty;
2327 count += 1;
2328 }
2329 (ty, count)
2330}
2331
2332pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2335 loop {
2336 match expr.kind {
2337 ExprKind::AddrOf(_, _, e) => expr = e,
2338 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2339 _ => break,
2340 }
2341 }
2342 expr
2343}
2344
2345pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2346 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2347 && let Res::Def(_, def_id) = path.res
2348 {
2349 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2350 }
2351 false
2352}
2353
2354static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2355
2356fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2359 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2360 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2361 let value = map.entry(module);
2362 match value {
2363 Entry::Occupied(entry) => f(entry.get()),
2364 Entry::Vacant(entry) => {
2365 let mut names = Vec::new();
2366 for id in tcx.hir_module_free_items(module) {
2367 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2368 && let item = tcx.hir_item(id)
2369 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2370 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2371 && let Res::Def(DefKind::Struct, _) = path.res
2373 {
2374 let has_test_marker = tcx
2375 .hir_attrs(item.hir_id())
2376 .iter()
2377 .any(|a| a.has_name(sym::rustc_test_marker));
2378 if has_test_marker {
2379 names.push(ident.name);
2380 }
2381 }
2382 }
2383 names.sort_unstable();
2384 f(entry.insert(names))
2385 },
2386 }
2387}
2388
2389pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2393 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2394 let node = tcx.hir_node(id);
2395 once((id, node))
2396 .chain(tcx.hir_parent_iter(id))
2397 .any(|(_id, node)| {
2400 if let Node::Item(item) = node
2401 && let ItemKind::Fn { ident, .. } = item.kind
2402 {
2403 return names.binary_search(&ident.name).is_ok();
2406 }
2407 false
2408 })
2409 })
2410}
2411
2412pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2419 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2420 if let Node::Item(item) = tcx.hir_node(id)
2421 && let ItemKind::Fn { ident, .. } = item.kind
2422 {
2423 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2424 names.binary_search(&ident.name).is_ok()
2425 })
2426 } else {
2427 false
2428 }
2429}
2430
2431pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2436 tcx.hir_attrs(id).iter().any(|attr| {
2437 if attr.has_name(sym::cfg_trace)
2438 && let Some(items) = attr.meta_item_list()
2439 && let [item] = &*items
2440 && item.has_name(sym::test)
2441 {
2442 true
2443 } else {
2444 false
2445 }
2446 })
2447}
2448
2449pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2451 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2452}
2453
2454pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2456 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2457}
2458
2459pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2461 tcx.has_attr(def_id, sym::cfg_trace)
2462 || tcx
2463 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2464 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2465 .any(|attr| attr.has_name(sym::cfg_trace))
2466}
2467
2468pub fn walk_to_expr_usage<'tcx, T>(
2479 cx: &LateContext<'tcx>,
2480 e: &Expr<'tcx>,
2481 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2482) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2483 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2484 let mut child_id = e.hir_id;
2485
2486 while let Some((parent_id, parent)) = iter.next() {
2487 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2488 return Some(ControlFlow::Break(x));
2489 }
2490 let parent_expr = match parent {
2491 Node::Expr(e) => e,
2492 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2493 child_id = parent_id;
2494 continue;
2495 },
2496 Node::Arm(a) if a.body.hir_id == child_id => {
2497 child_id = parent_id;
2498 continue;
2499 },
2500 _ => return Some(ControlFlow::Continue((parent, child_id))),
2501 };
2502 match parent_expr.kind {
2503 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2504 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2505 child_id = id;
2506 iter = cx.tcx.hir_parent_iter(id);
2507 },
2508 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2509 _ => return Some(ControlFlow::Continue((parent, child_id))),
2510 }
2511 }
2512 debug_assert!(false, "no parent node found for `{child_id:?}`");
2513 None
2514}
2515
2516#[derive(Clone, Copy)]
2518pub enum DefinedTy<'tcx> {
2519 Hir(&'tcx hir::Ty<'tcx>),
2521 Mir {
2529 def_site_def_id: Option<DefId>,
2530 ty: Binder<'tcx, Ty<'tcx>>,
2531 },
2532}
2533
2534pub struct ExprUseCtxt<'tcx> {
2536 pub node: Node<'tcx>,
2538 pub child_id: HirId,
2540 pub adjustments: &'tcx [Adjustment<'tcx>],
2542 pub is_ty_unified: bool,
2544 pub moved_before_use: bool,
2546 pub same_ctxt: bool,
2548}
2549impl<'tcx> ExprUseCtxt<'tcx> {
2550 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2551 match self.node {
2552 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2553 Node::ExprField(field) => ExprUseNode::Field(field),
2554
2555 Node::Item(&Item {
2556 kind: ItemKind::Static(..) | ItemKind::Const(..),
2557 owner_id,
2558 ..
2559 })
2560 | Node::TraitItem(&TraitItem {
2561 kind: TraitItemKind::Const(..),
2562 owner_id,
2563 ..
2564 })
2565 | Node::ImplItem(&ImplItem {
2566 kind: ImplItemKind::Const(..),
2567 owner_id,
2568 ..
2569 }) => ExprUseNode::ConstStatic(owner_id),
2570
2571 Node::Item(&Item {
2572 kind: ItemKind::Fn { .. },
2573 owner_id,
2574 ..
2575 })
2576 | Node::TraitItem(&TraitItem {
2577 kind: TraitItemKind::Fn(..),
2578 owner_id,
2579 ..
2580 })
2581 | Node::ImplItem(&ImplItem {
2582 kind: ImplItemKind::Fn(..),
2583 owner_id,
2584 ..
2585 }) => ExprUseNode::Return(owner_id),
2586
2587 Node::Expr(use_expr) => match use_expr.kind {
2588 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2589 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2590 }),
2591
2592 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2593 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2594 Some(i) => ExprUseNode::FnArg(func, i),
2595 None => ExprUseNode::Callee,
2596 },
2597 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2598 use_expr.hir_id,
2599 name.args,
2600 args.iter()
2601 .position(|arg| arg.hir_id == self.child_id)
2602 .map_or(0, |i| i + 1),
2603 ),
2604 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2605 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2606 _ => ExprUseNode::Other,
2607 },
2608 _ => ExprUseNode::Other,
2609 }
2610 }
2611}
2612
2613pub enum ExprUseNode<'tcx> {
2615 LetStmt(&'tcx LetStmt<'tcx>),
2617 ConstStatic(OwnerId),
2619 Return(OwnerId),
2621 Field(&'tcx ExprField<'tcx>),
2623 FnArg(&'tcx Expr<'tcx>, usize),
2625 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2627 Callee,
2629 FieldAccess(Ident),
2631 AddrOf(ast::BorrowKind, Mutability),
2633 Other,
2634}
2635impl<'tcx> ExprUseNode<'tcx> {
2636 pub fn is_return(&self) -> bool {
2638 matches!(self, Self::Return(_))
2639 }
2640
2641 pub fn is_recv(&self) -> bool {
2643 matches!(self, Self::MethodArg(_, _, 0))
2644 }
2645
2646 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2648 match *self {
2649 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2650 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2651 def_site_def_id: Some(id.def_id.to_def_id()),
2652 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2653 }),
2654 Self::Return(id) => {
2655 if let Node::Expr(Expr {
2656 kind: ExprKind::Closure(c),
2657 ..
2658 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2659 {
2660 match c.fn_decl.output {
2661 FnRetTy::DefaultReturn(_) => None,
2662 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2663 }
2664 } else {
2665 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2666 Some(DefinedTy::Mir {
2667 def_site_def_id: Some(id.def_id.to_def_id()),
2668 ty,
2669 })
2670 }
2671 },
2672 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2673 Some(Expr {
2674 hir_id,
2675 kind: ExprKind::Struct(path, ..),
2676 ..
2677 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2678 .and_then(|(adt, variant)| {
2679 variant
2680 .fields
2681 .iter()
2682 .find(|f| f.name == field.ident.name)
2683 .map(|f| (adt, f))
2684 })
2685 .map(|(adt, field_def)| DefinedTy::Mir {
2686 def_site_def_id: Some(adt.did()),
2687 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2688 }),
2689 _ => None,
2690 },
2691 Self::FnArg(callee, i) => {
2692 let sig = expr_sig(cx, callee)?;
2693 let (hir_ty, ty) = sig.input_with_hir(i)?;
2694 Some(match hir_ty {
2695 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2696 None => DefinedTy::Mir {
2697 def_site_def_id: sig.predicates_id(),
2698 ty,
2699 },
2700 })
2701 },
2702 Self::MethodArg(id, _, i) => {
2703 let id = cx.typeck_results().type_dependent_def_id(id)?;
2704 let sig = cx.tcx.fn_sig(id).skip_binder();
2705 Some(DefinedTy::Mir {
2706 def_site_def_id: Some(id),
2707 ty: sig.input(i),
2708 })
2709 },
2710 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2711 }
2712 }
2713}
2714
2715pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2717 let mut adjustments = [].as_slice();
2718 let mut is_ty_unified = false;
2719 let mut moved_before_use = false;
2720 let mut same_ctxt = true;
2721 let ctxt = e.span.ctxt();
2722 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2723 if adjustments.is_empty()
2724 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2725 {
2726 adjustments = cx.typeck_results().expr_adjustments(e);
2727 }
2728 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2729 if let Node::Expr(e) = parent {
2730 match e.kind {
2731 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2732 is_ty_unified = true;
2733 moved_before_use = true;
2734 },
2735 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2736 is_ty_unified = true;
2737 moved_before_use = true;
2738 },
2739 ExprKind::Block(..) => moved_before_use = true,
2740 _ => {},
2741 }
2742 }
2743 ControlFlow::Continue(())
2744 });
2745 match node {
2746 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2747 node,
2748 child_id,
2749 adjustments,
2750 is_ty_unified,
2751 moved_before_use,
2752 same_ctxt,
2753 },
2754 #[allow(unreachable_patterns)]
2755 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
2756 None => ExprUseCtxt {
2757 node: Node::Crate(cx.tcx.hir_root_module()),
2758 child_id: HirId::INVALID,
2759 adjustments: &[],
2760 is_ty_unified: true,
2761 moved_before_use: true,
2762 same_ctxt: false,
2763 },
2764 }
2765}
2766
2767pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2769 let mut pos = 0;
2770 tokenize(s).map(move |t| {
2771 let end = pos + t.len;
2772 let range = pos as usize..end as usize;
2773 let inner = InnerSpan::new(range.start, range.end);
2774 pos = end;
2775 (t.kind, s.get(range).unwrap_or_default(), inner)
2776 })
2777}
2778
2779pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2782 let Ok(snippet) = sm.span_to_snippet(span) else {
2783 return false;
2784 };
2785 return tokenize(&snippet).any(|token| {
2786 matches!(
2787 token.kind,
2788 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2789 )
2790 });
2791}
2792
2793pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2797 span_extract_comments(sm, span).join("\n")
2798}
2799
2800pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2804 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2805 tokenize_with_text(&snippet)
2806 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2807 .map(|(_, s, _)| s.to_string())
2808 .collect::<Vec<_>>()
2809}
2810
2811pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2812 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2813}
2814
2815pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2840 cx: &LateContext<'_>,
2841 pat: &'a Pat<'hir>,
2842 else_body: &Expr<'_>,
2843) -> Option<&'a Pat<'hir>> {
2844 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
2845 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
2846 && !is_refutable(cx, inner_pat)
2847 && let else_body = peel_blocks(else_body)
2848 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2849 && let ExprKind::Path(ret_path) = ret_val.kind
2850 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
2851 {
2852 Some(inner_pat)
2853 } else {
2854 None
2855 }
2856}
2857
2858macro_rules! op_utils {
2859 ($($name:ident $assign:ident)*) => {
2860 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2862
2863 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2865
2866 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2868 match kind {
2869 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2870 _ => None,
2871 }
2872 }
2873 };
2874}
2875
2876op_utils! {
2877 Add AddAssign
2878 Sub SubAssign
2879 Mul MulAssign
2880 Div DivAssign
2881 Rem RemAssign
2882 BitXor BitXorAssign
2883 BitAnd BitAndAssign
2884 BitOr BitOrAssign
2885 Shl ShlAssign
2886 Shr ShrAssign
2887}
2888
2889pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2892 match *pat {
2893 PatKind::Wild => true,
2894 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2895 !visitors::is_local_used(cx, body, id)
2896 },
2897 _ => false,
2898 }
2899}
2900
2901#[derive(Clone, Copy)]
2902pub enum RequiresSemi {
2903 Yes,
2904 No,
2905}
2906impl RequiresSemi {
2907 pub fn requires_semi(self) -> bool {
2908 matches!(self, Self::Yes)
2909 }
2910}
2911
2912#[expect(clippy::too_many_lines)]
2915pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2916 struct BreakTarget {
2917 id: HirId,
2918 unused: bool,
2919 }
2920
2921 struct V<'cx, 'tcx> {
2922 cx: &'cx LateContext<'tcx>,
2923 break_targets: Vec<BreakTarget>,
2924 break_targets_for_result_ty: u32,
2925 in_final_expr: bool,
2926 requires_semi: bool,
2927 is_never: bool,
2928 }
2929
2930 impl V<'_, '_> {
2931 fn push_break_target(&mut self, id: HirId) {
2932 self.break_targets.push(BreakTarget { id, unused: true });
2933 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2934 }
2935 }
2936
2937 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
2938 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
2939 if self.is_never && self.break_targets.is_empty() {
2956 if self.in_final_expr && !self.requires_semi {
2957 match e.kind {
2960 ExprKind::DropTemps(e) => self.visit_expr(e),
2961 ExprKind::If(_, then, Some(else_)) => {
2962 self.visit_expr(then);
2963 self.visit_expr(else_);
2964 },
2965 ExprKind::Match(_, arms, _) => {
2966 for arm in arms {
2967 self.visit_expr(arm.body);
2968 }
2969 },
2970 ExprKind::Loop(b, ..) => {
2971 self.push_break_target(e.hir_id);
2972 self.in_final_expr = false;
2973 self.visit_block(b);
2974 self.break_targets.pop();
2975 },
2976 ExprKind::Block(b, _) => {
2977 if b.targeted_by_break {
2978 self.push_break_target(b.hir_id);
2979 self.visit_block(b);
2980 self.break_targets.pop();
2981 } else {
2982 self.visit_block(b);
2983 }
2984 },
2985 _ => {
2986 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
2987 },
2988 }
2989 }
2990 return;
2991 }
2992 match e.kind {
2993 ExprKind::DropTemps(e) => self.visit_expr(e),
2994 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
2995 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
2996 self.in_final_expr = false;
2997 self.visit_expr(e);
2998 self.is_never = true;
2999 },
3000 ExprKind::Break(dest, e) => {
3001 if let Some(e) = e {
3002 self.in_final_expr = false;
3003 self.visit_expr(e);
3004 }
3005 if let Ok(id) = dest.target_id
3006 && let Some((i, target)) = self
3007 .break_targets
3008 .iter_mut()
3009 .enumerate()
3010 .find(|(_, target)| target.id == id)
3011 {
3012 target.unused &= self.is_never;
3013 if i < self.break_targets_for_result_ty as usize {
3014 self.requires_semi = true;
3015 }
3016 }
3017 self.is_never = true;
3018 },
3019 ExprKind::If(cond, then, else_) => {
3020 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3021 self.visit_expr(cond);
3022 self.in_final_expr = in_final_expr;
3023
3024 if self.is_never {
3025 self.visit_expr(then);
3026 if let Some(else_) = else_ {
3027 self.visit_expr(else_);
3028 }
3029 } else {
3030 self.visit_expr(then);
3031 let is_never = mem::replace(&mut self.is_never, false);
3032 if let Some(else_) = else_ {
3033 self.visit_expr(else_);
3034 self.is_never &= is_never;
3035 }
3036 }
3037 },
3038 ExprKind::Match(scrutinee, arms, _) => {
3039 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3040 self.visit_expr(scrutinee);
3041 self.in_final_expr = in_final_expr;
3042
3043 if self.is_never {
3044 for arm in arms {
3045 self.visit_arm(arm);
3046 }
3047 } else {
3048 let mut is_never = true;
3049 for arm in arms {
3050 self.is_never = false;
3051 if let Some(guard) = arm.guard {
3052 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3053 self.visit_expr(guard);
3054 self.in_final_expr = in_final_expr;
3055 self.is_never = false;
3057 }
3058 self.visit_expr(arm.body);
3059 is_never &= self.is_never;
3060 }
3061 self.is_never = is_never;
3062 }
3063 },
3064 ExprKind::Loop(b, _, _, _) => {
3065 self.push_break_target(e.hir_id);
3066 self.in_final_expr = false;
3067 self.visit_block(b);
3068 self.is_never = self.break_targets.pop().unwrap().unused;
3069 },
3070 ExprKind::Block(b, _) => {
3071 if b.targeted_by_break {
3072 self.push_break_target(b.hir_id);
3073 self.visit_block(b);
3074 self.is_never &= self.break_targets.pop().unwrap().unused;
3075 } else {
3076 self.visit_block(b);
3077 }
3078 },
3079 _ => {
3080 self.in_final_expr = false;
3081 walk_expr(self, e);
3082 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3083 },
3084 }
3085 }
3086
3087 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3088 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3089 for s in b.stmts {
3090 self.visit_stmt(s);
3091 }
3092 self.in_final_expr = in_final_expr;
3093 if let Some(e) = b.expr {
3094 self.visit_expr(e);
3095 }
3096 }
3097
3098 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3099 if let Some(e) = l.init {
3100 self.visit_expr(e);
3101 }
3102 if let Some(else_) = l.els {
3103 let is_never = self.is_never;
3104 self.visit_block(else_);
3105 self.is_never = is_never;
3106 }
3107 }
3108
3109 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3110 if let Some(guard) = arm.guard {
3111 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3112 self.visit_expr(guard);
3113 self.in_final_expr = in_final_expr;
3114 }
3115 self.visit_expr(arm.body);
3116 }
3117 }
3118
3119 if cx.typeck_results().expr_ty(e).is_never() {
3120 Some(RequiresSemi::No)
3121 } else if let ExprKind::Block(b, _) = e.kind
3122 && !b.targeted_by_break
3123 && b.expr.is_none()
3124 {
3125 None
3127 } else {
3128 let mut v = V {
3129 cx,
3130 break_targets: Vec::new(),
3131 break_targets_for_result_ty: 0,
3132 in_final_expr: true,
3133 requires_semi: false,
3134 is_never: false,
3135 };
3136 v.visit_expr(e);
3137 v.is_never
3138 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3139 RequiresSemi::Yes
3140 } else {
3141 RequiresSemi::No
3142 })
3143 }
3144}
3145
3146pub fn get_path_from_caller_to_method_type<'tcx>(
3152 tcx: TyCtxt<'tcx>,
3153 from: LocalDefId,
3154 method: DefId,
3155 args: GenericArgsRef<'tcx>,
3156) -> String {
3157 let assoc_item = tcx.associated_item(method);
3158 let def_id = assoc_item.container_id(tcx);
3159 match assoc_item.container {
3160 rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
3161 rustc_ty::AssocItemContainer::Impl => {
3162 let ty = tcx.type_of(def_id).instantiate_identity();
3163 get_path_to_ty(tcx, from, ty, args)
3164 },
3165 }
3166}
3167
3168fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3169 match ty.kind() {
3170 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3171 rustc_ty::Array(..)
3173 | rustc_ty::Dynamic(..)
3174 | rustc_ty::Never
3175 | rustc_ty::RawPtr(_, _)
3176 | rustc_ty::Ref(..)
3177 | rustc_ty::Slice(_)
3178 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3179 _ => ty.to_string(),
3180 }
3181}
3182
3183fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3185 if callee.is_local() {
3187 let callee_path = tcx.def_path(callee);
3188 let caller_path = tcx.def_path(from.to_def_id());
3189 maybe_get_relative_path(&caller_path, &callee_path, 2)
3190 } else {
3191 tcx.def_path_str(callee)
3192 }
3193}
3194
3195fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3208 use itertools::EitherOrBoth::{Both, Left, Right};
3209
3210 let unique_parts = to
3212 .data
3213 .iter()
3214 .zip_longest(from.data.iter())
3215 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3216 .map(|el| match el {
3217 Both(l, r) => Both(l.data, r.data),
3218 Left(l) => Left(l.data),
3219 Right(r) => Right(r.data),
3220 });
3221
3222 let mut go_up_by = 0;
3224 let mut path = Vec::new();
3225 for el in unique_parts {
3226 match el {
3227 Both(l, r) => {
3228 if let DefPathData::TypeNs(s) = l {
3238 path.push(s.to_string());
3239 }
3240 if let DefPathData::TypeNs(_) = r {
3241 go_up_by += 1;
3242 }
3243 },
3244 Left(DefPathData::TypeNs(sym)) => path.push(sym.to_string()),
3249 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3254 _ => {},
3255 }
3256 }
3257
3258 if go_up_by > max_super {
3259 once(String::from("crate"))
3261 .chain(to.data.iter().filter_map(|el| {
3262 if let DefPathData::TypeNs(sym) = el.data {
3263 Some(sym.to_string())
3264 } else {
3265 None
3266 }
3267 }))
3268 .join("::")
3269 } else {
3270 repeat_n(String::from("super"), go_up_by).chain(path).join("::")
3271 }
3272}
3273
3274pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3277 matches!(
3278 cx.tcx.parent_hir_node(id),
3279 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3280 )
3281}
3282
3283pub fn is_block_like(expr: &Expr<'_>) -> bool {
3286 matches!(
3287 expr.kind,
3288 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3289 )
3290}
3291
3292pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3294 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3295 match expr.kind {
3296 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3297 _ if is_block_like(expr) => is_operand,
3298 _ => false,
3299 }
3300 }
3301
3302 contains_block(expr, false)
3303}
3304
3305pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3307 if let Some(parent_expr) = get_parent_expr(cx, expr)
3308 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3309 && receiver.hir_id == expr.hir_id
3310 {
3311 return true;
3312 }
3313 false
3314}
3315
3316pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3319 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3320 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3321 && temporary_ty
3322 .walk()
3323 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3324 {
3325 ControlFlow::Break(())
3326 } else {
3327 ControlFlow::Continue(())
3328 }
3329 })
3330 .is_break()
3331}
3332
3333pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3344 let expr_ty_is_adjusted = cx
3345 .typeck_results()
3346 .expr_adjustments(expr)
3347 .iter()
3348 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3350 if expr_ty_is_adjusted {
3351 return true;
3352 }
3353
3354 match expr.kind {
3357 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3358 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3359
3360 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3361 return false;
3362 }
3363
3364 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3365 let mut args_with_ty_param = {
3366 fn_sig
3367 .inputs()
3368 .skip_binder()
3369 .iter()
3370 .skip(self_arg_count)
3371 .zip(args)
3372 .filter_map(|(arg_ty, arg)| {
3373 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3374 Some(arg)
3375 } else {
3376 None
3377 }
3378 })
3379 };
3380 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3381 },
3382 ExprKind::Struct(qpath, _, _) => {
3384 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3385 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3386 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3387 return true;
3389 };
3390 v_def
3391 .fields
3392 .iter()
3393 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3394 } else {
3395 false
3396 }
3397 },
3398 ExprKind::Block(
3400 &Block {
3401 expr: Some(ret_expr), ..
3402 },
3403 _,
3404 )
3405 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3406
3407 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3409 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3411 ExprKind::If(_, then, maybe_else) => {
3413 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3414 },
3415 ExprKind::Match(_, arms, _) => arms
3416 .iter()
3417 .map(|arm| arm.body)
3418 .any(|body| expr_requires_coercion(cx, body)),
3419 _ => false,
3420 }
3421}
3422
3423pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3426 if let Some(hir_id) = path_to_local(expr)
3427 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3428 {
3429 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3430 } else if let ExprKind::Path(p) = &expr.kind
3431 && let Some(mutability) = cx
3432 .qpath_res(p, expr.hir_id)
3433 .opt_def_id()
3434 .and_then(|id| cx.tcx.static_mutability(id))
3435 {
3436 mutability == Mutability::Mut
3437 } else if let ExprKind::Field(parent, _) = expr.kind {
3438 is_mutable(cx, parent)
3439 } else {
3440 true
3441 }
3442}
3443
3444pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3447 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3448 return hir_ty;
3449 };
3450 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3451 && let Some(segment) = path.segments.last()
3452 && segment.ident.name == sym::Option
3453 && let Res::Def(DefKind::Enum, def_id) = segment.res
3454 && def_id == option_def_id
3455 && let [GenericArg::Type(arg_ty)] = segment.args().args
3456 {
3457 hir_ty = arg_ty.as_unambig_ty();
3458 }
3459 hir_ty
3460}
3461
3462pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3465 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3466 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3467 && let ctxt = expr.span.ctxt()
3468 && for_each_expr_without_closures(into_future_arg, |e| {
3469 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3470 })
3471 .is_none()
3472 {
3473 Some(into_future_arg)
3474 } else {
3475 None
3476 }
3477}
3478
3479pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3481 if let ExprKind::Call(fn_expr, []) = &expr.kind
3482 && let ExprKind::Path(qpath) = &fn_expr.kind
3483 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3484 {
3485 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3486 } else {
3487 false
3488 }
3489}