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, zip};
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_data_structures::fx::FxHashMap;
94use rustc_data_structures::packed::Pu128;
95use rustc_data_structures::unhash::UnindexMap;
96use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
97use rustc_hir::attrs::AttributeKind;
98use rustc_hir::def::{DefKind, Res};
99use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
100use rustc_hir::definitions::{DefPath, DefPathData};
101use rustc_hir::hir_id::{HirIdMap, HirIdSet};
102use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
103use rustc_hir::{
104 self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
105 CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs,
106 HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId,
107 OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
108 TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr,
109};
110use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
111use rustc_lint::{LateContext, Level, Lint, LintContext};
112use rustc_middle::hir::nested_filter;
113use rustc_middle::hir::place::PlaceBase;
114use rustc_middle::lint::LevelAndSource;
115use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
116use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, PointerCoercion};
117use rustc_middle::ty::layout::IntegerExt;
118use rustc_middle::ty::{
119 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
120 TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
121};
122use rustc_span::hygiene::{ExpnKind, MacroKind};
123use rustc_span::source_map::SourceMap;
124use rustc_span::symbol::{Ident, Symbol, kw};
125use rustc_span::{InnerSpan, Span};
126use source::{SpanRangeExt, walk_span_to_context};
127use visitors::{Visitable, for_each_unconsumed_temporary};
128
129use crate::ast_utils::unordered_over;
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 {
314 matches!(
315 expr.kind,
316 ExprKind::Block(
317 Block {
318 stmts: [],
319 expr: None,
320 ..
321 },
322 _
323 ) | ExprKind::Tup([])
324 )
325}
326
327pub fn is_wild(pat: &Pat<'_>) -> bool {
329 matches!(pat.kind, PatKind::Wild)
330}
331
332pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
334 matches!(
335 arm.pat.kind,
336 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
337 if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone)
338 )
339}
340
341pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
343 match *qpath {
344 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
345 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
346 _ => false,
347 }
348}
349
350pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
352 if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
353 cx.tcx.trait_of_assoc(method_id).is_none()
354 } else {
355 false
356 }
357}
358
359pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
361 if let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
362 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
363 {
364 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
365 }
366 false
367}
368
369pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
371 if let Some(trait_did) = cx.tcx.trait_of_assoc(def_id) {
372 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
373 }
374 false
375}
376
377pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
379 cx.typeck_results()
380 .type_dependent_def_id(expr.hir_id)
381 .is_some_and(|did| is_diag_trait_item(cx, did, diag_item))
382}
383
384pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
386 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
387 && let ItemKind::Impl(imp) = item.kind
388 {
389 imp.of_trait.is_some()
390 } else {
391 false
392 }
393}
394
395pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
405 if let ExprKind::Path(ref qpath) = expr.kind {
406 cx.qpath_res(qpath, expr.hir_id)
407 .opt_def_id()
408 .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item))
409 } else {
410 false
411 }
412}
413
414pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
415 match *path {
416 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
417 QPath::TypeRelative(_, seg) => seg,
418 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
419 }
420}
421
422pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
423 last_path_segment(qpath)
424 .args
425 .map_or(&[][..], |a| a.args)
426 .iter()
427 .filter_map(|a| match a {
428 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
429 _ => None,
430 })
431}
432
433pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
436 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id))
437}
438
439pub fn is_path_diagnostic_item<'tcx>(
442 cx: &LateContext<'_>,
443 maybe_path: &impl MaybePath<'tcx>,
444 diag_item: Symbol,
445) -> bool {
446 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
447}
448
449pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
451 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
452 && let Res::Local(id) = path.res
453 {
454 return Some(id);
455 }
456 None
457}
458
459pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
462 path_to_local(expr) == Some(id)
463}
464
465pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
470 match expr.kind {
471 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
472 ExprKind::Path(QPath::Resolved(
473 _,
474 Path {
475 res: Res::Local(local), ..
476 },
477 )) => Some(*local),
478 _ => None,
479 }
480}
481
482pub trait MaybePath<'hir> {
483 fn hir_id(&self) -> HirId;
484 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
485}
486
487macro_rules! maybe_path {
488 ($ty:ident, $kind:ident) => {
489 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
490 fn hir_id(&self) -> HirId {
491 self.hir_id
492 }
493 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
494 match &self.kind {
495 hir::$kind::Path(qpath) => Some(qpath),
496 _ => None,
497 }
498 }
499 }
500 };
501}
502maybe_path!(Expr, ExprKind);
503impl<'hir> MaybePath<'hir> for Pat<'hir> {
504 fn hir_id(&self) -> HirId {
505 self.hir_id
506 }
507 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
508 match &self.kind {
509 PatKind::Expr(PatExpr {
510 kind: PatExprKind::Path(qpath),
511 ..
512 }) => Some(qpath),
513 _ => None,
514 }
515 }
516}
517maybe_path!(Ty, TyKind);
518
519pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
521 match maybe_path.qpath_opt() {
522 None => Res::Err,
523 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
524 }
525}
526
527pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
529 path_res(cx, maybe_path).opt_def_id()
530}
531
532pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
548 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
549 && let ItemKind::Impl(impl_) = &item.kind
550 && let Some(of_trait) = impl_.of_trait
551 {
552 return Some(&of_trait.trait_ref);
553 }
554 None
555}
556
557fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
565 let mut result = vec![];
566 let root = loop {
567 match e.kind {
568 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
569 result.push(e);
570 e = ep;
571 },
572 _ => break e,
573 }
574 };
575 result.reverse();
576 (result, root)
577}
578
579pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
581 cx.typeck_results()
582 .expr_adjustments(e)
583 .iter()
584 .find_map(|a| match a.kind {
585 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
586 Adjust::Deref(None) => None,
587 _ => Some(None),
588 })
589 .and_then(|x| x)
590}
591
592pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
595 let (s1, r1) = projection_stack(e1);
596 let (s2, r2) = projection_stack(e2);
597 if !eq_expr_value(cx, r1, r2) {
598 return true;
599 }
600 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
601 return false;
602 }
603
604 for (x1, x2) in zip(&s1, &s2) {
605 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
606 return false;
607 }
608
609 match (&x1.kind, &x2.kind) {
610 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
611 if i1 != i2 {
612 return true;
613 }
614 },
615 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
616 if !eq_expr_value(cx, i1, i2) {
617 return false;
618 }
619 },
620 _ => return false,
621 }
622 }
623 false
624}
625
626fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
629 let std_types_symbols = &[
630 sym::Vec,
631 sym::VecDeque,
632 sym::LinkedList,
633 sym::HashMap,
634 sym::BTreeMap,
635 sym::HashSet,
636 sym::BTreeSet,
637 sym::BinaryHeap,
638 ];
639
640 if let QPath::TypeRelative(_, method) = path
641 && method.ident.name == sym::new
642 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
643 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
644 {
645 return Some(adt.did()) == cx.tcx.lang_items().string()
646 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
647 }
648 false
649}
650
651pub fn is_default_equivalent_call(
653 cx: &LateContext<'_>,
654 repl_func: &Expr<'_>,
655 whole_call_expr: Option<&Expr<'_>>,
656) -> bool {
657 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
658 && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
659 && (is_diag_trait_item(cx, repl_def_id, sym::Default)
660 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath))
661 {
662 return true;
663 }
664
665 let Some(e) = whole_call_expr else { return false };
668 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
669 return false;
670 };
671 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
672 return false;
673 };
674 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
675 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
676 cx.tcx.lifetimes.re_erased.into()
677 } else if param.index == 0 && param.name == kw::SelfUpper {
678 ty.into()
679 } else {
680 param.to_error(cx.tcx)
681 }
682 });
683 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
684
685 let Ok(Some(instance)) = instance else { return false };
686 if let rustc_ty::InstanceKind::Item(def) = instance.def
687 && !cx.tcx.is_mir_available(def)
688 {
689 return false;
690 }
691 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
692 return false;
693 };
694 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
695 return false;
696 };
697
698 let body = cx.tcx.instance_mir(instance.def);
704 for block_data in body.basic_blocks.iter() {
705 if block_data.statements.len() == 1
706 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
707 && assign.0.local == RETURN_PLACE
708 && let Rvalue::Aggregate(kind, _places) = &assign.1
709 && let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
710 && let def = cx.tcx.adt_def(did)
711 && let variant = &def.variant(*variant_index)
712 && variant.fields.is_empty()
713 && let Some((_, did)) = variant.ctor
714 && did == repl_def_id
715 {
716 return true;
717 } else if block_data.statements.is_empty()
718 && let Some(term) = &block_data.terminator
719 {
720 match &term.kind {
721 TerminatorKind::Call {
722 func: Operand::Constant(c),
723 ..
724 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
725 && *did == repl_def_id =>
726 {
727 return true;
728 },
729 TerminatorKind::TailCall {
730 func: Operand::Constant(c),
731 ..
732 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
733 && *did == repl_def_id =>
734 {
735 return true;
736 },
737 _ => {},
738 }
739 }
740 }
741 false
742}
743
744pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
748 match &e.kind {
749 ExprKind::Lit(lit) => match lit.node {
750 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
751 LitKind::Str(s, _) => s.is_empty(),
752 _ => false,
753 },
754 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
755 ExprKind::Repeat(x, len) => {
756 if let ConstArgKind::Anon(anon_const) = len.kind
757 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
758 && let LitKind::Int(v, _) = const_lit.node
759 && v <= 32
760 && is_default_equivalent(cx, x)
761 {
762 true
763 } else {
764 false
765 }
766 },
767 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
768 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
769 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
770 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
771 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
772 _ => false,
773 }
774}
775
776fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
777 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
778 && seg.ident.name == sym::from
779 {
780 match arg.kind {
781 ExprKind::Lit(hir::Lit {
782 node: LitKind::Str(sym, _),
783 ..
784 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
785 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
786 ExprKind::Repeat(_, len) => {
787 if let ConstArgKind::Anon(anon_const) = len.kind
788 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
789 && let LitKind::Int(v, _) = const_lit.node
790 {
791 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
792 }
793 },
794 _ => (),
795 }
796 }
797 false
798}
799
800pub fn can_move_expr_to_closure_no_visit<'tcx>(
832 cx: &LateContext<'tcx>,
833 expr: &'tcx Expr<'_>,
834 loop_ids: &[HirId],
835 ignore_locals: &HirIdSet,
836) -> bool {
837 match expr.kind {
838 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
839 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
840 if loop_ids.contains(&id) =>
841 {
842 true
843 },
844 ExprKind::Break(..)
845 | ExprKind::Continue(_)
846 | ExprKind::Ret(_)
847 | ExprKind::Yield(..)
848 | ExprKind::InlineAsm(_) => false,
849 ExprKind::Field(
852 &Expr {
853 hir_id,
854 kind:
855 ExprKind::Path(QPath::Resolved(
856 _,
857 Path {
858 res: Res::Local(local_id),
859 ..
860 },
861 )),
862 ..
863 },
864 _,
865 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
866 false
868 },
869 _ => true,
870 }
871}
872
873#[derive(Debug, Clone, Copy, PartialEq, Eq)]
875pub enum CaptureKind {
876 Value,
877 Use,
878 Ref(Mutability),
879}
880impl CaptureKind {
881 pub fn is_imm_ref(self) -> bool {
882 self == Self::Ref(Mutability::Not)
883 }
884}
885impl std::ops::BitOr for CaptureKind {
886 type Output = Self;
887 fn bitor(self, rhs: Self) -> Self::Output {
888 match (self, rhs) {
889 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
890 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
891 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
892 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
893 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
894 }
895 }
896}
897impl std::ops::BitOrAssign for CaptureKind {
898 fn bitor_assign(&mut self, rhs: Self) {
899 *self = *self | rhs;
900 }
901}
902
903pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
909 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
910 let mut capture = CaptureKind::Ref(Mutability::Not);
911 pat.each_binding_or_first(&mut |_, id, span, _| match cx
912 .typeck_results()
913 .extract_binding_mode(cx.sess(), id, span)
914 .0
915 {
916 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
917 capture = CaptureKind::Value;
918 },
919 ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
920 capture = CaptureKind::Ref(Mutability::Mut);
921 },
922 _ => (),
923 });
924 capture
925 }
926
927 debug_assert!(matches!(
928 e.kind,
929 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
930 ));
931
932 let mut child_id = e.hir_id;
933 let mut capture = CaptureKind::Value;
934 let mut capture_expr_ty = e;
935
936 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
937 if let [
938 Adjustment {
939 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
940 target,
941 },
942 ref adjust @ ..,
943 ] = *cx
944 .typeck_results()
945 .adjustments()
946 .get(child_id)
947 .map_or(&[][..], |x| &**x)
948 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
949 *adjust.last().map_or(target, |a| a.target).kind()
950 {
951 return CaptureKind::Ref(mutability);
952 }
953
954 match parent {
955 Node::Expr(e) => match e.kind {
956 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
957 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
958 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
959 return CaptureKind::Ref(Mutability::Mut);
960 },
961 ExprKind::Field(..) => {
962 if capture == CaptureKind::Value {
963 capture_expr_ty = e;
964 }
965 },
966 ExprKind::Let(let_expr) => {
967 let mutability = match pat_capture_kind(cx, let_expr.pat) {
968 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
969 CaptureKind::Ref(m) => m,
970 };
971 return CaptureKind::Ref(mutability);
972 },
973 ExprKind::Match(_, arms, _) => {
974 let mut mutability = Mutability::Not;
975 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
976 match capture {
977 CaptureKind::Value | CaptureKind::Use => break,
978 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
979 CaptureKind::Ref(Mutability::Not) => (),
980 }
981 }
982 return CaptureKind::Ref(mutability);
983 },
984 _ => break,
985 },
986 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
987 CaptureKind::Value | CaptureKind::Use => break,
988 capture @ CaptureKind::Ref(_) => return capture,
989 },
990 _ => break,
991 }
992
993 child_id = parent_id;
994 }
995
996 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
997 CaptureKind::Ref(Mutability::Not)
999 } else {
1000 capture
1001 }
1002}
1003
1004pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
1007 struct V<'cx, 'tcx> {
1008 cx: &'cx LateContext<'tcx>,
1009 loops: Vec<HirId>,
1011 locals: HirIdSet,
1013 allow_closure: bool,
1015 captures: HirIdMap<CaptureKind>,
1018 }
1019 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1020 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1021 if !self.allow_closure {
1022 return;
1023 }
1024
1025 match e.kind {
1026 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1027 if !self.locals.contains(&l) {
1028 let cap = capture_local_usage(self.cx, e);
1029 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1030 }
1031 },
1032 ExprKind::Closure(closure) => {
1033 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
1034 let local_id = match capture.place.base {
1035 PlaceBase::Local(id) => id,
1036 PlaceBase::Upvar(var) => var.var_path.hir_id,
1037 _ => continue,
1038 };
1039 if !self.locals.contains(&local_id) {
1040 let capture = match capture.info.capture_kind {
1041 UpvarCapture::ByValue => CaptureKind::Value,
1042 UpvarCapture::ByUse => CaptureKind::Use,
1043 UpvarCapture::ByRef(kind) => match kind {
1044 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
1045 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
1046 CaptureKind::Ref(Mutability::Mut)
1047 },
1048 },
1049 };
1050 self.captures
1051 .entry(local_id)
1052 .and_modify(|e| *e |= capture)
1053 .or_insert(capture);
1054 }
1055 }
1056 },
1057 ExprKind::Loop(b, ..) => {
1058 self.loops.push(e.hir_id);
1059 self.visit_block(b);
1060 self.loops.pop();
1061 },
1062 _ => {
1063 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1064 walk_expr(self, e);
1065 },
1066 }
1067 }
1068
1069 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1070 p.each_binding_or_first(&mut |_, id, _, _| {
1071 self.locals.insert(id);
1072 });
1073 }
1074 }
1075
1076 let mut v = V {
1077 cx,
1078 loops: Vec::new(),
1079 locals: HirIdSet::default(),
1080 allow_closure: true,
1081 captures: HirIdMap::default(),
1082 };
1083 v.visit_expr(expr);
1084 v.allow_closure.then_some(v.captures)
1085}
1086
1087pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1089
1090pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1093 let mut method_names = Vec::with_capacity(max_depth);
1094 let mut arg_lists = Vec::with_capacity(max_depth);
1095 let mut spans = Vec::with_capacity(max_depth);
1096
1097 let mut current = expr;
1098 for _ in 0..max_depth {
1099 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1100 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1101 break;
1102 }
1103 method_names.push(path.ident.name);
1104 arg_lists.push((*receiver, &**args));
1105 spans.push(path.ident.span);
1106 current = receiver;
1107 } else {
1108 break;
1109 }
1110 }
1111
1112 (method_names, arg_lists, spans)
1113}
1114
1115pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1122 let mut current = expr;
1123 let mut matched = Vec::with_capacity(methods.len());
1124 for method_name in methods.iter().rev() {
1125 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1127 if path.ident.name == *method_name {
1128 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1129 return None;
1130 }
1131 matched.push((receiver, args)); current = receiver; } else {
1134 return None;
1135 }
1136 } else {
1137 return None;
1138 }
1139 }
1140 matched.reverse();
1142 Some(matched)
1143}
1144
1145pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1147 cx.tcx
1148 .entry_fn(())
1149 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1150}
1151
1152pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1154 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1155 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1156}
1157
1158pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1160 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1161 match cx.tcx.hir_node_by_def_id(parent_id) {
1162 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1163 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1164 _ => None,
1165 }
1166}
1167
1168pub struct ContainsName<'a, 'tcx> {
1169 pub cx: &'a LateContext<'tcx>,
1170 pub name: Symbol,
1171}
1172
1173impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1174 type Result = ControlFlow<()>;
1175 type NestedFilter = nested_filter::OnlyBodies;
1176
1177 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1178 if self.name == name {
1179 ControlFlow::Break(())
1180 } else {
1181 ControlFlow::Continue(())
1182 }
1183 }
1184
1185 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1186 self.cx.tcx
1187 }
1188}
1189
1190pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1192 let mut cn = ContainsName { cx, name };
1193 cn.visit_expr(expr).is_break()
1194}
1195
1196pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1198 for_each_expr_without_closures(expr, |e| {
1199 if matches!(e.kind, ExprKind::Ret(..)) {
1200 ControlFlow::Break(())
1201 } else {
1202 ControlFlow::Continue(())
1203 }
1204 })
1205 .is_some()
1206}
1207
1208pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1210 get_parent_expr_for_hir(cx, e.hir_id)
1211}
1212
1213pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1216 match cx.tcx.parent_hir_node(hir_id) {
1217 Node::Expr(parent) => Some(parent),
1218 _ => None,
1219 }
1220}
1221
1222pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1224 let enclosing_node = cx
1225 .tcx
1226 .hir_get_enclosing_scope(hir_id)
1227 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1228 enclosing_node.and_then(|node| match node {
1229 Node::Block(block) => Some(block),
1230 Node::Item(&Item {
1231 kind: ItemKind::Fn { body: eid, .. },
1232 ..
1233 })
1234 | Node::ImplItem(&ImplItem {
1235 kind: ImplItemKind::Fn(_, eid),
1236 ..
1237 })
1238 | Node::TraitItem(&TraitItem {
1239 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1240 ..
1241 }) => match cx.tcx.hir_body(eid).value.kind {
1242 ExprKind::Block(block, _) => Some(block),
1243 _ => None,
1244 },
1245 _ => None,
1246 })
1247}
1248
1249pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1251 cx: &LateContext<'tcx>,
1252 expr: &Expr<'_>,
1253) -> Option<&'tcx Expr<'tcx>> {
1254 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1255 match node {
1256 Node::Expr(e) => match e.kind {
1257 ExprKind::Closure { .. }
1258 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1259 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1260
1261 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1263 _ => (),
1264 },
1265 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1266 _ => break,
1267 }
1268 }
1269 None
1270}
1271
1272pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1274 match tcx.hir_parent_iter(id).next() {
1275 Some((
1276 _,
1277 Node::Item(Item {
1278 kind: ItemKind::Impl(imp),
1279 ..
1280 }),
1281 )) => Some(imp),
1282 _ => None,
1283 }
1284}
1285
1286pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1297 while let ExprKind::Block(
1298 Block {
1299 stmts: [],
1300 expr: Some(inner),
1301 rules: BlockCheckMode::DefaultBlock,
1302 ..
1303 },
1304 _,
1305 ) = expr.kind
1306 {
1307 expr = inner;
1308 }
1309 expr
1310}
1311
1312pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1323 while let ExprKind::Block(
1324 Block {
1325 stmts: [],
1326 expr: Some(inner),
1327 rules: BlockCheckMode::DefaultBlock,
1328 ..
1329 }
1330 | Block {
1331 stmts:
1332 [
1333 Stmt {
1334 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1335 ..
1336 },
1337 ],
1338 expr: None,
1339 rules: BlockCheckMode::DefaultBlock,
1340 ..
1341 },
1342 _,
1343 ) = expr.kind
1344 {
1345 expr = inner;
1346 }
1347 expr
1348}
1349
1350pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1352 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1353 match iter.next() {
1354 Some((
1355 _,
1356 Node::Expr(Expr {
1357 kind: ExprKind::If(_, _, Some(else_expr)),
1358 ..
1359 }),
1360 )) => else_expr.hir_id == expr.hir_id,
1361 _ => false,
1362 }
1363}
1364
1365pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1368 let mut child_id = expr.hir_id;
1369 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1370 if let Node::LetStmt(LetStmt {
1371 init: Some(init),
1372 els: Some(els),
1373 ..
1374 }) = node
1375 && (init.hir_id == child_id || els.hir_id == child_id)
1376 {
1377 return true;
1378 }
1379
1380 child_id = parent_id;
1381 }
1382
1383 false
1384}
1385
1386pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1388 let mut child_id = expr.hir_id;
1389 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1390 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1391 && els.hir_id == child_id
1392 {
1393 return true;
1394 }
1395
1396 child_id = parent_id;
1397 }
1398
1399 false
1400}
1401
1402pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1417 let ty = cx.typeck_results().expr_ty(expr);
1418 if let Some(Range { start, end, limits }) = Range::hir(expr) {
1419 let start_is_none_or_min = start.is_none_or(|start| {
1420 if let rustc_ty::Adt(_, subst) = ty.kind()
1421 && let bnd_ty = subst.type_at(0)
1422 && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx)
1423 && let Some(min_const) = mir_to_const(cx.tcx, min_const)
1424 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1425 {
1426 start_const == min_const
1427 } else {
1428 false
1429 }
1430 });
1431 let end_is_none_or_max = end.is_none_or(|end| match limits {
1432 RangeLimits::Closed => {
1433 if let rustc_ty::Adt(_, subst) = ty.kind()
1434 && let bnd_ty = subst.type_at(0)
1435 && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx)
1436 && let Some(max_const) = mir_to_const(cx.tcx, max_const)
1437 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1438 {
1439 end_const == max_const
1440 } else {
1441 false
1442 }
1443 },
1444 RangeLimits::HalfOpen => {
1445 if let Some(container_path) = container_path
1446 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1447 && name.ident.name == sym::len
1448 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1449 {
1450 container_path.res == path.res
1451 } else {
1452 false
1453 }
1454 },
1455 });
1456 return start_is_none_or_min && end_is_none_or_max;
1457 }
1458 false
1459}
1460
1461pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1464 if is_integer_literal(e, value) {
1465 return true;
1466 }
1467 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1468 if let Some(Constant::Int(v)) =
1469 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1470 {
1471 return value == v;
1472 }
1473 false
1474}
1475
1476pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1478 if let ExprKind::Lit(spanned) = expr.kind
1480 && let LitKind::Int(v, _) = spanned.node
1481 {
1482 return v == value;
1483 }
1484 false
1485}
1486
1487pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1489 if let ExprKind::Lit(spanned) = expr.kind
1490 && let LitKind::Float(v, _) = spanned.node
1491 {
1492 v.as_str().parse() == Ok(value)
1493 } else {
1494 false
1495 }
1496}
1497
1498pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1506 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1507}
1508
1509#[must_use]
1513pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1514 loop {
1515 if span.from_expansion() {
1516 let data = span.ctxt().outer_expn_data();
1517 let new_span = data.call_site;
1518
1519 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1520 && mac_name == name
1521 {
1522 return Some(new_span);
1523 }
1524
1525 span = new_span;
1526 } else {
1527 return None;
1528 }
1529 }
1530}
1531
1532#[must_use]
1543pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1544 if span.from_expansion() {
1545 let data = span.ctxt().outer_expn_data();
1546 let new_span = data.call_site;
1547
1548 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1549 && mac_name == name
1550 {
1551 return Some(new_span);
1552 }
1553 }
1554
1555 None
1556}
1557
1558pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1560 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1561 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1562}
1563
1564pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1566 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1567 cx.tcx.instantiate_bound_regions_with_erased(arg)
1568}
1569
1570pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1572 if let ExprKind::Call(fun, _) = expr.kind
1573 && let ExprKind::Path(ref qp) = fun.kind
1574 {
1575 let res = cx.qpath_res(qp, fun.hir_id);
1576 return match res {
1577 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1578 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1579 _ => false,
1580 };
1581 }
1582 false
1583}
1584
1585pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1588 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1589 !matches!(
1590 cx.qpath_res(qpath, id),
1591 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1592 )
1593 }
1594
1595 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1596 i.into_iter().any(|pat| is_refutable(cx, pat))
1597 }
1598
1599 match pat.kind {
1600 PatKind::Missing => unreachable!(),
1601 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1603 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1604 PatKind::Expr(PatExpr {
1605 kind: PatExprKind::Path(qpath),
1606 hir_id,
1607 ..
1608 }) => is_qpath_refutable(cx, qpath, *hir_id),
1609 PatKind::Or(pats) => {
1610 are_refutable(cx, pats)
1612 },
1613 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1614 PatKind::Struct(ref qpath, fields, _) => {
1615 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1616 },
1617 PatKind::TupleStruct(ref qpath, pats, _) => {
1618 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1619 },
1620 PatKind::Slice(head, middle, tail) => {
1621 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1622 rustc_ty::Slice(..) => {
1623 !head.is_empty() || middle.is_none() || !tail.is_empty()
1625 },
1626 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1627 _ => {
1628 true
1630 },
1631 }
1632 },
1633 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1634 }
1635}
1636
1637pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1640 if let PatKind::Or(pats) = pat.kind {
1641 pats.iter().for_each(f);
1642 } else {
1643 f(pat);
1644 }
1645}
1646
1647pub fn is_self(slf: &Param<'_>) -> bool {
1648 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1649 name.name == kw::SelfLower
1650 } else {
1651 false
1652 }
1653}
1654
1655pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1656 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1657 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1658 {
1659 return true;
1660 }
1661 false
1662}
1663
1664pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1665 (0..decl.inputs.len()).map(move |i| &body.params[i])
1666}
1667
1668pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1671 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1672 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1673 && ddpos.as_opt_usize().is_none()
1674 && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk)
1675 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1676 && path_to_local_id(arm.body, hir_id)
1677 {
1678 return true;
1679 }
1680 false
1681 }
1682
1683 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1684 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1685 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1686 } else {
1687 false
1688 }
1689 }
1690
1691 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1692 if let MatchSource::TryDesugar(_) = *source {
1694 return Some(expr);
1695 }
1696
1697 if arms.len() == 2
1698 && arms[0].guard.is_none()
1699 && arms[1].guard.is_none()
1700 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1701 {
1702 return Some(expr);
1703 }
1704 }
1705
1706 None
1707}
1708
1709pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1719 let mut suppress_lint = false;
1720
1721 for id in ids {
1722 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1723 if let Some(expectation) = lint_id {
1724 cx.fulfill_expectation(expectation);
1725 }
1726
1727 match level {
1728 Level::Allow | Level::Expect => suppress_lint = true,
1729 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1730 }
1731 }
1732
1733 suppress_lint
1734}
1735
1736pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1744 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1745}
1746
1747pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1748 while let PatKind::Ref(subpat, _) = pat.kind {
1749 pat = subpat;
1750 }
1751 pat
1752}
1753
1754pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1755 Integer::from_int_ty(&tcx, ity).size().bits()
1756}
1757
1758#[expect(clippy::cast_possible_wrap)]
1759pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1761 let amt = 128 - int_bits(tcx, ity);
1762 ((u as i128) << amt) >> amt
1763}
1764
1765#[expect(clippy::cast_sign_loss)]
1766pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1768 let amt = 128 - int_bits(tcx, ity);
1769 ((u as u128) << amt) >> amt
1770}
1771
1772pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1774 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1775 let amt = 128 - bits;
1776 (u << amt) >> amt
1777}
1778
1779pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1780 attrs.iter().any(|attr| attr.has_name(symbol))
1781}
1782
1783pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1784 find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr { .. })
1785}
1786
1787pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1788 let mut prev_enclosing_node = None;
1789 let mut enclosing_node = node;
1790 while Some(enclosing_node) != prev_enclosing_node {
1791 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1792 return true;
1793 }
1794 prev_enclosing_node = Some(enclosing_node);
1795 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1796 }
1797
1798 false
1799}
1800
1801pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1804 tcx.hir_parent_owner_iter(id)
1805 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1806 .any(|(id, _)| {
1807 find_attr!(
1808 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1809 AttributeKind::AutomaticallyDerived(..)
1810 )
1811 })
1812}
1813
1814pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1816 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1819}
1820
1821pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1826 let mut conds = Vec::new();
1827 let mut blocks: Vec<&Block<'_>> = Vec::new();
1828
1829 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1830 conds.push(cond);
1831 if let ExprKind::Block(block, _) = then.kind {
1832 blocks.push(block);
1833 } else {
1834 panic!("ExprKind::If node is not an ExprKind::Block");
1835 }
1836
1837 if let Some(else_expr) = r#else {
1838 expr = else_expr;
1839 } else {
1840 break;
1841 }
1842 }
1843
1844 if !blocks.is_empty()
1846 && let ExprKind::Block(block, _) = expr.kind
1847 {
1848 blocks.push(block);
1849 }
1850
1851 (conds, blocks)
1852}
1853
1854pub fn is_async_fn(kind: FnKind<'_>) -> bool {
1856 match kind {
1857 FnKind::ItemFn(_, _, header) => header.asyncness.is_async(),
1858 FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
1859 FnKind::Closure => false,
1860 }
1861}
1862
1863pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1865 if let ExprKind::Closure(&Closure {
1866 body,
1867 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1868 ..
1869 }) = expr.kind
1870 && let ExprKind::Block(
1871 Block {
1872 expr:
1873 Some(Expr {
1874 kind: ExprKind::DropTemps(inner_expr),
1875 ..
1876 }),
1877 ..
1878 },
1879 _,
1880 ) = tcx.hir_body(body).value.kind
1881 {
1882 Some(inner_expr)
1883 } else {
1884 None
1885 }
1886}
1887
1888pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1890 get_async_closure_expr(tcx, body.value)
1891}
1892
1893pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1895 let did = match expr.kind {
1896 ExprKind::Call(path, _) => {
1897 if let ExprKind::Path(ref qpath) = path.kind
1898 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1899 {
1900 Some(did)
1901 } else {
1902 None
1903 }
1904 },
1905 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1906 _ => None,
1907 };
1908
1909 did.is_some_and(|did| find_attr!(cx.tcx.get_all_attrs(did), AttributeKind::MustUse { .. }))
1910}
1911
1912fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1924 let [param] = func.params else {
1925 return false;
1926 };
1927
1928 let mut expr = func.value;
1929 loop {
1930 match expr.kind {
1931 ExprKind::Block(
1932 &Block {
1933 stmts: [],
1934 expr: Some(e),
1935 ..
1936 },
1937 _,
1938 )
1939 | ExprKind::Ret(Some(e)) => expr = e,
1940 ExprKind::Block(
1941 &Block {
1942 stmts: [stmt],
1943 expr: None,
1944 ..
1945 },
1946 _,
1947 ) => {
1948 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1949 && let ExprKind::Ret(Some(ret_val)) = e.kind
1950 {
1951 expr = ret_val;
1952 } else {
1953 return false;
1954 }
1955 },
1956 _ => return is_expr_identity_of_pat(cx, param.pat, expr, true),
1957 }
1958 }
1959}
1960
1961pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1971 if cx
1972 .typeck_results()
1973 .pat_binding_modes()
1974 .get(pat.hir_id)
1975 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
1976 {
1977 return false;
1981 }
1982
1983 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1985
1986 match (pat.kind, expr.kind) {
1987 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1988 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1989 },
1990 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1991 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1992 },
1993 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1994 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1995 {
1996 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1997 },
1998 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1999 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
2000 },
2001 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
2002 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
2003 {
2004 if let ExprKind::Path(ident) = &ident.kind
2006 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
2007 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
2009 {
2010 true
2011 } else {
2012 false
2013 }
2014 },
2015 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
2016 if field_pats.len() == fields.len() =>
2017 {
2018 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
2020 && unordered_over(field_pats, fields, |field_pat, field| {
2022 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
2023 })
2024 },
2025 _ => false,
2026 }
2027}
2028
2029pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2034 match expr.kind {
2035 ExprKind::Closure(&Closure { body, fn_decl, .. })
2036 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
2037 {
2038 is_body_identity_function(cx, cx.tcx.hir_body(body))
2039 },
2040 ExprKind::Path(QPath::Resolved(_, path))
2041 if path.segments.iter().all(|seg| seg.infer_args)
2042 && let Some(did) = path.res.opt_def_id() =>
2043 {
2044 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
2045 },
2046 _ => false,
2047 }
2048}
2049
2050pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2059 match expr.kind {
2060 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
2061 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
2062 }
2063}
2064
2065pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2068 let mut child_id = expr.hir_id;
2069 let mut iter = tcx.hir_parent_iter(child_id);
2070 loop {
2071 match iter.next() {
2072 None => break None,
2073 Some((id, Node::Block(_))) => child_id = id,
2074 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2075 Some((_, Node::Expr(expr))) => match expr.kind {
2076 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2077 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2078 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2079 _ => break Some((Node::Expr(expr), child_id)),
2080 },
2081 Some((_, node)) => break Some((node, child_id)),
2082 }
2083 }
2084}
2085
2086pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2088 !matches!(
2089 get_expr_use_or_unification_node(tcx, expr),
2090 None | Some((
2091 Node::Stmt(Stmt {
2092 kind: StmtKind::Expr(_)
2093 | StmtKind::Semi(_)
2094 | StmtKind::Let(LetStmt {
2095 pat: Pat {
2096 kind: PatKind::Wild,
2097 ..
2098 },
2099 ..
2100 }),
2101 ..
2102 }),
2103 _
2104 ))
2105 )
2106}
2107
2108pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2110 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2111}
2112
2113pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2117 !expr.is_place_expr(|base| {
2118 cx.typeck_results()
2119 .adjustments()
2120 .get(base.hir_id)
2121 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2122 })
2123}
2124
2125pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2126 if !is_no_std_crate(cx) {
2127 Some("std")
2128 } else if !is_no_core_crate(cx) {
2129 Some("core")
2130 } else {
2131 None
2132 }
2133}
2134
2135pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2136 find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoStd(..))
2137}
2138
2139pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2140 find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoCore(..))
2141}
2142
2143pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2153 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2154 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2155 } else {
2156 false
2157 }
2158}
2159
2160pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2170 use rustc_trait_selection::traits;
2171 let predicates = cx
2172 .tcx
2173 .predicates_of(did)
2174 .predicates
2175 .iter()
2176 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2177 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2178}
2179
2180pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2182 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2183}
2184
2185pub fn fn_def_id_with_node_args<'tcx>(
2188 cx: &LateContext<'tcx>,
2189 expr: &Expr<'_>,
2190) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2191 let typeck = cx.typeck_results();
2192 match &expr.kind {
2193 ExprKind::MethodCall(..) => Some((
2194 typeck.type_dependent_def_id(expr.hir_id)?,
2195 typeck.node_args(expr.hir_id),
2196 )),
2197 ExprKind::Call(
2198 Expr {
2199 kind: ExprKind::Path(qpath),
2200 hir_id: path_hir_id,
2201 ..
2202 },
2203 ..,
2204 ) => {
2205 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2208 typeck.qpath_res(qpath, *path_hir_id)
2209 {
2210 Some((id, typeck.node_args(*path_hir_id)))
2211 } else {
2212 None
2213 }
2214 },
2215 _ => None,
2216 }
2217}
2218
2219pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2224 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2225 let expr_kind = expr_type.kind();
2226 let is_primitive = match expr_kind {
2227 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2228 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2229 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2230 is_recursively_primitive_type(*element_type)
2231 } else {
2232 unreachable!()
2233 }
2234 },
2235 _ => false,
2236 };
2237
2238 if is_primitive {
2239 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2242 rustc_ty::Slice(..) => return Some("slice".into()),
2243 rustc_ty::Array(..) => return Some("array".into()),
2244 rustc_ty::Tuple(..) => return Some("tuple".into()),
2245 _ => {
2246 let refs_peeled = expr_type.peel_refs();
2249 return Some(refs_peeled.walk().last().unwrap().to_string());
2250 },
2251 }
2252 }
2253 None
2254}
2255
2256pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2264where
2265 Hash: FnMut(&T) -> u64,
2266 Eq: FnMut(&T, &T) -> bool,
2267{
2268 match exprs {
2269 [a, b] if eq(a, b) => return vec![vec![a, b]],
2270 _ if exprs.len() <= 2 => return vec![],
2271 _ => {},
2272 }
2273
2274 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2275
2276 for expr in exprs {
2277 match buckets.entry(hash(expr)) {
2278 indexmap::map::Entry::Occupied(mut o) => {
2279 let bucket = o.get_mut();
2280 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2281 Some(group) => group.push(expr),
2282 None => bucket.push(vec![expr]),
2283 }
2284 },
2285 indexmap::map::Entry::Vacant(v) => {
2286 v.insert(vec![vec![expr]]);
2287 },
2288 }
2289 }
2290
2291 buckets
2292 .into_values()
2293 .flatten()
2294 .filter(|group| group.len() > 1)
2295 .collect()
2296}
2297
2298pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2301 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2302 if let PatKind::Ref(pat, _) = pat.kind {
2303 peel(pat, count + 1)
2304 } else {
2305 (pat, count)
2306 }
2307 }
2308 peel(pat, 0)
2309}
2310
2311pub fn peel_hir_expr_while<'tcx>(
2313 mut expr: &'tcx Expr<'tcx>,
2314 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2315) -> &'tcx Expr<'tcx> {
2316 while let Some(e) = f(expr) {
2317 expr = e;
2318 }
2319 expr
2320}
2321
2322pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2325 let mut remaining = count;
2326 let e = peel_hir_expr_while(expr, |e| match e.kind {
2327 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2328 remaining -= 1;
2329 Some(e)
2330 },
2331 _ => None,
2332 });
2333 (e, count - remaining)
2334}
2335
2336pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2339 let mut count: usize = 0;
2340 let mut curr_expr = expr;
2341 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2342 count = count.wrapping_add(1);
2343 curr_expr = local_expr;
2344 }
2345 (curr_expr, count)
2346}
2347
2348pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2351 let mut count = 0;
2352 let e = peel_hir_expr_while(expr, |e| match e.kind {
2353 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2354 count += 1;
2355 Some(e)
2356 },
2357 _ => None,
2358 });
2359 (e, count)
2360}
2361
2362pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2365 let mut count = 0;
2366 loop {
2367 match &ty.kind {
2368 TyKind::Ref(_, ref_ty) => {
2369 ty = ref_ty.ty;
2370 count += 1;
2371 },
2372 _ => break (ty, count),
2373 }
2374 }
2375}
2376
2377pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2380 let mut count = 0;
2381 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2382 ty = *dest_ty;
2383 count += 1;
2384 }
2385 (ty, count)
2386}
2387
2388pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2391 loop {
2392 match expr.kind {
2393 ExprKind::AddrOf(_, _, e) => expr = e,
2394 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2395 _ => break,
2396 }
2397 }
2398 expr
2399}
2400
2401pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2404 let mut operators = Vec::new();
2405 peel_hir_expr_while(expr, |expr| match expr.kind {
2406 ExprKind::AddrOf(_, _, e) => {
2407 operators.push(expr);
2408 Some(e)
2409 },
2410 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2411 operators.push(expr);
2412 Some(e)
2413 },
2414 _ => None,
2415 });
2416 operators
2417}
2418
2419pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2420 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2421 && let Res::Def(_, def_id) = path.res
2422 {
2423 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2424 }
2425 false
2426}
2427
2428static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2429
2430fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2433 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2434 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2435 let value = map.entry(module);
2436 match value {
2437 Entry::Occupied(entry) => f(entry.get()),
2438 Entry::Vacant(entry) => {
2439 let mut names = Vec::new();
2440 for id in tcx.hir_module_free_items(module) {
2441 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2442 && let item = tcx.hir_item(id)
2443 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2444 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2445 && let Res::Def(DefKind::Struct, _) = path.res
2447 {
2448 let has_test_marker = tcx
2449 .hir_attrs(item.hir_id())
2450 .iter()
2451 .any(|a| a.has_name(sym::rustc_test_marker));
2452 if has_test_marker {
2453 names.push(ident.name);
2454 }
2455 }
2456 }
2457 names.sort_unstable();
2458 f(entry.insert(names))
2459 },
2460 }
2461}
2462
2463pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2467 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2468 let node = tcx.hir_node(id);
2469 once((id, node))
2470 .chain(tcx.hir_parent_iter(id))
2471 .any(|(_id, node)| {
2474 if let Node::Item(item) = node
2475 && let ItemKind::Fn { ident, .. } = item.kind
2476 {
2477 return names.binary_search(&ident.name).is_ok();
2480 }
2481 false
2482 })
2483 })
2484}
2485
2486pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2493 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2494 if let Node::Item(item) = tcx.hir_node(id)
2495 && let ItemKind::Fn { ident, .. } = item.kind
2496 {
2497 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2498 names.binary_search(&ident.name).is_ok()
2499 })
2500 } else {
2501 false
2502 }
2503}
2504
2505pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2510 tcx.hir_attrs(id).iter().any(|attr| {
2511 if attr.has_name(sym::cfg_trace)
2512 && let Some(items) = attr.meta_item_list()
2513 && let [item] = &*items
2514 && item.has_name(sym::test)
2515 {
2516 true
2517 } else {
2518 false
2519 }
2520 })
2521}
2522
2523pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2525 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2526}
2527
2528pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2530 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2531}
2532
2533pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2535 tcx.has_attr(def_id, sym::cfg_trace)
2536 || tcx
2537 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2538 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2539 .any(|attr| attr.has_name(sym::cfg_trace))
2540}
2541
2542pub fn walk_to_expr_usage<'tcx, T>(
2553 cx: &LateContext<'tcx>,
2554 e: &Expr<'tcx>,
2555 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2556) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2557 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2558 let mut child_id = e.hir_id;
2559
2560 while let Some((parent_id, parent)) = iter.next() {
2561 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2562 return Some(ControlFlow::Break(x));
2563 }
2564 let parent_expr = match parent {
2565 Node::Expr(e) => e,
2566 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2567 child_id = parent_id;
2568 continue;
2569 },
2570 Node::Arm(a) if a.body.hir_id == child_id => {
2571 child_id = parent_id;
2572 continue;
2573 },
2574 _ => return Some(ControlFlow::Continue((parent, child_id))),
2575 };
2576 match parent_expr.kind {
2577 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2578 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2579 child_id = id;
2580 iter = cx.tcx.hir_parent_iter(id);
2581 },
2582 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2583 _ => return Some(ControlFlow::Continue((parent, child_id))),
2584 }
2585 }
2586 debug_assert!(false, "no parent node found for `{child_id:?}`");
2587 None
2588}
2589
2590#[derive(Clone, Copy)]
2592pub enum DefinedTy<'tcx> {
2593 Hir(&'tcx hir::Ty<'tcx>),
2595 Mir {
2603 def_site_def_id: Option<DefId>,
2604 ty: Binder<'tcx, Ty<'tcx>>,
2605 },
2606}
2607
2608pub struct ExprUseCtxt<'tcx> {
2610 pub node: Node<'tcx>,
2612 pub child_id: HirId,
2614 pub adjustments: &'tcx [Adjustment<'tcx>],
2616 pub is_ty_unified: bool,
2618 pub moved_before_use: bool,
2620 pub same_ctxt: bool,
2622}
2623impl<'tcx> ExprUseCtxt<'tcx> {
2624 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2625 match self.node {
2626 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2627 Node::ExprField(field) => ExprUseNode::Field(field),
2628
2629 Node::Item(&Item {
2630 kind: ItemKind::Static(..) | ItemKind::Const(..),
2631 owner_id,
2632 ..
2633 })
2634 | Node::TraitItem(&TraitItem {
2635 kind: TraitItemKind::Const(..),
2636 owner_id,
2637 ..
2638 })
2639 | Node::ImplItem(&ImplItem {
2640 kind: ImplItemKind::Const(..),
2641 owner_id,
2642 ..
2643 }) => ExprUseNode::ConstStatic(owner_id),
2644
2645 Node::Item(&Item {
2646 kind: ItemKind::Fn { .. },
2647 owner_id,
2648 ..
2649 })
2650 | Node::TraitItem(&TraitItem {
2651 kind: TraitItemKind::Fn(..),
2652 owner_id,
2653 ..
2654 })
2655 | Node::ImplItem(&ImplItem {
2656 kind: ImplItemKind::Fn(..),
2657 owner_id,
2658 ..
2659 }) => ExprUseNode::Return(owner_id),
2660
2661 Node::Expr(use_expr) => match use_expr.kind {
2662 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2663 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2664 }),
2665
2666 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2667 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2668 Some(i) => ExprUseNode::FnArg(func, i),
2669 None => ExprUseNode::Callee,
2670 },
2671 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2672 use_expr.hir_id,
2673 name.args,
2674 args.iter()
2675 .position(|arg| arg.hir_id == self.child_id)
2676 .map_or(0, |i| i + 1),
2677 ),
2678 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2679 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2680 _ => ExprUseNode::Other,
2681 },
2682 _ => ExprUseNode::Other,
2683 }
2684 }
2685}
2686
2687pub enum ExprUseNode<'tcx> {
2689 LetStmt(&'tcx LetStmt<'tcx>),
2691 ConstStatic(OwnerId),
2693 Return(OwnerId),
2695 Field(&'tcx ExprField<'tcx>),
2697 FnArg(&'tcx Expr<'tcx>, usize),
2699 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2701 Callee,
2703 FieldAccess(Ident),
2705 AddrOf(ast::BorrowKind, Mutability),
2707 Other,
2708}
2709impl<'tcx> ExprUseNode<'tcx> {
2710 pub fn is_return(&self) -> bool {
2712 matches!(self, Self::Return(_))
2713 }
2714
2715 pub fn is_recv(&self) -> bool {
2717 matches!(self, Self::MethodArg(_, _, 0))
2718 }
2719
2720 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2722 match *self {
2723 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2724 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2725 def_site_def_id: Some(id.def_id.to_def_id()),
2726 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2727 }),
2728 Self::Return(id) => {
2729 if let Node::Expr(Expr {
2730 kind: ExprKind::Closure(c),
2731 ..
2732 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2733 {
2734 match c.fn_decl.output {
2735 FnRetTy::DefaultReturn(_) => None,
2736 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2737 }
2738 } else {
2739 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2740 Some(DefinedTy::Mir {
2741 def_site_def_id: Some(id.def_id.to_def_id()),
2742 ty,
2743 })
2744 }
2745 },
2746 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2747 Some(Expr {
2748 hir_id,
2749 kind: ExprKind::Struct(path, ..),
2750 ..
2751 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2752 .and_then(|(adt, variant)| {
2753 variant
2754 .fields
2755 .iter()
2756 .find(|f| f.name == field.ident.name)
2757 .map(|f| (adt, f))
2758 })
2759 .map(|(adt, field_def)| DefinedTy::Mir {
2760 def_site_def_id: Some(adt.did()),
2761 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2762 }),
2763 _ => None,
2764 },
2765 Self::FnArg(callee, i) => {
2766 let sig = expr_sig(cx, callee)?;
2767 let (hir_ty, ty) = sig.input_with_hir(i)?;
2768 Some(match hir_ty {
2769 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2770 None => DefinedTy::Mir {
2771 def_site_def_id: sig.predicates_id(),
2772 ty,
2773 },
2774 })
2775 },
2776 Self::MethodArg(id, _, i) => {
2777 let id = cx.typeck_results().type_dependent_def_id(id)?;
2778 let sig = cx.tcx.fn_sig(id).skip_binder();
2779 Some(DefinedTy::Mir {
2780 def_site_def_id: Some(id),
2781 ty: sig.input(i),
2782 })
2783 },
2784 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2785 }
2786 }
2787}
2788
2789pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2791 let mut adjustments = [].as_slice();
2792 let mut is_ty_unified = false;
2793 let mut moved_before_use = false;
2794 let mut same_ctxt = true;
2795 let ctxt = e.span.ctxt();
2796 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2797 if adjustments.is_empty()
2798 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2799 {
2800 adjustments = cx.typeck_results().expr_adjustments(e);
2801 }
2802 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2803 if let Node::Expr(e) = parent {
2804 match e.kind {
2805 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2806 is_ty_unified = true;
2807 moved_before_use = true;
2808 },
2809 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2810 is_ty_unified = true;
2811 moved_before_use = true;
2812 },
2813 ExprKind::Block(..) => moved_before_use = true,
2814 _ => {},
2815 }
2816 }
2817 ControlFlow::Continue(())
2818 });
2819 match node {
2820 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2821 node,
2822 child_id,
2823 adjustments,
2824 is_ty_unified,
2825 moved_before_use,
2826 same_ctxt,
2827 },
2828 #[allow(unreachable_patterns)]
2829 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
2830 None => ExprUseCtxt {
2831 node: Node::Crate(cx.tcx.hir_root_module()),
2832 child_id: HirId::INVALID,
2833 adjustments: &[],
2834 is_ty_unified: true,
2835 moved_before_use: true,
2836 same_ctxt: false,
2837 },
2838 }
2839}
2840
2841pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2843 let mut pos = 0;
2844 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2845 let end = pos + t.len;
2846 let range = pos as usize..end as usize;
2847 let inner = InnerSpan::new(range.start, range.end);
2848 pos = end;
2849 (t.kind, s.get(range).unwrap_or_default(), inner)
2850 })
2851}
2852
2853pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2856 let Ok(snippet) = sm.span_to_snippet(span) else {
2857 return false;
2858 };
2859 return tokenize(&snippet, FrontmatterAllowed::No).any(|token| {
2860 matches!(
2861 token.kind,
2862 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2863 )
2864 });
2865}
2866
2867pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2872 matches!(span.get_source_text(cx), Some(snippet) if tokenize_with_text(&snippet).any(|(token, _, _)|
2873 match token {
2874 TokenKind::Whitespace => false,
2875 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2876 _ => true,
2877 }
2878 ))
2879}
2880pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2884 span_extract_comments(sm, span).join("\n")
2885}
2886
2887pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2891 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2892 tokenize_with_text(&snippet)
2893 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2894 .map(|(_, s, _)| s.to_string())
2895 .collect::<Vec<_>>()
2896}
2897
2898pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2899 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2900}
2901
2902pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2927 cx: &LateContext<'_>,
2928 pat: &'a Pat<'hir>,
2929 else_body: &Expr<'_>,
2930) -> Option<&'a Pat<'hir>> {
2931 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
2932 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
2933 && !is_refutable(cx, inner_pat)
2934 && let else_body = peel_blocks(else_body)
2935 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2936 && let ExprKind::Path(ret_path) = ret_val.kind
2937 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
2938 {
2939 Some(inner_pat)
2940 } else {
2941 None
2942 }
2943}
2944
2945macro_rules! op_utils {
2946 ($($name:ident $assign:ident)*) => {
2947 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2949
2950 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2952
2953 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2955 match kind {
2956 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2957 _ => None,
2958 }
2959 }
2960 };
2961}
2962
2963op_utils! {
2964 Add AddAssign
2965 Sub SubAssign
2966 Mul MulAssign
2967 Div DivAssign
2968 Rem RemAssign
2969 BitXor BitXorAssign
2970 BitAnd BitAndAssign
2971 BitOr BitOrAssign
2972 Shl ShlAssign
2973 Shr ShrAssign
2974}
2975
2976pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2979 match *pat {
2980 PatKind::Wild => true,
2981 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2982 !visitors::is_local_used(cx, body, id)
2983 },
2984 _ => false,
2985 }
2986}
2987
2988#[derive(Clone, Copy)]
2989pub enum RequiresSemi {
2990 Yes,
2991 No,
2992}
2993impl RequiresSemi {
2994 pub fn requires_semi(self) -> bool {
2995 matches!(self, Self::Yes)
2996 }
2997}
2998
2999#[expect(clippy::too_many_lines)]
3002pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
3003 struct BreakTarget {
3004 id: HirId,
3005 unused: bool,
3006 }
3007
3008 struct V<'cx, 'tcx> {
3009 cx: &'cx LateContext<'tcx>,
3010 break_targets: Vec<BreakTarget>,
3011 break_targets_for_result_ty: u32,
3012 in_final_expr: bool,
3013 requires_semi: bool,
3014 is_never: bool,
3015 }
3016
3017 impl V<'_, '_> {
3018 fn push_break_target(&mut self, id: HirId) {
3019 self.break_targets.push(BreakTarget { id, unused: true });
3020 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3021 }
3022 }
3023
3024 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3025 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3026 if self.is_never && self.break_targets.is_empty() {
3043 if self.in_final_expr && !self.requires_semi {
3044 match e.kind {
3047 ExprKind::DropTemps(e) => self.visit_expr(e),
3048 ExprKind::If(_, then, Some(else_)) => {
3049 self.visit_expr(then);
3050 self.visit_expr(else_);
3051 },
3052 ExprKind::Match(_, arms, _) => {
3053 for arm in arms {
3054 self.visit_expr(arm.body);
3055 }
3056 },
3057 ExprKind::Loop(b, ..) => {
3058 self.push_break_target(e.hir_id);
3059 self.in_final_expr = false;
3060 self.visit_block(b);
3061 self.break_targets.pop();
3062 },
3063 ExprKind::Block(b, _) => {
3064 if b.targeted_by_break {
3065 self.push_break_target(b.hir_id);
3066 self.visit_block(b);
3067 self.break_targets.pop();
3068 } else {
3069 self.visit_block(b);
3070 }
3071 },
3072 _ => {
3073 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3074 },
3075 }
3076 }
3077 return;
3078 }
3079 match e.kind {
3080 ExprKind::DropTemps(e) => self.visit_expr(e),
3081 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3082 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3083 self.in_final_expr = false;
3084 self.visit_expr(e);
3085 self.is_never = true;
3086 },
3087 ExprKind::Break(dest, e) => {
3088 if let Some(e) = e {
3089 self.in_final_expr = false;
3090 self.visit_expr(e);
3091 }
3092 if let Ok(id) = dest.target_id
3093 && let Some((i, target)) = self
3094 .break_targets
3095 .iter_mut()
3096 .enumerate()
3097 .find(|(_, target)| target.id == id)
3098 {
3099 target.unused &= self.is_never;
3100 if i < self.break_targets_for_result_ty as usize {
3101 self.requires_semi = true;
3102 }
3103 }
3104 self.is_never = true;
3105 },
3106 ExprKind::If(cond, then, else_) => {
3107 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3108 self.visit_expr(cond);
3109 self.in_final_expr = in_final_expr;
3110
3111 if self.is_never {
3112 self.visit_expr(then);
3113 if let Some(else_) = else_ {
3114 self.visit_expr(else_);
3115 }
3116 } else {
3117 self.visit_expr(then);
3118 let is_never = mem::replace(&mut self.is_never, false);
3119 if let Some(else_) = else_ {
3120 self.visit_expr(else_);
3121 self.is_never &= is_never;
3122 }
3123 }
3124 },
3125 ExprKind::Match(scrutinee, arms, _) => {
3126 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3127 self.visit_expr(scrutinee);
3128 self.in_final_expr = in_final_expr;
3129
3130 if self.is_never {
3131 for arm in arms {
3132 self.visit_arm(arm);
3133 }
3134 } else {
3135 let mut is_never = true;
3136 for arm in arms {
3137 self.is_never = false;
3138 if let Some(guard) = arm.guard {
3139 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3140 self.visit_expr(guard);
3141 self.in_final_expr = in_final_expr;
3142 self.is_never = false;
3144 }
3145 self.visit_expr(arm.body);
3146 is_never &= self.is_never;
3147 }
3148 self.is_never = is_never;
3149 }
3150 },
3151 ExprKind::Loop(b, _, _, _) => {
3152 self.push_break_target(e.hir_id);
3153 self.in_final_expr = false;
3154 self.visit_block(b);
3155 self.is_never = self.break_targets.pop().unwrap().unused;
3156 },
3157 ExprKind::Block(b, _) => {
3158 if b.targeted_by_break {
3159 self.push_break_target(b.hir_id);
3160 self.visit_block(b);
3161 self.is_never &= self.break_targets.pop().unwrap().unused;
3162 } else {
3163 self.visit_block(b);
3164 }
3165 },
3166 _ => {
3167 self.in_final_expr = false;
3168 walk_expr(self, e);
3169 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3170 },
3171 }
3172 }
3173
3174 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3175 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3176 for s in b.stmts {
3177 self.visit_stmt(s);
3178 }
3179 self.in_final_expr = in_final_expr;
3180 if let Some(e) = b.expr {
3181 self.visit_expr(e);
3182 }
3183 }
3184
3185 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3186 if let Some(e) = l.init {
3187 self.visit_expr(e);
3188 }
3189 if let Some(else_) = l.els {
3190 let is_never = self.is_never;
3191 self.visit_block(else_);
3192 self.is_never = is_never;
3193 }
3194 }
3195
3196 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3197 if let Some(guard) = arm.guard {
3198 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3199 self.visit_expr(guard);
3200 self.in_final_expr = in_final_expr;
3201 }
3202 self.visit_expr(arm.body);
3203 }
3204 }
3205
3206 if cx.typeck_results().expr_ty(e).is_never() {
3207 Some(RequiresSemi::No)
3208 } else if let ExprKind::Block(b, _) = e.kind
3209 && !b.targeted_by_break
3210 && b.expr.is_none()
3211 {
3212 None
3214 } else {
3215 let mut v = V {
3216 cx,
3217 break_targets: Vec::new(),
3218 break_targets_for_result_ty: 0,
3219 in_final_expr: true,
3220 requires_semi: false,
3221 is_never: false,
3222 };
3223 v.visit_expr(e);
3224 v.is_never
3225 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3226 RequiresSemi::Yes
3227 } else {
3228 RequiresSemi::No
3229 })
3230 }
3231}
3232
3233pub fn get_path_from_caller_to_method_type<'tcx>(
3239 tcx: TyCtxt<'tcx>,
3240 from: LocalDefId,
3241 method: DefId,
3242 args: GenericArgsRef<'tcx>,
3243) -> String {
3244 let assoc_item = tcx.associated_item(method);
3245 let def_id = assoc_item.container_id(tcx);
3246 match assoc_item.container {
3247 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3248 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3249 let ty = tcx.type_of(def_id).instantiate_identity();
3250 get_path_to_ty(tcx, from, ty, args)
3251 },
3252 }
3253}
3254
3255fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3256 match ty.kind() {
3257 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3258 rustc_ty::Array(..)
3260 | rustc_ty::Dynamic(..)
3261 | rustc_ty::Never
3262 | rustc_ty::RawPtr(_, _)
3263 | rustc_ty::Ref(..)
3264 | rustc_ty::Slice(_)
3265 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3266 _ => ty.to_string(),
3267 }
3268}
3269
3270fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3272 if callee.is_local() {
3274 let callee_path = tcx.def_path(callee);
3275 let caller_path = tcx.def_path(from.to_def_id());
3276 maybe_get_relative_path(&caller_path, &callee_path, 2)
3277 } else {
3278 tcx.def_path_str(callee)
3279 }
3280}
3281
3282fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3295 use itertools::EitherOrBoth::{Both, Left, Right};
3296
3297 let unique_parts = to
3299 .data
3300 .iter()
3301 .zip_longest(from.data.iter())
3302 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3303 .map(|el| match el {
3304 Both(l, r) => Both(l.data, r.data),
3305 Left(l) => Left(l.data),
3306 Right(r) => Right(r.data),
3307 });
3308
3309 let mut go_up_by = 0;
3311 let mut path = Vec::new();
3312 for el in unique_parts {
3313 match el {
3314 Both(l, r) => {
3315 if let DefPathData::TypeNs(sym) = l {
3325 path.push(sym);
3326 }
3327 if let DefPathData::TypeNs(_) = r {
3328 go_up_by += 1;
3329 }
3330 },
3331 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3336 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3341 _ => {},
3342 }
3343 }
3344
3345 if go_up_by > max_super {
3346 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3348 if let DefPathData::TypeNs(sym) = el.data {
3349 Some(sym)
3350 } else {
3351 None
3352 }
3353 })))
3354 } else {
3355 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3356 }
3357}
3358
3359pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3362 matches!(
3363 cx.tcx.parent_hir_node(id),
3364 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3365 )
3366}
3367
3368pub fn is_block_like(expr: &Expr<'_>) -> bool {
3371 matches!(
3372 expr.kind,
3373 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3374 )
3375}
3376
3377pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3379 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3380 match expr.kind {
3381 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3382 _ if is_block_like(expr) => is_operand,
3383 _ => false,
3384 }
3385 }
3386
3387 contains_block(expr, false)
3388}
3389
3390pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3392 if let Some(parent_expr) = get_parent_expr(cx, expr)
3393 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3394 && receiver.hir_id == expr.hir_id
3395 {
3396 return true;
3397 }
3398 false
3399}
3400
3401pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3404 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3405 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3406 && temporary_ty
3407 .walk()
3408 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3409 {
3410 ControlFlow::Break(())
3411 } else {
3412 ControlFlow::Continue(())
3413 }
3414 })
3415 .is_break()
3416}
3417
3418pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3429 let expr_ty_is_adjusted = cx
3430 .typeck_results()
3431 .expr_adjustments(expr)
3432 .iter()
3433 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3435 if expr_ty_is_adjusted {
3436 return true;
3437 }
3438
3439 match expr.kind {
3442 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3443 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3444
3445 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3446 return false;
3447 }
3448
3449 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3450 let mut args_with_ty_param = {
3451 fn_sig
3452 .inputs()
3453 .skip_binder()
3454 .iter()
3455 .skip(self_arg_count)
3456 .zip(args)
3457 .filter_map(|(arg_ty, arg)| {
3458 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3459 Some(arg)
3460 } else {
3461 None
3462 }
3463 })
3464 };
3465 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3466 },
3467 ExprKind::Struct(qpath, _, _) => {
3469 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3470 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3471 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3472 return true;
3474 };
3475 v_def
3476 .fields
3477 .iter()
3478 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3479 } else {
3480 false
3481 }
3482 },
3483 ExprKind::Block(
3485 &Block {
3486 expr: Some(ret_expr), ..
3487 },
3488 _,
3489 )
3490 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3491
3492 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3494 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3496 ExprKind::If(_, then, maybe_else) => {
3498 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3499 },
3500 ExprKind::Match(_, arms, _) => arms
3501 .iter()
3502 .map(|arm| arm.body)
3503 .any(|body| expr_requires_coercion(cx, body)),
3504 _ => false,
3505 }
3506}
3507
3508pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3511 if let Some(hir_id) = path_to_local(expr)
3512 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3513 {
3514 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3515 } else if let ExprKind::Path(p) = &expr.kind
3516 && let Some(mutability) = cx
3517 .qpath_res(p, expr.hir_id)
3518 .opt_def_id()
3519 .and_then(|id| cx.tcx.static_mutability(id))
3520 {
3521 mutability == Mutability::Mut
3522 } else if let ExprKind::Field(parent, _) = expr.kind {
3523 is_mutable(cx, parent)
3524 } else {
3525 true
3526 }
3527}
3528
3529pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3532 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3533 return hir_ty;
3534 };
3535 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3536 && let Some(segment) = path.segments.last()
3537 && segment.ident.name == sym::Option
3538 && let Res::Def(DefKind::Enum, def_id) = segment.res
3539 && def_id == option_def_id
3540 && let [GenericArg::Type(arg_ty)] = segment.args().args
3541 {
3542 hir_ty = arg_ty.as_unambig_ty();
3543 }
3544 hir_ty
3545}
3546
3547pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3550 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3551 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3552 && let ctxt = expr.span.ctxt()
3553 && for_each_expr_without_closures(into_future_arg, |e| {
3554 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3555 })
3556 .is_none()
3557 {
3558 Some(into_future_arg)
3559 } else {
3560 None
3561 }
3562}
3563
3564pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3566 if let ExprKind::Call(fn_expr, []) = &expr.kind
3567 && let ExprKind::Path(qpath) = &fn_expr.kind
3568 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3569 {
3570 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3571 } else {
3572 false
3573 }
3574}
3575
3576pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3593 let enclosing_body_owner = cx
3594 .tcx
3595 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3596 let mut prev_id = expr.hir_id;
3597 let mut skip_until_id = None;
3598 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3599 if hir_id == enclosing_body_owner {
3600 return true;
3601 }
3602 if let Some(id) = skip_until_id {
3603 prev_id = hir_id;
3604 if id == hir_id {
3605 skip_until_id = None;
3606 }
3607 continue;
3608 }
3609 match node {
3610 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3611 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3612 Node::Expr(expr) => match expr.kind {
3613 ExprKind::Ret(_) => return true,
3614 ExprKind::If(_, then, opt_else)
3615 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3616 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3617 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3618 ExprKind::Break(
3619 Destination {
3620 target_id: Ok(target_id),
3621 ..
3622 },
3623 _,
3624 ) => skip_until_id = Some(target_id),
3625 _ => break,
3626 },
3627 _ => break,
3628 }
3629 prev_id = hir_id;
3630 }
3631
3632 false
3635}
3636
3637pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3640 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3641 matches!(
3642 adj.kind,
3643 Adjust::Deref(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny
3644 )
3645 })
3646}
3647
3648pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3650 matches!(
3651 expr.kind,
3652 ExprKind::Closure(Closure {
3653 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3654 CoroutineDesugaring::Async,
3655 CoroutineSource::Block
3656 )),
3657 ..
3658 })
3659 )
3660}