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::{SpanRangeExt, 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!(cx.tcx.get_all_attrs(did), AttributeKind::MustUse { .. }))
1890}
1891
1892fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1901 fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
1902 if cx
1903 .typeck_results()
1904 .pat_binding_modes()
1905 .get(pat.hir_id)
1906 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
1907 {
1908 return false;
1912 }
1913
1914 match (pat.kind, expr.kind) {
1915 (PatKind::Binding(_, id, _, _), _) => {
1916 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1917 },
1918 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1919 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1920 {
1921 pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr))
1922 },
1923 _ => false,
1924 }
1925 }
1926
1927 let [param] = func.params else {
1928 return false;
1929 };
1930
1931 let mut expr = func.value;
1932 loop {
1933 match expr.kind {
1934 ExprKind::Block(
1935 &Block {
1936 stmts: [],
1937 expr: Some(e),
1938 ..
1939 },
1940 _,
1941 )
1942 | ExprKind::Ret(Some(e)) => expr = e,
1943 ExprKind::Block(
1944 &Block {
1945 stmts: [stmt],
1946 expr: None,
1947 ..
1948 },
1949 _,
1950 ) => {
1951 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1952 && let ExprKind::Ret(Some(ret_val)) = e.kind
1953 {
1954 expr = ret_val;
1955 } else {
1956 return false;
1957 }
1958 },
1959 _ => return check_pat(cx, param.pat, expr),
1960 }
1961 }
1962}
1963
1964pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1969 match expr.kind {
1970 ExprKind::Closure(&Closure { body, fn_decl, .. })
1971 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1972 {
1973 is_body_identity_function(cx, cx.tcx.hir_body(body))
1974 },
1975 ExprKind::Path(QPath::Resolved(_, path))
1976 if path.segments.iter().all(|seg| seg.infer_args)
1977 && let Some(did) = path.res.opt_def_id() =>
1978 {
1979 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1980 },
1981 _ => false,
1982 }
1983}
1984
1985pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1994 match expr.kind {
1995 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1996 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
1997 }
1998}
1999
2000pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2003 let mut child_id = expr.hir_id;
2004 let mut iter = tcx.hir_parent_iter(child_id);
2005 loop {
2006 match iter.next() {
2007 None => break None,
2008 Some((id, Node::Block(_))) => child_id = id,
2009 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2010 Some((_, Node::Expr(expr))) => match expr.kind {
2011 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2012 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2013 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2014 _ => break Some((Node::Expr(expr), child_id)),
2015 },
2016 Some((_, node)) => break Some((node, child_id)),
2017 }
2018 }
2019}
2020
2021pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2023 !matches!(
2024 get_expr_use_or_unification_node(tcx, expr),
2025 None | Some((
2026 Node::Stmt(Stmt {
2027 kind: StmtKind::Expr(_)
2028 | StmtKind::Semi(_)
2029 | StmtKind::Let(LetStmt {
2030 pat: Pat {
2031 kind: PatKind::Wild,
2032 ..
2033 },
2034 ..
2035 }),
2036 ..
2037 }),
2038 _
2039 ))
2040 )
2041}
2042
2043pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2045 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2046}
2047
2048pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2052 !expr.is_place_expr(|base| {
2053 cx.typeck_results()
2054 .adjustments()
2055 .get(base.hir_id)
2056 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2057 })
2058}
2059
2060pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2061 if !is_no_std_crate(cx) {
2062 Some("std")
2063 } else if !is_no_core_crate(cx) {
2064 Some("core")
2065 } else {
2066 None
2067 }
2068}
2069
2070pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2071 cx.tcx
2072 .hir_attrs(hir::CRATE_HIR_ID)
2073 .iter()
2074 .any(|attr| attr.has_name(sym::no_std))
2075}
2076
2077pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2078 cx.tcx
2079 .hir_attrs(hir::CRATE_HIR_ID)
2080 .iter()
2081 .any(|attr| attr.has_name(sym::no_core))
2082}
2083
2084pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2094 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2095 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2096 } else {
2097 false
2098 }
2099}
2100
2101pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2111 use rustc_trait_selection::traits;
2112 let predicates = cx
2113 .tcx
2114 .predicates_of(did)
2115 .predicates
2116 .iter()
2117 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2118 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2119}
2120
2121pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2123 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2124}
2125
2126pub fn fn_def_id_with_node_args<'tcx>(
2129 cx: &LateContext<'tcx>,
2130 expr: &Expr<'_>,
2131) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2132 let typeck = cx.typeck_results();
2133 match &expr.kind {
2134 ExprKind::MethodCall(..) => Some((
2135 typeck.type_dependent_def_id(expr.hir_id)?,
2136 typeck.node_args(expr.hir_id),
2137 )),
2138 ExprKind::Call(
2139 Expr {
2140 kind: ExprKind::Path(qpath),
2141 hir_id: path_hir_id,
2142 ..
2143 },
2144 ..,
2145 ) => {
2146 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2149 typeck.qpath_res(qpath, *path_hir_id)
2150 {
2151 Some((id, typeck.node_args(*path_hir_id)))
2152 } else {
2153 None
2154 }
2155 },
2156 _ => None,
2157 }
2158}
2159
2160pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2165 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2166 let expr_kind = expr_type.kind();
2167 let is_primitive = match expr_kind {
2168 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2169 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2170 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2171 is_recursively_primitive_type(*element_type)
2172 } else {
2173 unreachable!()
2174 }
2175 },
2176 _ => false,
2177 };
2178
2179 if is_primitive {
2180 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2183 rustc_ty::Slice(..) => return Some("slice".into()),
2184 rustc_ty::Array(..) => return Some("array".into()),
2185 rustc_ty::Tuple(..) => return Some("tuple".into()),
2186 _ => {
2187 let refs_peeled = expr_type.peel_refs();
2190 return Some(refs_peeled.walk().last().unwrap().to_string());
2191 },
2192 }
2193 }
2194 None
2195}
2196
2197pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2205where
2206 Hash: FnMut(&T) -> u64,
2207 Eq: FnMut(&T, &T) -> bool,
2208{
2209 match exprs {
2210 [a, b] if eq(a, b) => return vec![vec![a, b]],
2211 _ if exprs.len() <= 2 => return vec![],
2212 _ => {},
2213 }
2214
2215 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2216
2217 for expr in exprs {
2218 match buckets.entry(hash(expr)) {
2219 indexmap::map::Entry::Occupied(mut o) => {
2220 let bucket = o.get_mut();
2221 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2222 Some(group) => group.push(expr),
2223 None => bucket.push(vec![expr]),
2224 }
2225 },
2226 indexmap::map::Entry::Vacant(v) => {
2227 v.insert(vec![vec![expr]]);
2228 },
2229 }
2230 }
2231
2232 buckets
2233 .into_values()
2234 .flatten()
2235 .filter(|group| group.len() > 1)
2236 .collect()
2237}
2238
2239pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2242 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2243 if let PatKind::Ref(pat, _) = pat.kind {
2244 peel(pat, count + 1)
2245 } else {
2246 (pat, count)
2247 }
2248 }
2249 peel(pat, 0)
2250}
2251
2252pub fn peel_hir_expr_while<'tcx>(
2254 mut expr: &'tcx Expr<'tcx>,
2255 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2256) -> &'tcx Expr<'tcx> {
2257 while let Some(e) = f(expr) {
2258 expr = e;
2259 }
2260 expr
2261}
2262
2263pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2266 let mut remaining = count;
2267 let e = peel_hir_expr_while(expr, |e| match e.kind {
2268 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2269 remaining -= 1;
2270 Some(e)
2271 },
2272 _ => None,
2273 });
2274 (e, count - remaining)
2275}
2276
2277pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2280 let mut count: usize = 0;
2281 let mut curr_expr = expr;
2282 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2283 count = count.wrapping_add(1);
2284 curr_expr = local_expr;
2285 }
2286 (curr_expr, count)
2287}
2288
2289pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2292 let mut count = 0;
2293 let e = peel_hir_expr_while(expr, |e| match e.kind {
2294 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2295 count += 1;
2296 Some(e)
2297 },
2298 _ => None,
2299 });
2300 (e, count)
2301}
2302
2303pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2306 let mut count = 0;
2307 loop {
2308 match &ty.kind {
2309 TyKind::Ref(_, ref_ty) => {
2310 ty = ref_ty.ty;
2311 count += 1;
2312 },
2313 _ => break (ty, count),
2314 }
2315 }
2316}
2317
2318pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2321 let mut count = 0;
2322 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2323 ty = *dest_ty;
2324 count += 1;
2325 }
2326 (ty, count)
2327}
2328
2329pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2332 loop {
2333 match expr.kind {
2334 ExprKind::AddrOf(_, _, e) => expr = e,
2335 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2336 _ => break,
2337 }
2338 }
2339 expr
2340}
2341
2342pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2343 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2344 && let Res::Def(_, def_id) = path.res
2345 {
2346 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2347 }
2348 false
2349}
2350
2351static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2352
2353fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2356 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2357 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2358 let value = map.entry(module);
2359 match value {
2360 Entry::Occupied(entry) => f(entry.get()),
2361 Entry::Vacant(entry) => {
2362 let mut names = Vec::new();
2363 for id in tcx.hir_module_free_items(module) {
2364 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2365 && let item = tcx.hir_item(id)
2366 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2367 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2368 && let Res::Def(DefKind::Struct, _) = path.res
2370 {
2371 let has_test_marker = tcx
2372 .hir_attrs(item.hir_id())
2373 .iter()
2374 .any(|a| a.has_name(sym::rustc_test_marker));
2375 if has_test_marker {
2376 names.push(ident.name);
2377 }
2378 }
2379 }
2380 names.sort_unstable();
2381 f(entry.insert(names))
2382 },
2383 }
2384}
2385
2386pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2390 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2391 let node = tcx.hir_node(id);
2392 once((id, node))
2393 .chain(tcx.hir_parent_iter(id))
2394 .any(|(_id, node)| {
2397 if let Node::Item(item) = node
2398 && let ItemKind::Fn { ident, .. } = item.kind
2399 {
2400 return names.binary_search(&ident.name).is_ok();
2403 }
2404 false
2405 })
2406 })
2407}
2408
2409pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2416 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2417 if let Node::Item(item) = tcx.hir_node(id)
2418 && let ItemKind::Fn { ident, .. } = item.kind
2419 {
2420 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2421 names.binary_search(&ident.name).is_ok()
2422 })
2423 } else {
2424 false
2425 }
2426}
2427
2428pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2433 tcx.hir_attrs(id).iter().any(|attr| {
2434 if attr.has_name(sym::cfg_trace)
2435 && let Some(items) = attr.meta_item_list()
2436 && let [item] = &*items
2437 && item.has_name(sym::test)
2438 {
2439 true
2440 } else {
2441 false
2442 }
2443 })
2444}
2445
2446pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2448 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2449}
2450
2451pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2453 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2454}
2455
2456pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2458 tcx.has_attr(def_id, sym::cfg_trace)
2459 || tcx
2460 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2461 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2462 .any(|attr| attr.has_name(sym::cfg_trace))
2463}
2464
2465pub fn walk_to_expr_usage<'tcx, T>(
2476 cx: &LateContext<'tcx>,
2477 e: &Expr<'tcx>,
2478 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2479) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2480 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2481 let mut child_id = e.hir_id;
2482
2483 while let Some((parent_id, parent)) = iter.next() {
2484 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2485 return Some(ControlFlow::Break(x));
2486 }
2487 let parent_expr = match parent {
2488 Node::Expr(e) => e,
2489 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2490 child_id = parent_id;
2491 continue;
2492 },
2493 Node::Arm(a) if a.body.hir_id == child_id => {
2494 child_id = parent_id;
2495 continue;
2496 },
2497 _ => return Some(ControlFlow::Continue((parent, child_id))),
2498 };
2499 match parent_expr.kind {
2500 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2501 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2502 child_id = id;
2503 iter = cx.tcx.hir_parent_iter(id);
2504 },
2505 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2506 _ => return Some(ControlFlow::Continue((parent, child_id))),
2507 }
2508 }
2509 debug_assert!(false, "no parent node found for `{child_id:?}`");
2510 None
2511}
2512
2513#[derive(Clone, Copy)]
2515pub enum DefinedTy<'tcx> {
2516 Hir(&'tcx hir::Ty<'tcx>),
2518 Mir {
2526 def_site_def_id: Option<DefId>,
2527 ty: Binder<'tcx, Ty<'tcx>>,
2528 },
2529}
2530
2531pub struct ExprUseCtxt<'tcx> {
2533 pub node: Node<'tcx>,
2535 pub child_id: HirId,
2537 pub adjustments: &'tcx [Adjustment<'tcx>],
2539 pub is_ty_unified: bool,
2541 pub moved_before_use: bool,
2543 pub same_ctxt: bool,
2545}
2546impl<'tcx> ExprUseCtxt<'tcx> {
2547 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2548 match self.node {
2549 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2550 Node::ExprField(field) => ExprUseNode::Field(field),
2551
2552 Node::Item(&Item {
2553 kind: ItemKind::Static(..) | ItemKind::Const(..),
2554 owner_id,
2555 ..
2556 })
2557 | Node::TraitItem(&TraitItem {
2558 kind: TraitItemKind::Const(..),
2559 owner_id,
2560 ..
2561 })
2562 | Node::ImplItem(&ImplItem {
2563 kind: ImplItemKind::Const(..),
2564 owner_id,
2565 ..
2566 }) => ExprUseNode::ConstStatic(owner_id),
2567
2568 Node::Item(&Item {
2569 kind: ItemKind::Fn { .. },
2570 owner_id,
2571 ..
2572 })
2573 | Node::TraitItem(&TraitItem {
2574 kind: TraitItemKind::Fn(..),
2575 owner_id,
2576 ..
2577 })
2578 | Node::ImplItem(&ImplItem {
2579 kind: ImplItemKind::Fn(..),
2580 owner_id,
2581 ..
2582 }) => ExprUseNode::Return(owner_id),
2583
2584 Node::Expr(use_expr) => match use_expr.kind {
2585 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2586 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2587 }),
2588
2589 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2590 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2591 Some(i) => ExprUseNode::FnArg(func, i),
2592 None => ExprUseNode::Callee,
2593 },
2594 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2595 use_expr.hir_id,
2596 name.args,
2597 args.iter()
2598 .position(|arg| arg.hir_id == self.child_id)
2599 .map_or(0, |i| i + 1),
2600 ),
2601 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2602 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2603 _ => ExprUseNode::Other,
2604 },
2605 _ => ExprUseNode::Other,
2606 }
2607 }
2608}
2609
2610pub enum ExprUseNode<'tcx> {
2612 LetStmt(&'tcx LetStmt<'tcx>),
2614 ConstStatic(OwnerId),
2616 Return(OwnerId),
2618 Field(&'tcx ExprField<'tcx>),
2620 FnArg(&'tcx Expr<'tcx>, usize),
2622 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2624 Callee,
2626 FieldAccess(Ident),
2628 AddrOf(ast::BorrowKind, Mutability),
2630 Other,
2631}
2632impl<'tcx> ExprUseNode<'tcx> {
2633 pub fn is_return(&self) -> bool {
2635 matches!(self, Self::Return(_))
2636 }
2637
2638 pub fn is_recv(&self) -> bool {
2640 matches!(self, Self::MethodArg(_, _, 0))
2641 }
2642
2643 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2645 match *self {
2646 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2647 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2648 def_site_def_id: Some(id.def_id.to_def_id()),
2649 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2650 }),
2651 Self::Return(id) => {
2652 if let Node::Expr(Expr {
2653 kind: ExprKind::Closure(c),
2654 ..
2655 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2656 {
2657 match c.fn_decl.output {
2658 FnRetTy::DefaultReturn(_) => None,
2659 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2660 }
2661 } else {
2662 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2663 Some(DefinedTy::Mir {
2664 def_site_def_id: Some(id.def_id.to_def_id()),
2665 ty,
2666 })
2667 }
2668 },
2669 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2670 Some(Expr {
2671 hir_id,
2672 kind: ExprKind::Struct(path, ..),
2673 ..
2674 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2675 .and_then(|(adt, variant)| {
2676 variant
2677 .fields
2678 .iter()
2679 .find(|f| f.name == field.ident.name)
2680 .map(|f| (adt, f))
2681 })
2682 .map(|(adt, field_def)| DefinedTy::Mir {
2683 def_site_def_id: Some(adt.did()),
2684 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2685 }),
2686 _ => None,
2687 },
2688 Self::FnArg(callee, i) => {
2689 let sig = expr_sig(cx, callee)?;
2690 let (hir_ty, ty) = sig.input_with_hir(i)?;
2691 Some(match hir_ty {
2692 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2693 None => DefinedTy::Mir {
2694 def_site_def_id: sig.predicates_id(),
2695 ty,
2696 },
2697 })
2698 },
2699 Self::MethodArg(id, _, i) => {
2700 let id = cx.typeck_results().type_dependent_def_id(id)?;
2701 let sig = cx.tcx.fn_sig(id).skip_binder();
2702 Some(DefinedTy::Mir {
2703 def_site_def_id: Some(id),
2704 ty: sig.input(i),
2705 })
2706 },
2707 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2708 }
2709 }
2710}
2711
2712pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2714 let mut adjustments = [].as_slice();
2715 let mut is_ty_unified = false;
2716 let mut moved_before_use = false;
2717 let mut same_ctxt = true;
2718 let ctxt = e.span.ctxt();
2719 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2720 if adjustments.is_empty()
2721 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2722 {
2723 adjustments = cx.typeck_results().expr_adjustments(e);
2724 }
2725 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2726 if let Node::Expr(e) = parent {
2727 match e.kind {
2728 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2729 is_ty_unified = true;
2730 moved_before_use = true;
2731 },
2732 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2733 is_ty_unified = true;
2734 moved_before_use = true;
2735 },
2736 ExprKind::Block(..) => moved_before_use = true,
2737 _ => {},
2738 }
2739 }
2740 ControlFlow::Continue(())
2741 });
2742 match node {
2743 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2744 node,
2745 child_id,
2746 adjustments,
2747 is_ty_unified,
2748 moved_before_use,
2749 same_ctxt,
2750 },
2751 #[allow(unreachable_patterns)]
2752 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
2753 None => ExprUseCtxt {
2754 node: Node::Crate(cx.tcx.hir_root_module()),
2755 child_id: HirId::INVALID,
2756 adjustments: &[],
2757 is_ty_unified: true,
2758 moved_before_use: true,
2759 same_ctxt: false,
2760 },
2761 }
2762}
2763
2764pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2766 let mut pos = 0;
2767 tokenize(s).map(move |t| {
2768 let end = pos + t.len;
2769 let range = pos as usize..end as usize;
2770 let inner = InnerSpan::new(range.start, range.end);
2771 pos = end;
2772 (t.kind, s.get(range).unwrap_or_default(), inner)
2773 })
2774}
2775
2776pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2779 let Ok(snippet) = sm.span_to_snippet(span) else {
2780 return false;
2781 };
2782 return tokenize(&snippet).any(|token| {
2783 matches!(
2784 token.kind,
2785 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2786 )
2787 });
2788}
2789
2790pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2795 matches!(span.get_source_text(cx), Some(snippet) if tokenize_with_text(&snippet).any(|(token, _, _)|
2796 match token {
2797 TokenKind::Whitespace => false,
2798 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2799 _ => true,
2800 }
2801 ))
2802}
2803pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2807 span_extract_comments(sm, span).join("\n")
2808}
2809
2810pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2814 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2815 tokenize_with_text(&snippet)
2816 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2817 .map(|(_, s, _)| s.to_string())
2818 .collect::<Vec<_>>()
2819}
2820
2821pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2822 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2823}
2824
2825pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2850 cx: &LateContext<'_>,
2851 pat: &'a Pat<'hir>,
2852 else_body: &Expr<'_>,
2853) -> Option<&'a Pat<'hir>> {
2854 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
2855 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
2856 && !is_refutable(cx, inner_pat)
2857 && let else_body = peel_blocks(else_body)
2858 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2859 && let ExprKind::Path(ret_path) = ret_val.kind
2860 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
2861 {
2862 Some(inner_pat)
2863 } else {
2864 None
2865 }
2866}
2867
2868macro_rules! op_utils {
2869 ($($name:ident $assign:ident)*) => {
2870 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2872
2873 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2875
2876 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2878 match kind {
2879 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2880 _ => None,
2881 }
2882 }
2883 };
2884}
2885
2886op_utils! {
2887 Add AddAssign
2888 Sub SubAssign
2889 Mul MulAssign
2890 Div DivAssign
2891 Rem RemAssign
2892 BitXor BitXorAssign
2893 BitAnd BitAndAssign
2894 BitOr BitOrAssign
2895 Shl ShlAssign
2896 Shr ShrAssign
2897}
2898
2899pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2902 match *pat {
2903 PatKind::Wild => true,
2904 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2905 !visitors::is_local_used(cx, body, id)
2906 },
2907 _ => false,
2908 }
2909}
2910
2911#[derive(Clone, Copy)]
2912pub enum RequiresSemi {
2913 Yes,
2914 No,
2915}
2916impl RequiresSemi {
2917 pub fn requires_semi(self) -> bool {
2918 matches!(self, Self::Yes)
2919 }
2920}
2921
2922#[expect(clippy::too_many_lines)]
2925pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2926 struct BreakTarget {
2927 id: HirId,
2928 unused: bool,
2929 }
2930
2931 struct V<'cx, 'tcx> {
2932 cx: &'cx LateContext<'tcx>,
2933 break_targets: Vec<BreakTarget>,
2934 break_targets_for_result_ty: u32,
2935 in_final_expr: bool,
2936 requires_semi: bool,
2937 is_never: bool,
2938 }
2939
2940 impl V<'_, '_> {
2941 fn push_break_target(&mut self, id: HirId) {
2942 self.break_targets.push(BreakTarget { id, unused: true });
2943 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2944 }
2945 }
2946
2947 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
2948 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
2949 if self.is_never && self.break_targets.is_empty() {
2966 if self.in_final_expr && !self.requires_semi {
2967 match e.kind {
2970 ExprKind::DropTemps(e) => self.visit_expr(e),
2971 ExprKind::If(_, then, Some(else_)) => {
2972 self.visit_expr(then);
2973 self.visit_expr(else_);
2974 },
2975 ExprKind::Match(_, arms, _) => {
2976 for arm in arms {
2977 self.visit_expr(arm.body);
2978 }
2979 },
2980 ExprKind::Loop(b, ..) => {
2981 self.push_break_target(e.hir_id);
2982 self.in_final_expr = false;
2983 self.visit_block(b);
2984 self.break_targets.pop();
2985 },
2986 ExprKind::Block(b, _) => {
2987 if b.targeted_by_break {
2988 self.push_break_target(b.hir_id);
2989 self.visit_block(b);
2990 self.break_targets.pop();
2991 } else {
2992 self.visit_block(b);
2993 }
2994 },
2995 _ => {
2996 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
2997 },
2998 }
2999 }
3000 return;
3001 }
3002 match e.kind {
3003 ExprKind::DropTemps(e) => self.visit_expr(e),
3004 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3005 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3006 self.in_final_expr = false;
3007 self.visit_expr(e);
3008 self.is_never = true;
3009 },
3010 ExprKind::Break(dest, e) => {
3011 if let Some(e) = e {
3012 self.in_final_expr = false;
3013 self.visit_expr(e);
3014 }
3015 if let Ok(id) = dest.target_id
3016 && let Some((i, target)) = self
3017 .break_targets
3018 .iter_mut()
3019 .enumerate()
3020 .find(|(_, target)| target.id == id)
3021 {
3022 target.unused &= self.is_never;
3023 if i < self.break_targets_for_result_ty as usize {
3024 self.requires_semi = true;
3025 }
3026 }
3027 self.is_never = true;
3028 },
3029 ExprKind::If(cond, then, else_) => {
3030 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3031 self.visit_expr(cond);
3032 self.in_final_expr = in_final_expr;
3033
3034 if self.is_never {
3035 self.visit_expr(then);
3036 if let Some(else_) = else_ {
3037 self.visit_expr(else_);
3038 }
3039 } else {
3040 self.visit_expr(then);
3041 let is_never = mem::replace(&mut self.is_never, false);
3042 if let Some(else_) = else_ {
3043 self.visit_expr(else_);
3044 self.is_never &= is_never;
3045 }
3046 }
3047 },
3048 ExprKind::Match(scrutinee, arms, _) => {
3049 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3050 self.visit_expr(scrutinee);
3051 self.in_final_expr = in_final_expr;
3052
3053 if self.is_never {
3054 for arm in arms {
3055 self.visit_arm(arm);
3056 }
3057 } else {
3058 let mut is_never = true;
3059 for arm in arms {
3060 self.is_never = false;
3061 if let Some(guard) = arm.guard {
3062 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3063 self.visit_expr(guard);
3064 self.in_final_expr = in_final_expr;
3065 self.is_never = false;
3067 }
3068 self.visit_expr(arm.body);
3069 is_never &= self.is_never;
3070 }
3071 self.is_never = is_never;
3072 }
3073 },
3074 ExprKind::Loop(b, _, _, _) => {
3075 self.push_break_target(e.hir_id);
3076 self.in_final_expr = false;
3077 self.visit_block(b);
3078 self.is_never = self.break_targets.pop().unwrap().unused;
3079 },
3080 ExprKind::Block(b, _) => {
3081 if b.targeted_by_break {
3082 self.push_break_target(b.hir_id);
3083 self.visit_block(b);
3084 self.is_never &= self.break_targets.pop().unwrap().unused;
3085 } else {
3086 self.visit_block(b);
3087 }
3088 },
3089 _ => {
3090 self.in_final_expr = false;
3091 walk_expr(self, e);
3092 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3093 },
3094 }
3095 }
3096
3097 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3098 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3099 for s in b.stmts {
3100 self.visit_stmt(s);
3101 }
3102 self.in_final_expr = in_final_expr;
3103 if let Some(e) = b.expr {
3104 self.visit_expr(e);
3105 }
3106 }
3107
3108 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3109 if let Some(e) = l.init {
3110 self.visit_expr(e);
3111 }
3112 if let Some(else_) = l.els {
3113 let is_never = self.is_never;
3114 self.visit_block(else_);
3115 self.is_never = is_never;
3116 }
3117 }
3118
3119 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3120 if let Some(guard) = arm.guard {
3121 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3122 self.visit_expr(guard);
3123 self.in_final_expr = in_final_expr;
3124 }
3125 self.visit_expr(arm.body);
3126 }
3127 }
3128
3129 if cx.typeck_results().expr_ty(e).is_never() {
3130 Some(RequiresSemi::No)
3131 } else if let ExprKind::Block(b, _) = e.kind
3132 && !b.targeted_by_break
3133 && b.expr.is_none()
3134 {
3135 None
3137 } else {
3138 let mut v = V {
3139 cx,
3140 break_targets: Vec::new(),
3141 break_targets_for_result_ty: 0,
3142 in_final_expr: true,
3143 requires_semi: false,
3144 is_never: false,
3145 };
3146 v.visit_expr(e);
3147 v.is_never
3148 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3149 RequiresSemi::Yes
3150 } else {
3151 RequiresSemi::No
3152 })
3153 }
3154}
3155
3156pub fn get_path_from_caller_to_method_type<'tcx>(
3162 tcx: TyCtxt<'tcx>,
3163 from: LocalDefId,
3164 method: DefId,
3165 args: GenericArgsRef<'tcx>,
3166) -> String {
3167 let assoc_item = tcx.associated_item(method);
3168 let def_id = assoc_item.container_id(tcx);
3169 match assoc_item.container {
3170 rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
3171 rustc_ty::AssocItemContainer::Impl => {
3172 let ty = tcx.type_of(def_id).instantiate_identity();
3173 get_path_to_ty(tcx, from, ty, args)
3174 },
3175 }
3176}
3177
3178fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3179 match ty.kind() {
3180 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3181 rustc_ty::Array(..)
3183 | rustc_ty::Dynamic(..)
3184 | rustc_ty::Never
3185 | rustc_ty::RawPtr(_, _)
3186 | rustc_ty::Ref(..)
3187 | rustc_ty::Slice(_)
3188 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3189 _ => ty.to_string(),
3190 }
3191}
3192
3193fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3195 if callee.is_local() {
3197 let callee_path = tcx.def_path(callee);
3198 let caller_path = tcx.def_path(from.to_def_id());
3199 maybe_get_relative_path(&caller_path, &callee_path, 2)
3200 } else {
3201 tcx.def_path_str(callee)
3202 }
3203}
3204
3205fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3218 use itertools::EitherOrBoth::{Both, Left, Right};
3219
3220 let unique_parts = to
3222 .data
3223 .iter()
3224 .zip_longest(from.data.iter())
3225 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3226 .map(|el| match el {
3227 Both(l, r) => Both(l.data, r.data),
3228 Left(l) => Left(l.data),
3229 Right(r) => Right(r.data),
3230 });
3231
3232 let mut go_up_by = 0;
3234 let mut path = Vec::new();
3235 for el in unique_parts {
3236 match el {
3237 Both(l, r) => {
3238 if let DefPathData::TypeNs(s) = l {
3248 path.push(s.to_string());
3249 }
3250 if let DefPathData::TypeNs(_) = r {
3251 go_up_by += 1;
3252 }
3253 },
3254 Left(DefPathData::TypeNs(sym)) => path.push(sym.to_string()),
3259 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3264 _ => {},
3265 }
3266 }
3267
3268 if go_up_by > max_super {
3269 once(String::from("crate"))
3271 .chain(to.data.iter().filter_map(|el| {
3272 if let DefPathData::TypeNs(sym) = el.data {
3273 Some(sym.to_string())
3274 } else {
3275 None
3276 }
3277 }))
3278 .join("::")
3279 } else {
3280 repeat_n(String::from("super"), go_up_by).chain(path).join("::")
3281 }
3282}
3283
3284pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3287 matches!(
3288 cx.tcx.parent_hir_node(id),
3289 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3290 )
3291}
3292
3293pub fn is_block_like(expr: &Expr<'_>) -> bool {
3296 matches!(
3297 expr.kind,
3298 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3299 )
3300}
3301
3302pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3304 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3305 match expr.kind {
3306 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3307 _ if is_block_like(expr) => is_operand,
3308 _ => false,
3309 }
3310 }
3311
3312 contains_block(expr, false)
3313}
3314
3315pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3317 if let Some(parent_expr) = get_parent_expr(cx, expr)
3318 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3319 && receiver.hir_id == expr.hir_id
3320 {
3321 return true;
3322 }
3323 false
3324}
3325
3326pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3329 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3330 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3331 && temporary_ty
3332 .walk()
3333 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3334 {
3335 ControlFlow::Break(())
3336 } else {
3337 ControlFlow::Continue(())
3338 }
3339 })
3340 .is_break()
3341}
3342
3343pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3354 let expr_ty_is_adjusted = cx
3355 .typeck_results()
3356 .expr_adjustments(expr)
3357 .iter()
3358 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3360 if expr_ty_is_adjusted {
3361 return true;
3362 }
3363
3364 match expr.kind {
3367 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3368 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3369
3370 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3371 return false;
3372 }
3373
3374 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3375 let mut args_with_ty_param = {
3376 fn_sig
3377 .inputs()
3378 .skip_binder()
3379 .iter()
3380 .skip(self_arg_count)
3381 .zip(args)
3382 .filter_map(|(arg_ty, arg)| {
3383 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3384 Some(arg)
3385 } else {
3386 None
3387 }
3388 })
3389 };
3390 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3391 },
3392 ExprKind::Struct(qpath, _, _) => {
3394 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3395 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3396 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3397 return true;
3399 };
3400 v_def
3401 .fields
3402 .iter()
3403 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3404 } else {
3405 false
3406 }
3407 },
3408 ExprKind::Block(
3410 &Block {
3411 expr: Some(ret_expr), ..
3412 },
3413 _,
3414 )
3415 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3416
3417 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3419 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3421 ExprKind::If(_, then, maybe_else) => {
3423 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3424 },
3425 ExprKind::Match(_, arms, _) => arms
3426 .iter()
3427 .map(|arm| arm.body)
3428 .any(|body| expr_requires_coercion(cx, body)),
3429 _ => false,
3430 }
3431}
3432
3433pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3436 if let Some(hir_id) = path_to_local(expr)
3437 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3438 {
3439 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3440 } else if let ExprKind::Path(p) = &expr.kind
3441 && let Some(mutability) = cx
3442 .qpath_res(p, expr.hir_id)
3443 .opt_def_id()
3444 .and_then(|id| cx.tcx.static_mutability(id))
3445 {
3446 mutability == Mutability::Mut
3447 } else if let ExprKind::Field(parent, _) = expr.kind {
3448 is_mutable(cx, parent)
3449 } else {
3450 true
3451 }
3452}
3453
3454pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3457 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3458 return hir_ty;
3459 };
3460 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3461 && let Some(segment) = path.segments.last()
3462 && segment.ident.name == sym::Option
3463 && let Res::Def(DefKind::Enum, def_id) = segment.res
3464 && def_id == option_def_id
3465 && let [GenericArg::Type(arg_ty)] = segment.args().args
3466 {
3467 hir_ty = arg_ty.as_unambig_ty();
3468 }
3469 hir_ty
3470}
3471
3472pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3475 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3476 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3477 && let ctxt = expr.span.ctxt()
3478 && for_each_expr_without_closures(into_future_arg, |e| {
3479 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3480 })
3481 .is_none()
3482 {
3483 Some(into_future_arg)
3484 } else {
3485 None
3486 }
3487}
3488
3489pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3491 if let ExprKind::Call(fn_expr, []) = &expr.kind
3492 && let ExprKind::Path(qpath) = &fn_expr.kind
3493 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3494 {
3495 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3496 } else {
3497 false
3498 }
3499}