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_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1569 matches!(
1570 cx.qpath_res(qpath, id),
1571 Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
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_enum_variant(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_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1596 },
1597 PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
1598 PatKind::Slice(head, middle, tail) => {
1599 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1600 rustc_ty::Slice(..) => {
1601 !head.is_empty() || middle.is_none() || !tail.is_empty()
1603 },
1604 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1605 _ => {
1606 true
1608 },
1609 }
1610 },
1611 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1612 }
1613}
1614
1615pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1618 if let PatKind::Or(pats) = pat.kind {
1619 pats.iter().for_each(f);
1620 } else {
1621 f(pat);
1622 }
1623}
1624
1625pub fn is_self(slf: &Param<'_>) -> bool {
1626 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1627 name.name == kw::SelfLower
1628 } else {
1629 false
1630 }
1631}
1632
1633pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1634 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1635 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1636 {
1637 return true;
1638 }
1639 false
1640}
1641
1642pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1643 (0..decl.inputs.len()).map(move |i| &body.params[i])
1644}
1645
1646pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1649 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1650 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1651 && ddpos.as_opt_usize().is_none()
1652 && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk)
1653 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1654 && path_to_local_id(arm.body, hir_id)
1655 {
1656 return true;
1657 }
1658 false
1659 }
1660
1661 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1662 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1663 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1664 } else {
1665 false
1666 }
1667 }
1668
1669 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1670 if let MatchSource::TryDesugar(_) = *source {
1672 return Some(expr);
1673 }
1674
1675 if arms.len() == 2
1676 && arms[0].guard.is_none()
1677 && arms[1].guard.is_none()
1678 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1679 {
1680 return Some(expr);
1681 }
1682 }
1683
1684 None
1685}
1686
1687pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1697 let mut suppress_lint = false;
1698
1699 for id in ids {
1700 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1701 if let Some(expectation) = lint_id {
1702 cx.fulfill_expectation(expectation);
1703 }
1704
1705 match level {
1706 Level::Allow | Level::Expect => suppress_lint = true,
1707 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1708 }
1709 }
1710
1711 suppress_lint
1712}
1713
1714pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1722 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1723}
1724
1725pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1726 while let PatKind::Ref(subpat, _) = pat.kind {
1727 pat = subpat;
1728 }
1729 pat
1730}
1731
1732pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1733 Integer::from_int_ty(&tcx, ity).size().bits()
1734}
1735
1736#[expect(clippy::cast_possible_wrap)]
1737pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1739 let amt = 128 - int_bits(tcx, ity);
1740 ((u as i128) << amt) >> amt
1741}
1742
1743#[expect(clippy::cast_sign_loss)]
1744pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1746 let amt = 128 - int_bits(tcx, ity);
1747 ((u as u128) << amt) >> amt
1748}
1749
1750pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1752 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1753 let amt = 128 - bits;
1754 (u << amt) >> amt
1755}
1756
1757pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1758 attrs.iter().any(|attr| attr.has_name(symbol))
1759}
1760
1761pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1762 find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr(..))
1763}
1764
1765pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1766 let mut prev_enclosing_node = None;
1767 let mut enclosing_node = node;
1768 while Some(enclosing_node) != prev_enclosing_node {
1769 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1770 return true;
1771 }
1772 prev_enclosing_node = Some(enclosing_node);
1773 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1774 }
1775
1776 false
1777}
1778
1779pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1782 tcx.hir_parent_owner_iter(id)
1783 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1784 .any(|(id, _)| {
1785 has_attr(
1786 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1787 sym::automatically_derived,
1788 )
1789 })
1790}
1791
1792pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1794 let path = cx.get_def_path(did);
1795 path.first().is_some_and(|s| *s == sym::libc) && path.last().copied() == Some(name)
1798}
1799
1800pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1805 let mut conds = Vec::new();
1806 let mut blocks: Vec<&Block<'_>> = Vec::new();
1807
1808 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1809 conds.push(cond);
1810 if let ExprKind::Block(block, _) = then.kind {
1811 blocks.push(block);
1812 } else {
1813 panic!("ExprKind::If node is not an ExprKind::Block");
1814 }
1815
1816 if let Some(else_expr) = r#else {
1817 expr = else_expr;
1818 } else {
1819 break;
1820 }
1821 }
1822
1823 if !blocks.is_empty()
1825 && let ExprKind::Block(block, _) = expr.kind
1826 {
1827 blocks.push(block);
1828 }
1829
1830 (conds, blocks)
1831}
1832
1833pub fn is_async_fn(kind: FnKind<'_>) -> bool {
1835 match kind {
1836 FnKind::ItemFn(_, _, header) => header.asyncness.is_async(),
1837 FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
1838 FnKind::Closure => false,
1839 }
1840}
1841
1842pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1844 if let ExprKind::Closure(&Closure {
1845 body,
1846 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1847 ..
1848 }) = expr.kind
1849 && let ExprKind::Block(
1850 Block {
1851 expr:
1852 Some(Expr {
1853 kind: ExprKind::DropTemps(inner_expr),
1854 ..
1855 }),
1856 ..
1857 },
1858 _,
1859 ) = tcx.hir_body(body).value.kind
1860 {
1861 Some(inner_expr)
1862 } else {
1863 None
1864 }
1865}
1866
1867pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1869 get_async_closure_expr(tcx, body.value)
1870}
1871
1872pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1874 let did = match expr.kind {
1875 ExprKind::Call(path, _) => {
1876 if let ExprKind::Path(ref qpath) = path.kind
1877 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1878 {
1879 Some(did)
1880 } else {
1881 None
1882 }
1883 },
1884 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1885 _ => None,
1886 };
1887
1888 did.is_some_and(|did| cx.tcx.has_attr(did, sym::must_use))
1889}
1890
1891fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1900 fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
1901 if cx
1902 .typeck_results()
1903 .pat_binding_modes()
1904 .get(pat.hir_id)
1905 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
1906 {
1907 return false;
1911 }
1912
1913 match (pat.kind, expr.kind) {
1914 (PatKind::Binding(_, id, _, _), _) => {
1915 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1916 },
1917 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1918 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1919 {
1920 pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr))
1921 },
1922 _ => false,
1923 }
1924 }
1925
1926 let [param] = func.params else {
1927 return false;
1928 };
1929
1930 let mut expr = func.value;
1931 loop {
1932 match expr.kind {
1933 ExprKind::Block(
1934 &Block {
1935 stmts: [],
1936 expr: Some(e),
1937 ..
1938 },
1939 _,
1940 )
1941 | ExprKind::Ret(Some(e)) => expr = e,
1942 ExprKind::Block(
1943 &Block {
1944 stmts: [stmt],
1945 expr: None,
1946 ..
1947 },
1948 _,
1949 ) => {
1950 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1951 && let ExprKind::Ret(Some(ret_val)) = e.kind
1952 {
1953 expr = ret_val;
1954 } else {
1955 return false;
1956 }
1957 },
1958 _ => return check_pat(cx, param.pat, expr),
1959 }
1960 }
1961}
1962
1963pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1968 match expr.kind {
1969 ExprKind::Closure(&Closure { body, fn_decl, .. })
1970 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1971 {
1972 is_body_identity_function(cx, cx.tcx.hir_body(body))
1973 },
1974 ExprKind::Path(QPath::Resolved(_, path))
1975 if path.segments.iter().all(|seg| seg.infer_args)
1976 && let Some(did) = path.res.opt_def_id() =>
1977 {
1978 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1979 },
1980 _ => false,
1981 }
1982}
1983
1984pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1993 match expr.kind {
1994 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1995 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
1996 }
1997}
1998
1999pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2002 let mut child_id = expr.hir_id;
2003 let mut iter = tcx.hir_parent_iter(child_id);
2004 loop {
2005 match iter.next() {
2006 None => break None,
2007 Some((id, Node::Block(_))) => child_id = id,
2008 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2009 Some((_, Node::Expr(expr))) => match expr.kind {
2010 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2011 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2012 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2013 _ => break Some((Node::Expr(expr), child_id)),
2014 },
2015 Some((_, node)) => break Some((node, child_id)),
2016 }
2017 }
2018}
2019
2020pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2022 !matches!(
2023 get_expr_use_or_unification_node(tcx, expr),
2024 None | Some((
2025 Node::Stmt(Stmt {
2026 kind: StmtKind::Expr(_)
2027 | StmtKind::Semi(_)
2028 | StmtKind::Let(LetStmt {
2029 pat: Pat {
2030 kind: PatKind::Wild,
2031 ..
2032 },
2033 ..
2034 }),
2035 ..
2036 }),
2037 _
2038 ))
2039 )
2040}
2041
2042pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2044 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2045}
2046
2047pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2051 !expr.is_place_expr(|base| {
2052 cx.typeck_results()
2053 .adjustments()
2054 .get(base.hir_id)
2055 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2056 })
2057}
2058
2059pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2060 if !is_no_std_crate(cx) {
2061 Some("std")
2062 } else if !is_no_core_crate(cx) {
2063 Some("core")
2064 } else {
2065 None
2066 }
2067}
2068
2069pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2070 cx.tcx
2071 .hir_attrs(hir::CRATE_HIR_ID)
2072 .iter()
2073 .any(|attr| attr.has_name(sym::no_std))
2074}
2075
2076pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2077 cx.tcx
2078 .hir_attrs(hir::CRATE_HIR_ID)
2079 .iter()
2080 .any(|attr| attr.has_name(sym::no_core))
2081}
2082
2083pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2093 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2094 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2095 } else {
2096 false
2097 }
2098}
2099
2100pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2110 use rustc_trait_selection::traits;
2111 let predicates = cx
2112 .tcx
2113 .predicates_of(did)
2114 .predicates
2115 .iter()
2116 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2117 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2118}
2119
2120pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2122 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2123}
2124
2125pub fn fn_def_id_with_node_args<'tcx>(
2128 cx: &LateContext<'tcx>,
2129 expr: &Expr<'_>,
2130) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2131 let typeck = cx.typeck_results();
2132 match &expr.kind {
2133 ExprKind::MethodCall(..) => Some((
2134 typeck.type_dependent_def_id(expr.hir_id)?,
2135 typeck.node_args(expr.hir_id),
2136 )),
2137 ExprKind::Call(
2138 Expr {
2139 kind: ExprKind::Path(qpath),
2140 hir_id: path_hir_id,
2141 ..
2142 },
2143 ..,
2144 ) => {
2145 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2148 typeck.qpath_res(qpath, *path_hir_id)
2149 {
2150 Some((id, typeck.node_args(*path_hir_id)))
2151 } else {
2152 None
2153 }
2154 },
2155 _ => None,
2156 }
2157}
2158
2159pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2164 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2165 let expr_kind = expr_type.kind();
2166 let is_primitive = match expr_kind {
2167 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2168 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2169 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2170 is_recursively_primitive_type(*element_type)
2171 } else {
2172 unreachable!()
2173 }
2174 },
2175 _ => false,
2176 };
2177
2178 if is_primitive {
2179 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2182 rustc_ty::Slice(..) => return Some("slice".into()),
2183 rustc_ty::Array(..) => return Some("array".into()),
2184 rustc_ty::Tuple(..) => return Some("tuple".into()),
2185 _ => {
2186 let refs_peeled = expr_type.peel_refs();
2189 return Some(refs_peeled.walk().last().unwrap().to_string());
2190 },
2191 }
2192 }
2193 None
2194}
2195
2196pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2204where
2205 Hash: FnMut(&T) -> u64,
2206 Eq: FnMut(&T, &T) -> bool,
2207{
2208 match exprs {
2209 [a, b] if eq(a, b) => return vec![vec![a, b]],
2210 _ if exprs.len() <= 2 => return vec![],
2211 _ => {},
2212 }
2213
2214 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2215
2216 for expr in exprs {
2217 match buckets.entry(hash(expr)) {
2218 indexmap::map::Entry::Occupied(mut o) => {
2219 let bucket = o.get_mut();
2220 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2221 Some(group) => group.push(expr),
2222 None => bucket.push(vec![expr]),
2223 }
2224 },
2225 indexmap::map::Entry::Vacant(v) => {
2226 v.insert(vec![vec![expr]]);
2227 },
2228 }
2229 }
2230
2231 buckets
2232 .into_values()
2233 .flatten()
2234 .filter(|group| group.len() > 1)
2235 .collect()
2236}
2237
2238pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2241 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2242 if let PatKind::Ref(pat, _) = pat.kind {
2243 peel(pat, count + 1)
2244 } else {
2245 (pat, count)
2246 }
2247 }
2248 peel(pat, 0)
2249}
2250
2251pub fn peel_hir_expr_while<'tcx>(
2253 mut expr: &'tcx Expr<'tcx>,
2254 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2255) -> &'tcx Expr<'tcx> {
2256 while let Some(e) = f(expr) {
2257 expr = e;
2258 }
2259 expr
2260}
2261
2262pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2265 let mut remaining = count;
2266 let e = peel_hir_expr_while(expr, |e| match e.kind {
2267 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2268 remaining -= 1;
2269 Some(e)
2270 },
2271 _ => None,
2272 });
2273 (e, count - remaining)
2274}
2275
2276pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2279 let mut count: usize = 0;
2280 let mut curr_expr = expr;
2281 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2282 count = count.wrapping_add(1);
2283 curr_expr = local_expr;
2284 }
2285 (curr_expr, count)
2286}
2287
2288pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2291 let mut count = 0;
2292 let e = peel_hir_expr_while(expr, |e| match e.kind {
2293 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2294 count += 1;
2295 Some(e)
2296 },
2297 _ => None,
2298 });
2299 (e, count)
2300}
2301
2302pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2305 let mut count = 0;
2306 loop {
2307 match &ty.kind {
2308 TyKind::Ref(_, ref_ty) => {
2309 ty = ref_ty.ty;
2310 count += 1;
2311 },
2312 _ => break (ty, count),
2313 }
2314 }
2315}
2316
2317pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2320 let mut count = 0;
2321 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2322 ty = *dest_ty;
2323 count += 1;
2324 }
2325 (ty, count)
2326}
2327
2328pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2331 loop {
2332 match expr.kind {
2333 ExprKind::AddrOf(_, _, e) => expr = e,
2334 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2335 _ => break,
2336 }
2337 }
2338 expr
2339}
2340
2341pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2342 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2343 && let Res::Def(_, def_id) = path.res
2344 {
2345 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2346 }
2347 false
2348}
2349
2350static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2351
2352fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2355 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2356 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2357 let value = map.entry(module);
2358 match value {
2359 Entry::Occupied(entry) => f(entry.get()),
2360 Entry::Vacant(entry) => {
2361 let mut names = Vec::new();
2362 for id in tcx.hir_module_free_items(module) {
2363 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2364 && let item = tcx.hir_item(id)
2365 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2366 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2367 && let Res::Def(DefKind::Struct, _) = path.res
2369 {
2370 let has_test_marker = tcx
2371 .hir_attrs(item.hir_id())
2372 .iter()
2373 .any(|a| a.has_name(sym::rustc_test_marker));
2374 if has_test_marker {
2375 names.push(ident.name);
2376 }
2377 }
2378 }
2379 names.sort_unstable();
2380 f(entry.insert(names))
2381 },
2382 }
2383}
2384
2385pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2389 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2390 let node = tcx.hir_node(id);
2391 once((id, node))
2392 .chain(tcx.hir_parent_iter(id))
2393 .any(|(_id, node)| {
2396 if let Node::Item(item) = node
2397 && let ItemKind::Fn { ident, .. } = item.kind
2398 {
2399 return names.binary_search(&ident.name).is_ok();
2402 }
2403 false
2404 })
2405 })
2406}
2407
2408pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2415 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2416 if let Node::Item(item) = tcx.hir_node(id)
2417 && let ItemKind::Fn { ident, .. } = item.kind
2418 {
2419 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2420 names.binary_search(&ident.name).is_ok()
2421 })
2422 } else {
2423 false
2424 }
2425}
2426
2427pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2432 tcx.hir_attrs(id).iter().any(|attr| {
2433 if attr.has_name(sym::cfg_trace)
2434 && let Some(items) = attr.meta_item_list()
2435 && let [item] = &*items
2436 && item.has_name(sym::test)
2437 {
2438 true
2439 } else {
2440 false
2441 }
2442 })
2443}
2444
2445pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2447 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2448}
2449
2450pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2452 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2453}
2454
2455pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2457 tcx.has_attr(def_id, sym::cfg_trace)
2458 || tcx
2459 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2460 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2461 .any(|attr| attr.has_name(sym::cfg_trace))
2462}
2463
2464pub fn walk_to_expr_usage<'tcx, T>(
2475 cx: &LateContext<'tcx>,
2476 e: &Expr<'tcx>,
2477 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2478) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2479 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2480 let mut child_id = e.hir_id;
2481
2482 while let Some((parent_id, parent)) = iter.next() {
2483 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2484 return Some(ControlFlow::Break(x));
2485 }
2486 let parent_expr = match parent {
2487 Node::Expr(e) => e,
2488 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2489 child_id = parent_id;
2490 continue;
2491 },
2492 Node::Arm(a) if a.body.hir_id == child_id => {
2493 child_id = parent_id;
2494 continue;
2495 },
2496 _ => return Some(ControlFlow::Continue((parent, child_id))),
2497 };
2498 match parent_expr.kind {
2499 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2500 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2501 child_id = id;
2502 iter = cx.tcx.hir_parent_iter(id);
2503 },
2504 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2505 _ => return Some(ControlFlow::Continue((parent, child_id))),
2506 }
2507 }
2508 debug_assert!(false, "no parent node found for `{child_id:?}`");
2509 None
2510}
2511
2512#[derive(Clone, Copy)]
2514pub enum DefinedTy<'tcx> {
2515 Hir(&'tcx hir::Ty<'tcx>),
2517 Mir {
2525 def_site_def_id: Option<DefId>,
2526 ty: Binder<'tcx, Ty<'tcx>>,
2527 },
2528}
2529
2530pub struct ExprUseCtxt<'tcx> {
2532 pub node: Node<'tcx>,
2534 pub child_id: HirId,
2536 pub adjustments: &'tcx [Adjustment<'tcx>],
2538 pub is_ty_unified: bool,
2540 pub moved_before_use: bool,
2542 pub same_ctxt: bool,
2544}
2545impl<'tcx> ExprUseCtxt<'tcx> {
2546 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2547 match self.node {
2548 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2549 Node::ExprField(field) => ExprUseNode::Field(field),
2550
2551 Node::Item(&Item {
2552 kind: ItemKind::Static(..) | ItemKind::Const(..),
2553 owner_id,
2554 ..
2555 })
2556 | Node::TraitItem(&TraitItem {
2557 kind: TraitItemKind::Const(..),
2558 owner_id,
2559 ..
2560 })
2561 | Node::ImplItem(&ImplItem {
2562 kind: ImplItemKind::Const(..),
2563 owner_id,
2564 ..
2565 }) => ExprUseNode::ConstStatic(owner_id),
2566
2567 Node::Item(&Item {
2568 kind: ItemKind::Fn { .. },
2569 owner_id,
2570 ..
2571 })
2572 | Node::TraitItem(&TraitItem {
2573 kind: TraitItemKind::Fn(..),
2574 owner_id,
2575 ..
2576 })
2577 | Node::ImplItem(&ImplItem {
2578 kind: ImplItemKind::Fn(..),
2579 owner_id,
2580 ..
2581 }) => ExprUseNode::Return(owner_id),
2582
2583 Node::Expr(use_expr) => match use_expr.kind {
2584 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2585 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2586 }),
2587
2588 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2589 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2590 Some(i) => ExprUseNode::FnArg(func, i),
2591 None => ExprUseNode::Callee,
2592 },
2593 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2594 use_expr.hir_id,
2595 name.args,
2596 args.iter()
2597 .position(|arg| arg.hir_id == self.child_id)
2598 .map_or(0, |i| i + 1),
2599 ),
2600 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2601 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2602 _ => ExprUseNode::Other,
2603 },
2604 _ => ExprUseNode::Other,
2605 }
2606 }
2607}
2608
2609pub enum ExprUseNode<'tcx> {
2611 LetStmt(&'tcx LetStmt<'tcx>),
2613 ConstStatic(OwnerId),
2615 Return(OwnerId),
2617 Field(&'tcx ExprField<'tcx>),
2619 FnArg(&'tcx Expr<'tcx>, usize),
2621 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2623 Callee,
2625 FieldAccess(Ident),
2627 AddrOf(ast::BorrowKind, Mutability),
2629 Other,
2630}
2631impl<'tcx> ExprUseNode<'tcx> {
2632 pub fn is_return(&self) -> bool {
2634 matches!(self, Self::Return(_))
2635 }
2636
2637 pub fn is_recv(&self) -> bool {
2639 matches!(self, Self::MethodArg(_, _, 0))
2640 }
2641
2642 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2644 match *self {
2645 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2646 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2647 def_site_def_id: Some(id.def_id.to_def_id()),
2648 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2649 }),
2650 Self::Return(id) => {
2651 if let Node::Expr(Expr {
2652 kind: ExprKind::Closure(c),
2653 ..
2654 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2655 {
2656 match c.fn_decl.output {
2657 FnRetTy::DefaultReturn(_) => None,
2658 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2659 }
2660 } else {
2661 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2662 Some(DefinedTy::Mir {
2663 def_site_def_id: Some(id.def_id.to_def_id()),
2664 ty,
2665 })
2666 }
2667 },
2668 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2669 Some(Expr {
2670 hir_id,
2671 kind: ExprKind::Struct(path, ..),
2672 ..
2673 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2674 .and_then(|(adt, variant)| {
2675 variant
2676 .fields
2677 .iter()
2678 .find(|f| f.name == field.ident.name)
2679 .map(|f| (adt, f))
2680 })
2681 .map(|(adt, field_def)| DefinedTy::Mir {
2682 def_site_def_id: Some(adt.did()),
2683 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2684 }),
2685 _ => None,
2686 },
2687 Self::FnArg(callee, i) => {
2688 let sig = expr_sig(cx, callee)?;
2689 let (hir_ty, ty) = sig.input_with_hir(i)?;
2690 Some(match hir_ty {
2691 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2692 None => DefinedTy::Mir {
2693 def_site_def_id: sig.predicates_id(),
2694 ty,
2695 },
2696 })
2697 },
2698 Self::MethodArg(id, _, i) => {
2699 let id = cx.typeck_results().type_dependent_def_id(id)?;
2700 let sig = cx.tcx.fn_sig(id).skip_binder();
2701 Some(DefinedTy::Mir {
2702 def_site_def_id: Some(id),
2703 ty: sig.input(i),
2704 })
2705 },
2706 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2707 }
2708 }
2709}
2710
2711pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2713 let mut adjustments = [].as_slice();
2714 let mut is_ty_unified = false;
2715 let mut moved_before_use = false;
2716 let mut same_ctxt = true;
2717 let ctxt = e.span.ctxt();
2718 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2719 if adjustments.is_empty()
2720 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2721 {
2722 adjustments = cx.typeck_results().expr_adjustments(e);
2723 }
2724 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2725 if let Node::Expr(e) = parent {
2726 match e.kind {
2727 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2728 is_ty_unified = true;
2729 moved_before_use = true;
2730 },
2731 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2732 is_ty_unified = true;
2733 moved_before_use = true;
2734 },
2735 ExprKind::Block(..) => moved_before_use = true,
2736 _ => {},
2737 }
2738 }
2739 ControlFlow::Continue(())
2740 });
2741 match node {
2742 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2743 node,
2744 child_id,
2745 adjustments,
2746 is_ty_unified,
2747 moved_before_use,
2748 same_ctxt,
2749 },
2750 #[allow(unreachable_patterns)]
2751 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
2752 None => ExprUseCtxt {
2753 node: Node::Crate(cx.tcx.hir_root_module()),
2754 child_id: HirId::INVALID,
2755 adjustments: &[],
2756 is_ty_unified: true,
2757 moved_before_use: true,
2758 same_ctxt: false,
2759 },
2760 }
2761}
2762
2763pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2765 let mut pos = 0;
2766 tokenize(s).map(move |t| {
2767 let end = pos + t.len;
2768 let range = pos as usize..end as usize;
2769 let inner = InnerSpan::new(range.start, range.end);
2770 pos = end;
2771 (t.kind, s.get(range).unwrap_or_default(), inner)
2772 })
2773}
2774
2775pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2778 let Ok(snippet) = sm.span_to_snippet(span) else {
2779 return false;
2780 };
2781 return tokenize(&snippet).any(|token| {
2782 matches!(
2783 token.kind,
2784 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2785 )
2786 });
2787}
2788
2789pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2793 span_extract_comments(sm, span).join("\n")
2794}
2795
2796pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2800 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2801 tokenize_with_text(&snippet)
2802 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2803 .map(|(_, s, _)| s.to_string())
2804 .collect::<Vec<_>>()
2805}
2806
2807pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2808 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2809}
2810
2811pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2836 cx: &LateContext<'_>,
2837 pat: &'a Pat<'hir>,
2838 else_body: &Expr<'_>,
2839) -> Option<&'a Pat<'hir>> {
2840 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
2841 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
2842 && !is_refutable(cx, inner_pat)
2843 && let else_body = peel_blocks(else_body)
2844 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2845 && let ExprKind::Path(ret_path) = ret_val.kind
2846 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
2847 {
2848 Some(inner_pat)
2849 } else {
2850 None
2851 }
2852}
2853
2854macro_rules! op_utils {
2855 ($($name:ident $assign:ident)*) => {
2856 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2858
2859 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2861
2862 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2864 match kind {
2865 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2866 _ => None,
2867 }
2868 }
2869 };
2870}
2871
2872op_utils! {
2873 Add AddAssign
2874 Sub SubAssign
2875 Mul MulAssign
2876 Div DivAssign
2877 Rem RemAssign
2878 BitXor BitXorAssign
2879 BitAnd BitAndAssign
2880 BitOr BitOrAssign
2881 Shl ShlAssign
2882 Shr ShrAssign
2883}
2884
2885pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2888 match *pat {
2889 PatKind::Wild => true,
2890 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2891 !visitors::is_local_used(cx, body, id)
2892 },
2893 _ => false,
2894 }
2895}
2896
2897#[derive(Clone, Copy)]
2898pub enum RequiresSemi {
2899 Yes,
2900 No,
2901}
2902impl RequiresSemi {
2903 pub fn requires_semi(self) -> bool {
2904 matches!(self, Self::Yes)
2905 }
2906}
2907
2908#[expect(clippy::too_many_lines)]
2911pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2912 struct BreakTarget {
2913 id: HirId,
2914 unused: bool,
2915 }
2916
2917 struct V<'cx, 'tcx> {
2918 cx: &'cx LateContext<'tcx>,
2919 break_targets: Vec<BreakTarget>,
2920 break_targets_for_result_ty: u32,
2921 in_final_expr: bool,
2922 requires_semi: bool,
2923 is_never: bool,
2924 }
2925
2926 impl V<'_, '_> {
2927 fn push_break_target(&mut self, id: HirId) {
2928 self.break_targets.push(BreakTarget { id, unused: true });
2929 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2930 }
2931 }
2932
2933 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
2934 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
2935 if self.is_never && self.break_targets.is_empty() {
2952 if self.in_final_expr && !self.requires_semi {
2953 match e.kind {
2956 ExprKind::DropTemps(e) => self.visit_expr(e),
2957 ExprKind::If(_, then, Some(else_)) => {
2958 self.visit_expr(then);
2959 self.visit_expr(else_);
2960 },
2961 ExprKind::Match(_, arms, _) => {
2962 for arm in arms {
2963 self.visit_expr(arm.body);
2964 }
2965 },
2966 ExprKind::Loop(b, ..) => {
2967 self.push_break_target(e.hir_id);
2968 self.in_final_expr = false;
2969 self.visit_block(b);
2970 self.break_targets.pop();
2971 },
2972 ExprKind::Block(b, _) => {
2973 if b.targeted_by_break {
2974 self.push_break_target(b.hir_id);
2975 self.visit_block(b);
2976 self.break_targets.pop();
2977 } else {
2978 self.visit_block(b);
2979 }
2980 },
2981 _ => {
2982 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
2983 },
2984 }
2985 }
2986 return;
2987 }
2988 match e.kind {
2989 ExprKind::DropTemps(e) => self.visit_expr(e),
2990 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
2991 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
2992 self.in_final_expr = false;
2993 self.visit_expr(e);
2994 self.is_never = true;
2995 },
2996 ExprKind::Break(dest, e) => {
2997 if let Some(e) = e {
2998 self.in_final_expr = false;
2999 self.visit_expr(e);
3000 }
3001 if let Ok(id) = dest.target_id
3002 && let Some((i, target)) = self
3003 .break_targets
3004 .iter_mut()
3005 .enumerate()
3006 .find(|(_, target)| target.id == id)
3007 {
3008 target.unused &= self.is_never;
3009 if i < self.break_targets_for_result_ty as usize {
3010 self.requires_semi = true;
3011 }
3012 }
3013 self.is_never = true;
3014 },
3015 ExprKind::If(cond, then, else_) => {
3016 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3017 self.visit_expr(cond);
3018 self.in_final_expr = in_final_expr;
3019
3020 if self.is_never {
3021 self.visit_expr(then);
3022 if let Some(else_) = else_ {
3023 self.visit_expr(else_);
3024 }
3025 } else {
3026 self.visit_expr(then);
3027 let is_never = mem::replace(&mut self.is_never, false);
3028 if let Some(else_) = else_ {
3029 self.visit_expr(else_);
3030 self.is_never &= is_never;
3031 }
3032 }
3033 },
3034 ExprKind::Match(scrutinee, arms, _) => {
3035 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3036 self.visit_expr(scrutinee);
3037 self.in_final_expr = in_final_expr;
3038
3039 if self.is_never {
3040 for arm in arms {
3041 self.visit_arm(arm);
3042 }
3043 } else {
3044 let mut is_never = true;
3045 for arm in arms {
3046 self.is_never = false;
3047 if let Some(guard) = arm.guard {
3048 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3049 self.visit_expr(guard);
3050 self.in_final_expr = in_final_expr;
3051 self.is_never = false;
3053 }
3054 self.visit_expr(arm.body);
3055 is_never &= self.is_never;
3056 }
3057 self.is_never = is_never;
3058 }
3059 },
3060 ExprKind::Loop(b, _, _, _) => {
3061 self.push_break_target(e.hir_id);
3062 self.in_final_expr = false;
3063 self.visit_block(b);
3064 self.is_never = self.break_targets.pop().unwrap().unused;
3065 },
3066 ExprKind::Block(b, _) => {
3067 if b.targeted_by_break {
3068 self.push_break_target(b.hir_id);
3069 self.visit_block(b);
3070 self.is_never &= self.break_targets.pop().unwrap().unused;
3071 } else {
3072 self.visit_block(b);
3073 }
3074 },
3075 _ => {
3076 self.in_final_expr = false;
3077 walk_expr(self, e);
3078 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3079 },
3080 }
3081 }
3082
3083 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3084 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3085 for s in b.stmts {
3086 self.visit_stmt(s);
3087 }
3088 self.in_final_expr = in_final_expr;
3089 if let Some(e) = b.expr {
3090 self.visit_expr(e);
3091 }
3092 }
3093
3094 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3095 if let Some(e) = l.init {
3096 self.visit_expr(e);
3097 }
3098 if let Some(else_) = l.els {
3099 let is_never = self.is_never;
3100 self.visit_block(else_);
3101 self.is_never = is_never;
3102 }
3103 }
3104
3105 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3106 if let Some(guard) = arm.guard {
3107 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3108 self.visit_expr(guard);
3109 self.in_final_expr = in_final_expr;
3110 }
3111 self.visit_expr(arm.body);
3112 }
3113 }
3114
3115 if cx.typeck_results().expr_ty(e).is_never() {
3116 Some(RequiresSemi::No)
3117 } else if let ExprKind::Block(b, _) = e.kind
3118 && !b.targeted_by_break
3119 && b.expr.is_none()
3120 {
3121 None
3123 } else {
3124 let mut v = V {
3125 cx,
3126 break_targets: Vec::new(),
3127 break_targets_for_result_ty: 0,
3128 in_final_expr: true,
3129 requires_semi: false,
3130 is_never: false,
3131 };
3132 v.visit_expr(e);
3133 v.is_never
3134 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3135 RequiresSemi::Yes
3136 } else {
3137 RequiresSemi::No
3138 })
3139 }
3140}
3141
3142pub fn get_path_from_caller_to_method_type<'tcx>(
3148 tcx: TyCtxt<'tcx>,
3149 from: LocalDefId,
3150 method: DefId,
3151 args: GenericArgsRef<'tcx>,
3152) -> String {
3153 let assoc_item = tcx.associated_item(method);
3154 let def_id = assoc_item.container_id(tcx);
3155 match assoc_item.container {
3156 rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
3157 rustc_ty::AssocItemContainer::Impl => {
3158 let ty = tcx.type_of(def_id).instantiate_identity();
3159 get_path_to_ty(tcx, from, ty, args)
3160 },
3161 }
3162}
3163
3164fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3165 match ty.kind() {
3166 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3167 rustc_ty::Array(..)
3169 | rustc_ty::Dynamic(..)
3170 | rustc_ty::Never
3171 | rustc_ty::RawPtr(_, _)
3172 | rustc_ty::Ref(..)
3173 | rustc_ty::Slice(_)
3174 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3175 _ => ty.to_string(),
3176 }
3177}
3178
3179fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3181 if callee.is_local() {
3183 let callee_path = tcx.def_path(callee);
3184 let caller_path = tcx.def_path(from.to_def_id());
3185 maybe_get_relative_path(&caller_path, &callee_path, 2)
3186 } else {
3187 tcx.def_path_str(callee)
3188 }
3189}
3190
3191fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3204 use itertools::EitherOrBoth::{Both, Left, Right};
3205
3206 let unique_parts = to
3208 .data
3209 .iter()
3210 .zip_longest(from.data.iter())
3211 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3212 .map(|el| match el {
3213 Both(l, r) => Both(l.data, r.data),
3214 Left(l) => Left(l.data),
3215 Right(r) => Right(r.data),
3216 });
3217
3218 let mut go_up_by = 0;
3220 let mut path = Vec::new();
3221 for el in unique_parts {
3222 match el {
3223 Both(l, r) => {
3224 if let DefPathData::TypeNs(s) = l {
3234 path.push(s.to_string());
3235 }
3236 if let DefPathData::TypeNs(_) = r {
3237 go_up_by += 1;
3238 }
3239 },
3240 Left(DefPathData::TypeNs(sym)) => path.push(sym.to_string()),
3245 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3250 _ => {},
3251 }
3252 }
3253
3254 if go_up_by > max_super {
3255 once(String::from("crate"))
3257 .chain(to.data.iter().filter_map(|el| {
3258 if let DefPathData::TypeNs(sym) = el.data {
3259 Some(sym.to_string())
3260 } else {
3261 None
3262 }
3263 }))
3264 .join("::")
3265 } else {
3266 repeat_n(String::from("super"), go_up_by).chain(path).join("::")
3267 }
3268}
3269
3270pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3273 matches!(
3274 cx.tcx.parent_hir_node(id),
3275 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3276 )
3277}
3278
3279pub fn is_block_like(expr: &Expr<'_>) -> bool {
3282 matches!(
3283 expr.kind,
3284 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3285 )
3286}
3287
3288pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3290 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3291 match expr.kind {
3292 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3293 _ if is_block_like(expr) => is_operand,
3294 _ => false,
3295 }
3296 }
3297
3298 contains_block(expr, false)
3299}
3300
3301pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3303 if let Some(parent_expr) = get_parent_expr(cx, expr)
3304 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3305 && receiver.hir_id == expr.hir_id
3306 {
3307 return true;
3308 }
3309 false
3310}
3311
3312pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3315 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3316 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3317 && temporary_ty
3318 .walk()
3319 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3320 {
3321 ControlFlow::Break(())
3322 } else {
3323 ControlFlow::Continue(())
3324 }
3325 })
3326 .is_break()
3327}
3328
3329pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3340 let expr_ty_is_adjusted = cx
3341 .typeck_results()
3342 .expr_adjustments(expr)
3343 .iter()
3344 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3346 if expr_ty_is_adjusted {
3347 return true;
3348 }
3349
3350 match expr.kind {
3353 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3354 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3355
3356 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3357 return false;
3358 }
3359
3360 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3361 let mut args_with_ty_param = {
3362 fn_sig
3363 .inputs()
3364 .skip_binder()
3365 .iter()
3366 .skip(self_arg_count)
3367 .zip(args)
3368 .filter_map(|(arg_ty, arg)| {
3369 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3370 Some(arg)
3371 } else {
3372 None
3373 }
3374 })
3375 };
3376 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3377 },
3378 ExprKind::Struct(qpath, _, _) => {
3380 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3381 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3382 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3383 return true;
3385 };
3386 v_def
3387 .fields
3388 .iter()
3389 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3390 } else {
3391 false
3392 }
3393 },
3394 ExprKind::Block(
3396 &Block {
3397 expr: Some(ret_expr), ..
3398 },
3399 _,
3400 )
3401 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3402
3403 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3405 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3407 ExprKind::If(_, then, maybe_else) => {
3409 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3410 },
3411 ExprKind::Match(_, arms, _) => arms
3412 .iter()
3413 .map(|arm| arm.body)
3414 .any(|body| expr_requires_coercion(cx, body)),
3415 _ => false,
3416 }
3417}
3418
3419pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3422 if let Some(hir_id) = path_to_local(expr)
3423 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3424 {
3425 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3426 } else if let ExprKind::Path(p) = &expr.kind
3427 && let Some(mutability) = cx
3428 .qpath_res(p, expr.hir_id)
3429 .opt_def_id()
3430 .and_then(|id| cx.tcx.static_mutability(id))
3431 {
3432 mutability == Mutability::Mut
3433 } else if let ExprKind::Field(parent, _) = expr.kind {
3434 is_mutable(cx, parent)
3435 } else {
3436 true
3437 }
3438}
3439
3440pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3443 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3444 return hir_ty;
3445 };
3446 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3447 && let Some(segment) = path.segments.last()
3448 && segment.ident.name == sym::Option
3449 && let Res::Def(DefKind::Enum, def_id) = segment.res
3450 && def_id == option_def_id
3451 && let [GenericArg::Type(arg_ty)] = segment.args().args
3452 {
3453 hir_ty = arg_ty.as_unambig_ty();
3454 }
3455 hir_ty
3456}
3457
3458pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3461 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3462 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3463 && let ctxt = expr.span.ctxt()
3464 && for_each_expr_without_closures(into_future_arg, |e| {
3465 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3466 })
3467 .is_none()
3468 {
3469 Some(into_future_arg)
3470 } else {
3471 None
3472 }
3473}