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