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_parsing;
32extern crate rustc_const_eval;
33extern crate rustc_data_structures;
34#[allow(unused_extern_crates)]
36extern crate rustc_driver;
37extern crate rustc_errors;
38extern crate rustc_hir;
39extern crate rustc_hir_analysis;
40extern crate rustc_hir_typeck;
41extern crate rustc_index;
42extern crate rustc_infer;
43extern crate rustc_lexer;
44extern crate rustc_lint;
45extern crate rustc_middle;
46extern crate rustc_mir_dataflow;
47extern crate rustc_session;
48extern crate rustc_span;
49extern crate rustc_trait_selection;
50extern crate smallvec;
51
52pub mod ast_utils;
53pub mod attrs;
54mod check_proc_macro;
55pub mod comparisons;
56pub mod consts;
57pub mod diagnostics;
58pub mod eager_or_lazy;
59pub mod higher;
60mod hir_utils;
61pub mod macros;
62pub mod mir;
63pub mod msrvs;
64pub mod numeric_literal;
65pub mod paths;
66pub mod ptr;
67pub mod qualify_min_const_fn;
68pub mod source;
69pub mod str_utils;
70pub mod sugg;
71pub mod sym;
72pub mod ty;
73pub mod usage;
74pub mod visitors;
75
76pub use self::attrs::*;
77pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
78pub use self::hir_utils::{
79 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
80 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_ast::join_path_syms;
93use rustc_hir::attrs::{AttributeKind};
94use rustc_hir::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, PointerCoercion};
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_assoc(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_assoc(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_assoc(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_assoc(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 {
1904 fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
1905 if cx
1906 .typeck_results()
1907 .pat_binding_modes()
1908 .get(pat.hir_id)
1909 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
1910 {
1911 return false;
1915 }
1916
1917 match (pat.kind, expr.kind) {
1918 (PatKind::Binding(_, id, _, _), _) => {
1919 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1920 },
1921 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1922 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1923 {
1924 pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr))
1925 },
1926 (PatKind::Slice(before, slice, after), ExprKind::Array(arr))
1927 if slice.is_none() && before.len() + after.len() == arr.len() =>
1928 {
1929 (before.iter().chain(after))
1930 .zip(arr)
1931 .all(|(pat, expr)| check_pat(cx, pat, expr))
1932 },
1933 _ => false,
1934 }
1935 }
1936
1937 let [param] = func.params else {
1938 return false;
1939 };
1940
1941 let mut expr = func.value;
1942 loop {
1943 match expr.kind {
1944 ExprKind::Block(
1945 &Block {
1946 stmts: [],
1947 expr: Some(e),
1948 ..
1949 },
1950 _,
1951 )
1952 | ExprKind::Ret(Some(e)) => expr = e,
1953 ExprKind::Block(
1954 &Block {
1955 stmts: [stmt],
1956 expr: None,
1957 ..
1958 },
1959 _,
1960 ) => {
1961 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1962 && let ExprKind::Ret(Some(ret_val)) = e.kind
1963 {
1964 expr = ret_val;
1965 } else {
1966 return false;
1967 }
1968 },
1969 _ => return check_pat(cx, param.pat, expr),
1970 }
1971 }
1972}
1973
1974pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1979 match expr.kind {
1980 ExprKind::Closure(&Closure { body, fn_decl, .. })
1981 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1982 {
1983 is_body_identity_function(cx, cx.tcx.hir_body(body))
1984 },
1985 ExprKind::Path(QPath::Resolved(_, path))
1986 if path.segments.iter().all(|seg| seg.infer_args)
1987 && let Some(did) = path.res.opt_def_id() =>
1988 {
1989 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1990 },
1991 _ => false,
1992 }
1993}
1994
1995pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2004 match expr.kind {
2005 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
2006 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
2007 }
2008}
2009
2010pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2013 let mut child_id = expr.hir_id;
2014 let mut iter = tcx.hir_parent_iter(child_id);
2015 loop {
2016 match iter.next() {
2017 None => break None,
2018 Some((id, Node::Block(_))) => child_id = id,
2019 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2020 Some((_, Node::Expr(expr))) => match expr.kind {
2021 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2022 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2023 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2024 _ => break Some((Node::Expr(expr), child_id)),
2025 },
2026 Some((_, node)) => break Some((node, child_id)),
2027 }
2028 }
2029}
2030
2031pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2033 !matches!(
2034 get_expr_use_or_unification_node(tcx, expr),
2035 None | Some((
2036 Node::Stmt(Stmt {
2037 kind: StmtKind::Expr(_)
2038 | StmtKind::Semi(_)
2039 | StmtKind::Let(LetStmt {
2040 pat: Pat {
2041 kind: PatKind::Wild,
2042 ..
2043 },
2044 ..
2045 }),
2046 ..
2047 }),
2048 _
2049 ))
2050 )
2051}
2052
2053pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2055 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2056}
2057
2058pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2062 !expr.is_place_expr(|base| {
2063 cx.typeck_results()
2064 .adjustments()
2065 .get(base.hir_id)
2066 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2067 })
2068}
2069
2070pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2071 if !is_no_std_crate(cx) {
2072 Some("std")
2073 } else if !is_no_core_crate(cx) {
2074 Some("core")
2075 } else {
2076 None
2077 }
2078}
2079
2080pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2081 cx.tcx
2082 .hir_attrs(hir::CRATE_HIR_ID)
2083 .iter()
2084 .any(|attr| attr.has_name(sym::no_std))
2085}
2086
2087pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2088 cx.tcx
2089 .hir_attrs(hir::CRATE_HIR_ID)
2090 .iter()
2091 .any(|attr| attr.has_name(sym::no_core))
2092}
2093
2094pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2104 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2105 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2106 } else {
2107 false
2108 }
2109}
2110
2111pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2121 use rustc_trait_selection::traits;
2122 let predicates = cx
2123 .tcx
2124 .predicates_of(did)
2125 .predicates
2126 .iter()
2127 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2128 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2129}
2130
2131pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2133 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2134}
2135
2136pub fn fn_def_id_with_node_args<'tcx>(
2139 cx: &LateContext<'tcx>,
2140 expr: &Expr<'_>,
2141) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2142 let typeck = cx.typeck_results();
2143 match &expr.kind {
2144 ExprKind::MethodCall(..) => Some((
2145 typeck.type_dependent_def_id(expr.hir_id)?,
2146 typeck.node_args(expr.hir_id),
2147 )),
2148 ExprKind::Call(
2149 Expr {
2150 kind: ExprKind::Path(qpath),
2151 hir_id: path_hir_id,
2152 ..
2153 },
2154 ..,
2155 ) => {
2156 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2159 typeck.qpath_res(qpath, *path_hir_id)
2160 {
2161 Some((id, typeck.node_args(*path_hir_id)))
2162 } else {
2163 None
2164 }
2165 },
2166 _ => None,
2167 }
2168}
2169
2170pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2175 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2176 let expr_kind = expr_type.kind();
2177 let is_primitive = match expr_kind {
2178 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2179 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2180 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2181 is_recursively_primitive_type(*element_type)
2182 } else {
2183 unreachable!()
2184 }
2185 },
2186 _ => false,
2187 };
2188
2189 if is_primitive {
2190 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2193 rustc_ty::Slice(..) => return Some("slice".into()),
2194 rustc_ty::Array(..) => return Some("array".into()),
2195 rustc_ty::Tuple(..) => return Some("tuple".into()),
2196 _ => {
2197 let refs_peeled = expr_type.peel_refs();
2200 return Some(refs_peeled.walk().last().unwrap().to_string());
2201 },
2202 }
2203 }
2204 None
2205}
2206
2207pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2215where
2216 Hash: FnMut(&T) -> u64,
2217 Eq: FnMut(&T, &T) -> bool,
2218{
2219 match exprs {
2220 [a, b] if eq(a, b) => return vec![vec![a, b]],
2221 _ if exprs.len() <= 2 => return vec![],
2222 _ => {},
2223 }
2224
2225 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2226
2227 for expr in exprs {
2228 match buckets.entry(hash(expr)) {
2229 indexmap::map::Entry::Occupied(mut o) => {
2230 let bucket = o.get_mut();
2231 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2232 Some(group) => group.push(expr),
2233 None => bucket.push(vec![expr]),
2234 }
2235 },
2236 indexmap::map::Entry::Vacant(v) => {
2237 v.insert(vec![vec![expr]]);
2238 },
2239 }
2240 }
2241
2242 buckets
2243 .into_values()
2244 .flatten()
2245 .filter(|group| group.len() > 1)
2246 .collect()
2247}
2248
2249pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2252 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2253 if let PatKind::Ref(pat, _) = pat.kind {
2254 peel(pat, count + 1)
2255 } else {
2256 (pat, count)
2257 }
2258 }
2259 peel(pat, 0)
2260}
2261
2262pub fn peel_hir_expr_while<'tcx>(
2264 mut expr: &'tcx Expr<'tcx>,
2265 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2266) -> &'tcx Expr<'tcx> {
2267 while let Some(e) = f(expr) {
2268 expr = e;
2269 }
2270 expr
2271}
2272
2273pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2276 let mut remaining = count;
2277 let e = peel_hir_expr_while(expr, |e| match e.kind {
2278 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2279 remaining -= 1;
2280 Some(e)
2281 },
2282 _ => None,
2283 });
2284 (e, count - remaining)
2285}
2286
2287pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2290 let mut count: usize = 0;
2291 let mut curr_expr = expr;
2292 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2293 count = count.wrapping_add(1);
2294 curr_expr = local_expr;
2295 }
2296 (curr_expr, count)
2297}
2298
2299pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2302 let mut count = 0;
2303 let e = peel_hir_expr_while(expr, |e| match e.kind {
2304 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2305 count += 1;
2306 Some(e)
2307 },
2308 _ => None,
2309 });
2310 (e, count)
2311}
2312
2313pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2316 let mut count = 0;
2317 loop {
2318 match &ty.kind {
2319 TyKind::Ref(_, ref_ty) => {
2320 ty = ref_ty.ty;
2321 count += 1;
2322 },
2323 _ => break (ty, count),
2324 }
2325 }
2326}
2327
2328pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2331 let mut count = 0;
2332 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2333 ty = *dest_ty;
2334 count += 1;
2335 }
2336 (ty, count)
2337}
2338
2339pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2342 loop {
2343 match expr.kind {
2344 ExprKind::AddrOf(_, _, e) => expr = e,
2345 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2346 _ => break,
2347 }
2348 }
2349 expr
2350}
2351
2352pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2353 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2354 && let Res::Def(_, def_id) = path.res
2355 {
2356 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2357 }
2358 false
2359}
2360
2361static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2362
2363fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2366 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2367 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2368 let value = map.entry(module);
2369 match value {
2370 Entry::Occupied(entry) => f(entry.get()),
2371 Entry::Vacant(entry) => {
2372 let mut names = Vec::new();
2373 for id in tcx.hir_module_free_items(module) {
2374 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2375 && let item = tcx.hir_item(id)
2376 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2377 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2378 && let Res::Def(DefKind::Struct, _) = path.res
2380 {
2381 let has_test_marker = tcx
2382 .hir_attrs(item.hir_id())
2383 .iter()
2384 .any(|a| a.has_name(sym::rustc_test_marker));
2385 if has_test_marker {
2386 names.push(ident.name);
2387 }
2388 }
2389 }
2390 names.sort_unstable();
2391 f(entry.insert(names))
2392 },
2393 }
2394}
2395
2396pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2400 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2401 let node = tcx.hir_node(id);
2402 once((id, node))
2403 .chain(tcx.hir_parent_iter(id))
2404 .any(|(_id, node)| {
2407 if let Node::Item(item) = node
2408 && let ItemKind::Fn { ident, .. } = item.kind
2409 {
2410 return names.binary_search(&ident.name).is_ok();
2413 }
2414 false
2415 })
2416 })
2417}
2418
2419pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2426 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2427 if let Node::Item(item) = tcx.hir_node(id)
2428 && let ItemKind::Fn { ident, .. } = item.kind
2429 {
2430 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2431 names.binary_search(&ident.name).is_ok()
2432 })
2433 } else {
2434 false
2435 }
2436}
2437
2438pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2443 tcx.hir_attrs(id).iter().any(|attr| {
2444 if attr.has_name(sym::cfg_trace)
2445 && let Some(items) = attr.meta_item_list()
2446 && let [item] = &*items
2447 && item.has_name(sym::test)
2448 {
2449 true
2450 } else {
2451 false
2452 }
2453 })
2454}
2455
2456pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2458 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2459}
2460
2461pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2463 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2464}
2465
2466pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2468 tcx.has_attr(def_id, sym::cfg_trace)
2469 || tcx
2470 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2471 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2472 .any(|attr| attr.has_name(sym::cfg_trace))
2473}
2474
2475pub fn walk_to_expr_usage<'tcx, T>(
2486 cx: &LateContext<'tcx>,
2487 e: &Expr<'tcx>,
2488 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2489) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2490 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2491 let mut child_id = e.hir_id;
2492
2493 while let Some((parent_id, parent)) = iter.next() {
2494 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2495 return Some(ControlFlow::Break(x));
2496 }
2497 let parent_expr = match parent {
2498 Node::Expr(e) => e,
2499 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2500 child_id = parent_id;
2501 continue;
2502 },
2503 Node::Arm(a) if a.body.hir_id == child_id => {
2504 child_id = parent_id;
2505 continue;
2506 },
2507 _ => return Some(ControlFlow::Continue((parent, child_id))),
2508 };
2509 match parent_expr.kind {
2510 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2511 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2512 child_id = id;
2513 iter = cx.tcx.hir_parent_iter(id);
2514 },
2515 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2516 _ => return Some(ControlFlow::Continue((parent, child_id))),
2517 }
2518 }
2519 debug_assert!(false, "no parent node found for `{child_id:?}`");
2520 None
2521}
2522
2523#[derive(Clone, Copy)]
2525pub enum DefinedTy<'tcx> {
2526 Hir(&'tcx hir::Ty<'tcx>),
2528 Mir {
2536 def_site_def_id: Option<DefId>,
2537 ty: Binder<'tcx, Ty<'tcx>>,
2538 },
2539}
2540
2541pub struct ExprUseCtxt<'tcx> {
2543 pub node: Node<'tcx>,
2545 pub child_id: HirId,
2547 pub adjustments: &'tcx [Adjustment<'tcx>],
2549 pub is_ty_unified: bool,
2551 pub moved_before_use: bool,
2553 pub same_ctxt: bool,
2555}
2556impl<'tcx> ExprUseCtxt<'tcx> {
2557 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2558 match self.node {
2559 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2560 Node::ExprField(field) => ExprUseNode::Field(field),
2561
2562 Node::Item(&Item {
2563 kind: ItemKind::Static(..) | ItemKind::Const(..),
2564 owner_id,
2565 ..
2566 })
2567 | Node::TraitItem(&TraitItem {
2568 kind: TraitItemKind::Const(..),
2569 owner_id,
2570 ..
2571 })
2572 | Node::ImplItem(&ImplItem {
2573 kind: ImplItemKind::Const(..),
2574 owner_id,
2575 ..
2576 }) => ExprUseNode::ConstStatic(owner_id),
2577
2578 Node::Item(&Item {
2579 kind: ItemKind::Fn { .. },
2580 owner_id,
2581 ..
2582 })
2583 | Node::TraitItem(&TraitItem {
2584 kind: TraitItemKind::Fn(..),
2585 owner_id,
2586 ..
2587 })
2588 | Node::ImplItem(&ImplItem {
2589 kind: ImplItemKind::Fn(..),
2590 owner_id,
2591 ..
2592 }) => ExprUseNode::Return(owner_id),
2593
2594 Node::Expr(use_expr) => match use_expr.kind {
2595 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2596 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2597 }),
2598
2599 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2600 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2601 Some(i) => ExprUseNode::FnArg(func, i),
2602 None => ExprUseNode::Callee,
2603 },
2604 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2605 use_expr.hir_id,
2606 name.args,
2607 args.iter()
2608 .position(|arg| arg.hir_id == self.child_id)
2609 .map_or(0, |i| i + 1),
2610 ),
2611 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2612 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2613 _ => ExprUseNode::Other,
2614 },
2615 _ => ExprUseNode::Other,
2616 }
2617 }
2618}
2619
2620pub enum ExprUseNode<'tcx> {
2622 LetStmt(&'tcx LetStmt<'tcx>),
2624 ConstStatic(OwnerId),
2626 Return(OwnerId),
2628 Field(&'tcx ExprField<'tcx>),
2630 FnArg(&'tcx Expr<'tcx>, usize),
2632 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2634 Callee,
2636 FieldAccess(Ident),
2638 AddrOf(ast::BorrowKind, Mutability),
2640 Other,
2641}
2642impl<'tcx> ExprUseNode<'tcx> {
2643 pub fn is_return(&self) -> bool {
2645 matches!(self, Self::Return(_))
2646 }
2647
2648 pub fn is_recv(&self) -> bool {
2650 matches!(self, Self::MethodArg(_, _, 0))
2651 }
2652
2653 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2655 match *self {
2656 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2657 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2658 def_site_def_id: Some(id.def_id.to_def_id()),
2659 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2660 }),
2661 Self::Return(id) => {
2662 if let Node::Expr(Expr {
2663 kind: ExprKind::Closure(c),
2664 ..
2665 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2666 {
2667 match c.fn_decl.output {
2668 FnRetTy::DefaultReturn(_) => None,
2669 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2670 }
2671 } else {
2672 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2673 Some(DefinedTy::Mir {
2674 def_site_def_id: Some(id.def_id.to_def_id()),
2675 ty,
2676 })
2677 }
2678 },
2679 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2680 Some(Expr {
2681 hir_id,
2682 kind: ExprKind::Struct(path, ..),
2683 ..
2684 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2685 .and_then(|(adt, variant)| {
2686 variant
2687 .fields
2688 .iter()
2689 .find(|f| f.name == field.ident.name)
2690 .map(|f| (adt, f))
2691 })
2692 .map(|(adt, field_def)| DefinedTy::Mir {
2693 def_site_def_id: Some(adt.did()),
2694 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2695 }),
2696 _ => None,
2697 },
2698 Self::FnArg(callee, i) => {
2699 let sig = expr_sig(cx, callee)?;
2700 let (hir_ty, ty) = sig.input_with_hir(i)?;
2701 Some(match hir_ty {
2702 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2703 None => DefinedTy::Mir {
2704 def_site_def_id: sig.predicates_id(),
2705 ty,
2706 },
2707 })
2708 },
2709 Self::MethodArg(id, _, i) => {
2710 let id = cx.typeck_results().type_dependent_def_id(id)?;
2711 let sig = cx.tcx.fn_sig(id).skip_binder();
2712 Some(DefinedTy::Mir {
2713 def_site_def_id: Some(id),
2714 ty: sig.input(i),
2715 })
2716 },
2717 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2718 }
2719 }
2720}
2721
2722pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2724 let mut adjustments = [].as_slice();
2725 let mut is_ty_unified = false;
2726 let mut moved_before_use = false;
2727 let mut same_ctxt = true;
2728 let ctxt = e.span.ctxt();
2729 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2730 if adjustments.is_empty()
2731 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2732 {
2733 adjustments = cx.typeck_results().expr_adjustments(e);
2734 }
2735 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2736 if let Node::Expr(e) = parent {
2737 match e.kind {
2738 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2739 is_ty_unified = true;
2740 moved_before_use = true;
2741 },
2742 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2743 is_ty_unified = true;
2744 moved_before_use = true;
2745 },
2746 ExprKind::Block(..) => moved_before_use = true,
2747 _ => {},
2748 }
2749 }
2750 ControlFlow::Continue(())
2751 });
2752 match node {
2753 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2754 node,
2755 child_id,
2756 adjustments,
2757 is_ty_unified,
2758 moved_before_use,
2759 same_ctxt,
2760 },
2761 #[allow(unreachable_patterns)]
2762 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
2763 None => ExprUseCtxt {
2764 node: Node::Crate(cx.tcx.hir_root_module()),
2765 child_id: HirId::INVALID,
2766 adjustments: &[],
2767 is_ty_unified: true,
2768 moved_before_use: true,
2769 same_ctxt: false,
2770 },
2771 }
2772}
2773
2774pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2776 let mut pos = 0;
2777 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2778 let end = pos + t.len;
2779 let range = pos as usize..end as usize;
2780 let inner = InnerSpan::new(range.start, range.end);
2781 pos = end;
2782 (t.kind, s.get(range).unwrap_or_default(), inner)
2783 })
2784}
2785
2786pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2789 let Ok(snippet) = sm.span_to_snippet(span) else {
2790 return false;
2791 };
2792 return tokenize(&snippet, FrontmatterAllowed::No).any(|token| {
2793 matches!(
2794 token.kind,
2795 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2796 )
2797 });
2798}
2799
2800pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2805 matches!(span.get_source_text(cx), Some(snippet) if tokenize_with_text(&snippet).any(|(token, _, _)|
2806 match token {
2807 TokenKind::Whitespace => false,
2808 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2809 _ => true,
2810 }
2811 ))
2812}
2813pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2817 span_extract_comments(sm, span).join("\n")
2818}
2819
2820pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2824 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2825 tokenize_with_text(&snippet)
2826 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2827 .map(|(_, s, _)| s.to_string())
2828 .collect::<Vec<_>>()
2829}
2830
2831pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2832 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2833}
2834
2835pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2860 cx: &LateContext<'_>,
2861 pat: &'a Pat<'hir>,
2862 else_body: &Expr<'_>,
2863) -> Option<&'a Pat<'hir>> {
2864 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
2865 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
2866 && !is_refutable(cx, inner_pat)
2867 && let else_body = peel_blocks(else_body)
2868 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2869 && let ExprKind::Path(ret_path) = ret_val.kind
2870 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
2871 {
2872 Some(inner_pat)
2873 } else {
2874 None
2875 }
2876}
2877
2878macro_rules! op_utils {
2879 ($($name:ident $assign:ident)*) => {
2880 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2882
2883 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2885
2886 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2888 match kind {
2889 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2890 _ => None,
2891 }
2892 }
2893 };
2894}
2895
2896op_utils! {
2897 Add AddAssign
2898 Sub SubAssign
2899 Mul MulAssign
2900 Div DivAssign
2901 Rem RemAssign
2902 BitXor BitXorAssign
2903 BitAnd BitAndAssign
2904 BitOr BitOrAssign
2905 Shl ShlAssign
2906 Shr ShrAssign
2907}
2908
2909pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2912 match *pat {
2913 PatKind::Wild => true,
2914 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2915 !visitors::is_local_used(cx, body, id)
2916 },
2917 _ => false,
2918 }
2919}
2920
2921#[derive(Clone, Copy)]
2922pub enum RequiresSemi {
2923 Yes,
2924 No,
2925}
2926impl RequiresSemi {
2927 pub fn requires_semi(self) -> bool {
2928 matches!(self, Self::Yes)
2929 }
2930}
2931
2932#[expect(clippy::too_many_lines)]
2935pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2936 struct BreakTarget {
2937 id: HirId,
2938 unused: bool,
2939 }
2940
2941 struct V<'cx, 'tcx> {
2942 cx: &'cx LateContext<'tcx>,
2943 break_targets: Vec<BreakTarget>,
2944 break_targets_for_result_ty: u32,
2945 in_final_expr: bool,
2946 requires_semi: bool,
2947 is_never: bool,
2948 }
2949
2950 impl V<'_, '_> {
2951 fn push_break_target(&mut self, id: HirId) {
2952 self.break_targets.push(BreakTarget { id, unused: true });
2953 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2954 }
2955 }
2956
2957 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
2958 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
2959 if self.is_never && self.break_targets.is_empty() {
2976 if self.in_final_expr && !self.requires_semi {
2977 match e.kind {
2980 ExprKind::DropTemps(e) => self.visit_expr(e),
2981 ExprKind::If(_, then, Some(else_)) => {
2982 self.visit_expr(then);
2983 self.visit_expr(else_);
2984 },
2985 ExprKind::Match(_, arms, _) => {
2986 for arm in arms {
2987 self.visit_expr(arm.body);
2988 }
2989 },
2990 ExprKind::Loop(b, ..) => {
2991 self.push_break_target(e.hir_id);
2992 self.in_final_expr = false;
2993 self.visit_block(b);
2994 self.break_targets.pop();
2995 },
2996 ExprKind::Block(b, _) => {
2997 if b.targeted_by_break {
2998 self.push_break_target(b.hir_id);
2999 self.visit_block(b);
3000 self.break_targets.pop();
3001 } else {
3002 self.visit_block(b);
3003 }
3004 },
3005 _ => {
3006 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3007 },
3008 }
3009 }
3010 return;
3011 }
3012 match e.kind {
3013 ExprKind::DropTemps(e) => self.visit_expr(e),
3014 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3015 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3016 self.in_final_expr = false;
3017 self.visit_expr(e);
3018 self.is_never = true;
3019 },
3020 ExprKind::Break(dest, e) => {
3021 if let Some(e) = e {
3022 self.in_final_expr = false;
3023 self.visit_expr(e);
3024 }
3025 if let Ok(id) = dest.target_id
3026 && let Some((i, target)) = self
3027 .break_targets
3028 .iter_mut()
3029 .enumerate()
3030 .find(|(_, target)| target.id == id)
3031 {
3032 target.unused &= self.is_never;
3033 if i < self.break_targets_for_result_ty as usize {
3034 self.requires_semi = true;
3035 }
3036 }
3037 self.is_never = true;
3038 },
3039 ExprKind::If(cond, then, else_) => {
3040 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3041 self.visit_expr(cond);
3042 self.in_final_expr = in_final_expr;
3043
3044 if self.is_never {
3045 self.visit_expr(then);
3046 if let Some(else_) = else_ {
3047 self.visit_expr(else_);
3048 }
3049 } else {
3050 self.visit_expr(then);
3051 let is_never = mem::replace(&mut self.is_never, false);
3052 if let Some(else_) = else_ {
3053 self.visit_expr(else_);
3054 self.is_never &= is_never;
3055 }
3056 }
3057 },
3058 ExprKind::Match(scrutinee, arms, _) => {
3059 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3060 self.visit_expr(scrutinee);
3061 self.in_final_expr = in_final_expr;
3062
3063 if self.is_never {
3064 for arm in arms {
3065 self.visit_arm(arm);
3066 }
3067 } else {
3068 let mut is_never = true;
3069 for arm in arms {
3070 self.is_never = false;
3071 if let Some(guard) = arm.guard {
3072 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3073 self.visit_expr(guard);
3074 self.in_final_expr = in_final_expr;
3075 self.is_never = false;
3077 }
3078 self.visit_expr(arm.body);
3079 is_never &= self.is_never;
3080 }
3081 self.is_never = is_never;
3082 }
3083 },
3084 ExprKind::Loop(b, _, _, _) => {
3085 self.push_break_target(e.hir_id);
3086 self.in_final_expr = false;
3087 self.visit_block(b);
3088 self.is_never = self.break_targets.pop().unwrap().unused;
3089 },
3090 ExprKind::Block(b, _) => {
3091 if b.targeted_by_break {
3092 self.push_break_target(b.hir_id);
3093 self.visit_block(b);
3094 self.is_never &= self.break_targets.pop().unwrap().unused;
3095 } else {
3096 self.visit_block(b);
3097 }
3098 },
3099 _ => {
3100 self.in_final_expr = false;
3101 walk_expr(self, e);
3102 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3103 },
3104 }
3105 }
3106
3107 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3108 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3109 for s in b.stmts {
3110 self.visit_stmt(s);
3111 }
3112 self.in_final_expr = in_final_expr;
3113 if let Some(e) = b.expr {
3114 self.visit_expr(e);
3115 }
3116 }
3117
3118 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3119 if let Some(e) = l.init {
3120 self.visit_expr(e);
3121 }
3122 if let Some(else_) = l.els {
3123 let is_never = self.is_never;
3124 self.visit_block(else_);
3125 self.is_never = is_never;
3126 }
3127 }
3128
3129 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3130 if let Some(guard) = arm.guard {
3131 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3132 self.visit_expr(guard);
3133 self.in_final_expr = in_final_expr;
3134 }
3135 self.visit_expr(arm.body);
3136 }
3137 }
3138
3139 if cx.typeck_results().expr_ty(e).is_never() {
3140 Some(RequiresSemi::No)
3141 } else if let ExprKind::Block(b, _) = e.kind
3142 && !b.targeted_by_break
3143 && b.expr.is_none()
3144 {
3145 None
3147 } else {
3148 let mut v = V {
3149 cx,
3150 break_targets: Vec::new(),
3151 break_targets_for_result_ty: 0,
3152 in_final_expr: true,
3153 requires_semi: false,
3154 is_never: false,
3155 };
3156 v.visit_expr(e);
3157 v.is_never
3158 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3159 RequiresSemi::Yes
3160 } else {
3161 RequiresSemi::No
3162 })
3163 }
3164}
3165
3166pub fn get_path_from_caller_to_method_type<'tcx>(
3172 tcx: TyCtxt<'tcx>,
3173 from: LocalDefId,
3174 method: DefId,
3175 args: GenericArgsRef<'tcx>,
3176) -> String {
3177 let assoc_item = tcx.associated_item(method);
3178 let def_id = assoc_item.container_id(tcx);
3179 match assoc_item.container {
3180 rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
3181 rustc_ty::AssocItemContainer::Impl => {
3182 let ty = tcx.type_of(def_id).instantiate_identity();
3183 get_path_to_ty(tcx, from, ty, args)
3184 },
3185 }
3186}
3187
3188fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3189 match ty.kind() {
3190 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3191 rustc_ty::Array(..)
3193 | rustc_ty::Dynamic(..)
3194 | rustc_ty::Never
3195 | rustc_ty::RawPtr(_, _)
3196 | rustc_ty::Ref(..)
3197 | rustc_ty::Slice(_)
3198 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3199 _ => ty.to_string(),
3200 }
3201}
3202
3203fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3205 if callee.is_local() {
3207 let callee_path = tcx.def_path(callee);
3208 let caller_path = tcx.def_path(from.to_def_id());
3209 maybe_get_relative_path(&caller_path, &callee_path, 2)
3210 } else {
3211 tcx.def_path_str(callee)
3212 }
3213}
3214
3215fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3228 use itertools::EitherOrBoth::{Both, Left, Right};
3229
3230 let unique_parts = to
3232 .data
3233 .iter()
3234 .zip_longest(from.data.iter())
3235 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3236 .map(|el| match el {
3237 Both(l, r) => Both(l.data, r.data),
3238 Left(l) => Left(l.data),
3239 Right(r) => Right(r.data),
3240 });
3241
3242 let mut go_up_by = 0;
3244 let mut path = Vec::new();
3245 for el in unique_parts {
3246 match el {
3247 Both(l, r) => {
3248 if let DefPathData::TypeNs(sym) = l {
3258 path.push(sym);
3259 }
3260 if let DefPathData::TypeNs(_) = r {
3261 go_up_by += 1;
3262 }
3263 },
3264 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3269 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3274 _ => {},
3275 }
3276 }
3277
3278 if go_up_by > max_super {
3279 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3281 if let DefPathData::TypeNs(sym) = el.data {
3282 Some(sym)
3283 } else {
3284 None
3285 }
3286 })))
3287 } else {
3288 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3289 }
3290}
3291
3292pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3295 matches!(
3296 cx.tcx.parent_hir_node(id),
3297 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3298 )
3299}
3300
3301pub fn is_block_like(expr: &Expr<'_>) -> bool {
3304 matches!(
3305 expr.kind,
3306 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3307 )
3308}
3309
3310pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3312 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3313 match expr.kind {
3314 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3315 _ if is_block_like(expr) => is_operand,
3316 _ => false,
3317 }
3318 }
3319
3320 contains_block(expr, false)
3321}
3322
3323pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3325 if let Some(parent_expr) = get_parent_expr(cx, expr)
3326 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3327 && receiver.hir_id == expr.hir_id
3328 {
3329 return true;
3330 }
3331 false
3332}
3333
3334pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3337 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3338 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3339 && temporary_ty
3340 .walk()
3341 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3342 {
3343 ControlFlow::Break(())
3344 } else {
3345 ControlFlow::Continue(())
3346 }
3347 })
3348 .is_break()
3349}
3350
3351pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3362 let expr_ty_is_adjusted = cx
3363 .typeck_results()
3364 .expr_adjustments(expr)
3365 .iter()
3366 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3368 if expr_ty_is_adjusted {
3369 return true;
3370 }
3371
3372 match expr.kind {
3375 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3376 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3377
3378 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3379 return false;
3380 }
3381
3382 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3383 let mut args_with_ty_param = {
3384 fn_sig
3385 .inputs()
3386 .skip_binder()
3387 .iter()
3388 .skip(self_arg_count)
3389 .zip(args)
3390 .filter_map(|(arg_ty, arg)| {
3391 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3392 Some(arg)
3393 } else {
3394 None
3395 }
3396 })
3397 };
3398 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3399 },
3400 ExprKind::Struct(qpath, _, _) => {
3402 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3403 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3404 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3405 return true;
3407 };
3408 v_def
3409 .fields
3410 .iter()
3411 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3412 } else {
3413 false
3414 }
3415 },
3416 ExprKind::Block(
3418 &Block {
3419 expr: Some(ret_expr), ..
3420 },
3421 _,
3422 )
3423 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3424
3425 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3427 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3429 ExprKind::If(_, then, maybe_else) => {
3431 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3432 },
3433 ExprKind::Match(_, arms, _) => arms
3434 .iter()
3435 .map(|arm| arm.body)
3436 .any(|body| expr_requires_coercion(cx, body)),
3437 _ => false,
3438 }
3439}
3440
3441pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3444 if let Some(hir_id) = path_to_local(expr)
3445 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3446 {
3447 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3448 } else if let ExprKind::Path(p) = &expr.kind
3449 && let Some(mutability) = cx
3450 .qpath_res(p, expr.hir_id)
3451 .opt_def_id()
3452 .and_then(|id| cx.tcx.static_mutability(id))
3453 {
3454 mutability == Mutability::Mut
3455 } else if let ExprKind::Field(parent, _) = expr.kind {
3456 is_mutable(cx, parent)
3457 } else {
3458 true
3459 }
3460}
3461
3462pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3465 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3466 return hir_ty;
3467 };
3468 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3469 && let Some(segment) = path.segments.last()
3470 && segment.ident.name == sym::Option
3471 && let Res::Def(DefKind::Enum, def_id) = segment.res
3472 && def_id == option_def_id
3473 && let [GenericArg::Type(arg_ty)] = segment.args().args
3474 {
3475 hir_ty = arg_ty.as_unambig_ty();
3476 }
3477 hir_ty
3478}
3479
3480pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3483 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3484 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3485 && let ctxt = expr.span.ctxt()
3486 && for_each_expr_without_closures(into_future_arg, |e| {
3487 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3488 })
3489 .is_none()
3490 {
3491 Some(into_future_arg)
3492 } else {
3493 None
3494 }
3495}
3496
3497pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3499 if let ExprKind::Call(fn_expr, []) = &expr.kind
3500 && let ExprKind::Path(qpath) = &fn_expr.kind
3501 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3502 {
3503 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3504 } else {
3505 false
3506 }
3507}
3508
3509pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3526 let enclosing_body_owner = cx
3527 .tcx
3528 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3529 let mut prev_id = expr.hir_id;
3530 let mut skip_until_id = None;
3531 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3532 if hir_id == enclosing_body_owner {
3533 return true;
3534 }
3535 if let Some(id) = skip_until_id {
3536 prev_id = hir_id;
3537 if id == hir_id {
3538 skip_until_id = None;
3539 }
3540 continue;
3541 }
3542 match node {
3543 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3544 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3545 Node::Expr(expr) => match expr.kind {
3546 ExprKind::Ret(_) => return true,
3547 ExprKind::If(_, then, opt_else)
3548 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3549 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3550 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3551 ExprKind::Break(
3552 Destination {
3553 target_id: Ok(target_id),
3554 ..
3555 },
3556 _,
3557 ) => skip_until_id = Some(target_id),
3558 _ => break,
3559 },
3560 _ => break,
3561 }
3562 prev_id = hir_id;
3563 }
3564
3565 false
3568}
3569
3570pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3573 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3574 matches!(
3575 adj.kind,
3576 Adjust::Deref(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny
3577 )
3578 })
3579}