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 qualify_min_const_fn;
67pub mod source;
68pub mod str_utils;
69pub mod sugg;
70pub mod sym;
71pub mod ty;
72pub mod usage;
73pub mod visitors;
74
75pub use self::attrs::*;
76pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
77pub use self::hir_utils::{
78 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
79 hash_stmt, is_bool, over,
80};
81
82use core::mem;
83use core::ops::ControlFlow;
84use std::collections::hash_map::Entry;
85use std::iter::{once, repeat_n, zip};
86use std::sync::{Mutex, MutexGuard, OnceLock};
87
88use itertools::Itertools;
89use rustc_abi::Integer;
90use rustc_ast::ast::{self, LitKind, RangeLimits};
91use rustc_ast::join_path_syms;
92use rustc_data_structures::fx::FxHashMap;
93use rustc_data_structures::packed::Pu128;
94use rustc_data_structures::unhash::UnindexMap;
95use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
96use rustc_hir::attrs::AttributeKind;
97use rustc_hir::def::{DefKind, Res};
98use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
99use rustc_hir::definitions::{DefPath, DefPathData};
100use rustc_hir::hir_id::{HirIdMap, HirIdSet};
101use rustc_hir::intravisit::{Visitor, walk_expr};
102use rustc_hir::{
103 self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
104 CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs,
105 HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId,
106 OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
107 TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr,
108};
109use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
110use rustc_lint::{LateContext, Level, Lint, LintContext};
111use rustc_middle::hir::nested_filter;
112use rustc_middle::hir::place::PlaceBase;
113use rustc_middle::lint::LevelAndSource;
114use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
115use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, PointerCoercion};
116use rustc_middle::ty::layout::IntegerExt;
117use rustc_middle::ty::{
118 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
119 TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
120};
121use rustc_span::hygiene::{ExpnKind, MacroKind};
122use rustc_span::source_map::SourceMap;
123use rustc_span::symbol::{Ident, Symbol, kw};
124use rustc_span::{InnerSpan, Span};
125use source::{SpanRangeExt, walk_span_to_context};
126use visitors::{Visitable, for_each_unconsumed_temporary};
127
128use crate::ast_utils::unordered_over;
129use crate::consts::{ConstEvalCtxt, Constant};
130use crate::higher::Range;
131use crate::msrvs::Msrv;
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_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
334 matches!(pat.kind,
335 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
336 if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone))
337}
338
339pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
341 is_none_pattern(cx, arm.pat)
342 && matches!(peel_blocks(arm.body).kind, ExprKind::Path(qpath) if is_res_lang_ctor(cx, cx.qpath_res(&qpath, arm.body.hir_id), OptionNone))
343}
344
345pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
347 match *qpath {
348 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
349 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
350 _ => false,
351 }
352}
353
354pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
356 if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
357 cx.tcx.trait_of_assoc(method_id).is_none()
358 } else {
359 false
360 }
361}
362
363pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
365 if let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
366 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
367 {
368 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
369 }
370 false
371}
372
373pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
375 if let Some(trait_did) = cx.tcx.trait_of_assoc(def_id) {
376 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
377 }
378 false
379}
380
381pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
383 cx.typeck_results()
384 .type_dependent_def_id(expr.hir_id)
385 .is_some_and(|did| is_diag_trait_item(cx, did, diag_item))
386}
387
388pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
390 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
391 && let ItemKind::Impl(imp) = item.kind
392 {
393 imp.of_trait.is_some()
394 } else {
395 false
396 }
397}
398
399pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
409 if let ExprKind::Path(ref qpath) = expr.kind {
410 cx.qpath_res(qpath, expr.hir_id)
411 .opt_def_id()
412 .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item))
413 } else {
414 false
415 }
416}
417
418pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
419 match *path {
420 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
421 QPath::TypeRelative(_, seg) => seg,
422 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
423 }
424}
425
426pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
427 last_path_segment(qpath)
428 .args
429 .map_or(&[][..], |a| a.args)
430 .iter()
431 .filter_map(|a| match a {
432 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
433 _ => None,
434 })
435}
436
437pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
440 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id))
441}
442
443pub fn is_path_diagnostic_item<'tcx>(
446 cx: &LateContext<'_>,
447 maybe_path: &impl MaybePath<'tcx>,
448 diag_item: Symbol,
449) -> bool {
450 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
451}
452
453pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
455 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
456 && let Res::Local(id) = path.res
457 {
458 return Some(id);
459 }
460 None
461}
462
463pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
466 path_to_local(expr) == Some(id)
467}
468
469pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
474 match expr.kind {
475 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
476 ExprKind::Path(QPath::Resolved(
477 _,
478 Path {
479 res: Res::Local(local), ..
480 },
481 )) => Some(*local),
482 _ => None,
483 }
484}
485
486pub trait MaybePath<'hir> {
487 fn hir_id(&self) -> HirId;
488 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
489}
490
491macro_rules! maybe_path {
492 ($ty:ident, $kind:ident) => {
493 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
494 fn hir_id(&self) -> HirId {
495 self.hir_id
496 }
497 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
498 match &self.kind {
499 hir::$kind::Path(qpath) => Some(qpath),
500 _ => None,
501 }
502 }
503 }
504 };
505}
506maybe_path!(Expr, ExprKind);
507impl<'hir> MaybePath<'hir> for Pat<'hir> {
508 fn hir_id(&self) -> HirId {
509 self.hir_id
510 }
511 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
512 match &self.kind {
513 PatKind::Expr(PatExpr {
514 kind: PatExprKind::Path(qpath),
515 ..
516 }) => Some(qpath),
517 _ => None,
518 }
519 }
520}
521maybe_path!(Ty, TyKind);
522
523pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
525 match maybe_path.qpath_opt() {
526 None => Res::Err,
527 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
528 }
529}
530
531pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
533 path_res(cx, maybe_path).opt_def_id()
534}
535
536pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
552 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
553 && let ItemKind::Impl(impl_) = &item.kind
554 && let Some(of_trait) = impl_.of_trait
555 {
556 return Some(&of_trait.trait_ref);
557 }
558 None
559}
560
561fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
569 let mut result = vec![];
570 let root = loop {
571 match e.kind {
572 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
573 result.push(e);
574 e = ep;
575 },
576 _ => break e,
577 }
578 };
579 result.reverse();
580 (result, root)
581}
582
583pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
585 cx.typeck_results()
586 .expr_adjustments(e)
587 .iter()
588 .find_map(|a| match a.kind {
589 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
590 Adjust::Deref(None) => None,
591 _ => Some(None),
592 })
593 .and_then(|x| x)
594}
595
596pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
599 let (s1, r1) = projection_stack(e1);
600 let (s2, r2) = projection_stack(e2);
601 if !eq_expr_value(cx, r1, r2) {
602 return true;
603 }
604 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
605 return false;
606 }
607
608 for (x1, x2) in zip(&s1, &s2) {
609 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
610 return false;
611 }
612
613 match (&x1.kind, &x2.kind) {
614 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
615 if i1 != i2 {
616 return true;
617 }
618 },
619 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
620 if !eq_expr_value(cx, i1, i2) {
621 return false;
622 }
623 },
624 _ => return false,
625 }
626 }
627 false
628}
629
630fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
633 let std_types_symbols = &[
634 sym::Vec,
635 sym::VecDeque,
636 sym::LinkedList,
637 sym::HashMap,
638 sym::BTreeMap,
639 sym::HashSet,
640 sym::BTreeSet,
641 sym::BinaryHeap,
642 ];
643
644 if let QPath::TypeRelative(_, method) = path
645 && method.ident.name == sym::new
646 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
647 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
648 {
649 return Some(adt.did()) == cx.tcx.lang_items().string()
650 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
651 }
652 false
653}
654
655pub fn is_default_equivalent_call(
657 cx: &LateContext<'_>,
658 repl_func: &Expr<'_>,
659 whole_call_expr: Option<&Expr<'_>>,
660) -> bool {
661 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
662 && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
663 && (is_diag_trait_item(cx, repl_def_id, sym::Default)
664 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath))
665 {
666 return true;
667 }
668
669 let Some(e) = whole_call_expr else { return false };
672 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
673 return false;
674 };
675 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
676 return false;
677 };
678 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
679 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
680 cx.tcx.lifetimes.re_erased.into()
681 } else if param.index == 0 && param.name == kw::SelfUpper {
682 ty.into()
683 } else {
684 param.to_error(cx.tcx)
685 }
686 });
687 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
688
689 let Ok(Some(instance)) = instance else { return false };
690 if let rustc_ty::InstanceKind::Item(def) = instance.def
691 && !cx.tcx.is_mir_available(def)
692 {
693 return false;
694 }
695 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
696 return false;
697 };
698 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
699 return false;
700 };
701
702 let body = cx.tcx.instance_mir(instance.def);
708 for block_data in body.basic_blocks.iter() {
709 if block_data.statements.len() == 1
710 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
711 && assign.0.local == RETURN_PLACE
712 && let Rvalue::Aggregate(kind, _places) = &assign.1
713 && let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
714 && let def = cx.tcx.adt_def(did)
715 && let variant = &def.variant(*variant_index)
716 && variant.fields.is_empty()
717 && let Some((_, did)) = variant.ctor
718 && did == repl_def_id
719 {
720 return true;
721 } else if block_data.statements.is_empty()
722 && let Some(term) = &block_data.terminator
723 {
724 match &term.kind {
725 TerminatorKind::Call {
726 func: Operand::Constant(c),
727 ..
728 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
729 && *did == repl_def_id =>
730 {
731 return true;
732 },
733 TerminatorKind::TailCall {
734 func: Operand::Constant(c),
735 ..
736 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
737 && *did == repl_def_id =>
738 {
739 return true;
740 },
741 _ => {},
742 }
743 }
744 }
745 false
746}
747
748pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
752 match &e.kind {
753 ExprKind::Lit(lit) => match lit.node {
754 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
755 LitKind::Str(s, _) => s.is_empty(),
756 _ => false,
757 },
758 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
759 ExprKind::Repeat(x, len) => {
760 if let ConstArgKind::Anon(anon_const) = len.kind
761 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
762 && let LitKind::Int(v, _) = const_lit.node
763 && v <= 32
764 && is_default_equivalent(cx, x)
765 {
766 true
767 } else {
768 false
769 }
770 },
771 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
772 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
773 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
774 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
775 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
776 _ => false,
777 }
778}
779
780fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
781 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
782 && seg.ident.name == sym::from
783 {
784 match arg.kind {
785 ExprKind::Lit(hir::Lit {
786 node: LitKind::Str(sym, _),
787 ..
788 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
789 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
790 ExprKind::Repeat(_, len) => {
791 if let ConstArgKind::Anon(anon_const) = len.kind
792 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
793 && let LitKind::Int(v, _) = const_lit.node
794 {
795 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
796 }
797 },
798 _ => (),
799 }
800 }
801 false
802}
803
804pub fn can_move_expr_to_closure_no_visit<'tcx>(
836 cx: &LateContext<'tcx>,
837 expr: &'tcx Expr<'_>,
838 loop_ids: &[HirId],
839 ignore_locals: &HirIdSet,
840) -> bool {
841 match expr.kind {
842 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
843 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
844 if loop_ids.contains(&id) =>
845 {
846 true
847 },
848 ExprKind::Break(..)
849 | ExprKind::Continue(_)
850 | ExprKind::Ret(_)
851 | ExprKind::Yield(..)
852 | ExprKind::InlineAsm(_) => false,
853 ExprKind::Field(
856 &Expr {
857 hir_id,
858 kind:
859 ExprKind::Path(QPath::Resolved(
860 _,
861 Path {
862 res: Res::Local(local_id),
863 ..
864 },
865 )),
866 ..
867 },
868 _,
869 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
870 false
872 },
873 _ => true,
874 }
875}
876
877#[derive(Debug, Clone, Copy, PartialEq, Eq)]
879pub enum CaptureKind {
880 Value,
881 Use,
882 Ref(Mutability),
883}
884impl CaptureKind {
885 pub fn is_imm_ref(self) -> bool {
886 self == Self::Ref(Mutability::Not)
887 }
888}
889impl std::ops::BitOr for CaptureKind {
890 type Output = Self;
891 fn bitor(self, rhs: Self) -> Self::Output {
892 match (self, rhs) {
893 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
894 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
895 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
896 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
897 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
898 }
899 }
900}
901impl std::ops::BitOrAssign for CaptureKind {
902 fn bitor_assign(&mut self, rhs: Self) {
903 *self = *self | rhs;
904 }
905}
906
907pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
913 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
914 let mut capture = CaptureKind::Ref(Mutability::Not);
915 pat.each_binding_or_first(&mut |_, id, span, _| match cx
916 .typeck_results()
917 .extract_binding_mode(cx.sess(), id, span)
918 .0
919 {
920 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
921 capture = CaptureKind::Value;
922 },
923 ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
924 capture = CaptureKind::Ref(Mutability::Mut);
925 },
926 _ => (),
927 });
928 capture
929 }
930
931 debug_assert!(matches!(
932 e.kind,
933 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
934 ));
935
936 let mut child_id = e.hir_id;
937 let mut capture = CaptureKind::Value;
938 let mut capture_expr_ty = e;
939
940 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
941 if let [
942 Adjustment {
943 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
944 target,
945 },
946 ref adjust @ ..,
947 ] = *cx
948 .typeck_results()
949 .adjustments()
950 .get(child_id)
951 .map_or(&[][..], |x| &**x)
952 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
953 *adjust.last().map_or(target, |a| a.target).kind()
954 {
955 return CaptureKind::Ref(mutability);
956 }
957
958 match parent {
959 Node::Expr(e) => match e.kind {
960 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
961 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
962 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
963 return CaptureKind::Ref(Mutability::Mut);
964 },
965 ExprKind::Field(..) => {
966 if capture == CaptureKind::Value {
967 capture_expr_ty = e;
968 }
969 },
970 ExprKind::Let(let_expr) => {
971 let mutability = match pat_capture_kind(cx, let_expr.pat) {
972 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
973 CaptureKind::Ref(m) => m,
974 };
975 return CaptureKind::Ref(mutability);
976 },
977 ExprKind::Match(_, arms, _) => {
978 let mut mutability = Mutability::Not;
979 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
980 match capture {
981 CaptureKind::Value | CaptureKind::Use => break,
982 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
983 CaptureKind::Ref(Mutability::Not) => (),
984 }
985 }
986 return CaptureKind::Ref(mutability);
987 },
988 _ => break,
989 },
990 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
991 CaptureKind::Value | CaptureKind::Use => break,
992 capture @ CaptureKind::Ref(_) => return capture,
993 },
994 _ => break,
995 }
996
997 child_id = parent_id;
998 }
999
1000 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
1001 CaptureKind::Ref(Mutability::Not)
1003 } else {
1004 capture
1005 }
1006}
1007
1008pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
1011 struct V<'cx, 'tcx> {
1012 cx: &'cx LateContext<'tcx>,
1013 loops: Vec<HirId>,
1015 locals: HirIdSet,
1017 allow_closure: bool,
1019 captures: HirIdMap<CaptureKind>,
1022 }
1023 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1024 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1025 if !self.allow_closure {
1026 return;
1027 }
1028
1029 match e.kind {
1030 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1031 if !self.locals.contains(&l) {
1032 let cap = capture_local_usage(self.cx, e);
1033 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1034 }
1035 },
1036 ExprKind::Closure(closure) => {
1037 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
1038 let local_id = match capture.place.base {
1039 PlaceBase::Local(id) => id,
1040 PlaceBase::Upvar(var) => var.var_path.hir_id,
1041 _ => continue,
1042 };
1043 if !self.locals.contains(&local_id) {
1044 let capture = match capture.info.capture_kind {
1045 UpvarCapture::ByValue => CaptureKind::Value,
1046 UpvarCapture::ByUse => CaptureKind::Use,
1047 UpvarCapture::ByRef(kind) => match kind {
1048 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
1049 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
1050 CaptureKind::Ref(Mutability::Mut)
1051 },
1052 },
1053 };
1054 self.captures
1055 .entry(local_id)
1056 .and_modify(|e| *e |= capture)
1057 .or_insert(capture);
1058 }
1059 }
1060 },
1061 ExprKind::Loop(b, ..) => {
1062 self.loops.push(e.hir_id);
1063 self.visit_block(b);
1064 self.loops.pop();
1065 },
1066 _ => {
1067 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1068 walk_expr(self, e);
1069 },
1070 }
1071 }
1072
1073 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1074 p.each_binding_or_first(&mut |_, id, _, _| {
1075 self.locals.insert(id);
1076 });
1077 }
1078 }
1079
1080 let mut v = V {
1081 cx,
1082 loops: Vec::new(),
1083 locals: HirIdSet::default(),
1084 allow_closure: true,
1085 captures: HirIdMap::default(),
1086 };
1087 v.visit_expr(expr);
1088 v.allow_closure.then_some(v.captures)
1089}
1090
1091pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1093
1094pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1097 let mut method_names = Vec::with_capacity(max_depth);
1098 let mut arg_lists = Vec::with_capacity(max_depth);
1099 let mut spans = Vec::with_capacity(max_depth);
1100
1101 let mut current = expr;
1102 for _ in 0..max_depth {
1103 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1104 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1105 break;
1106 }
1107 method_names.push(path.ident.name);
1108 arg_lists.push((*receiver, &**args));
1109 spans.push(path.ident.span);
1110 current = receiver;
1111 } else {
1112 break;
1113 }
1114 }
1115
1116 (method_names, arg_lists, spans)
1117}
1118
1119pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1126 let mut current = expr;
1127 let mut matched = Vec::with_capacity(methods.len());
1128 for method_name in methods.iter().rev() {
1129 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1131 if path.ident.name == *method_name {
1132 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1133 return None;
1134 }
1135 matched.push((receiver, args)); current = receiver; } else {
1138 return None;
1139 }
1140 } else {
1141 return None;
1142 }
1143 }
1144 matched.reverse();
1146 Some(matched)
1147}
1148
1149pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1151 cx.tcx
1152 .entry_fn(())
1153 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1154}
1155
1156pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1158 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1159 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1160}
1161
1162pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1164 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1165 match cx.tcx.hir_node_by_def_id(parent_id) {
1166 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1167 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1168 _ => None,
1169 }
1170}
1171
1172pub struct ContainsName<'a, 'tcx> {
1173 pub cx: &'a LateContext<'tcx>,
1174 pub name: Symbol,
1175}
1176
1177impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1178 type Result = ControlFlow<()>;
1179 type NestedFilter = nested_filter::OnlyBodies;
1180
1181 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1182 if self.name == name {
1183 ControlFlow::Break(())
1184 } else {
1185 ControlFlow::Continue(())
1186 }
1187 }
1188
1189 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1190 self.cx.tcx
1191 }
1192}
1193
1194pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1196 let mut cn = ContainsName { cx, name };
1197 cn.visit_expr(expr).is_break()
1198}
1199
1200pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1202 for_each_expr_without_closures(expr, |e| {
1203 if matches!(e.kind, ExprKind::Ret(..)) {
1204 ControlFlow::Break(())
1205 } else {
1206 ControlFlow::Continue(())
1207 }
1208 })
1209 .is_some()
1210}
1211
1212pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1214 get_parent_expr_for_hir(cx, e.hir_id)
1215}
1216
1217pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1220 match cx.tcx.parent_hir_node(hir_id) {
1221 Node::Expr(parent) => Some(parent),
1222 _ => None,
1223 }
1224}
1225
1226pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1228 let enclosing_node = cx
1229 .tcx
1230 .hir_get_enclosing_scope(hir_id)
1231 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1232 enclosing_node.and_then(|node| match node {
1233 Node::Block(block) => Some(block),
1234 Node::Item(&Item {
1235 kind: ItemKind::Fn { body: eid, .. },
1236 ..
1237 })
1238 | Node::ImplItem(&ImplItem {
1239 kind: ImplItemKind::Fn(_, eid),
1240 ..
1241 })
1242 | Node::TraitItem(&TraitItem {
1243 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1244 ..
1245 }) => match cx.tcx.hir_body(eid).value.kind {
1246 ExprKind::Block(block, _) => Some(block),
1247 _ => None,
1248 },
1249 _ => None,
1250 })
1251}
1252
1253pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1255 cx: &LateContext<'tcx>,
1256 expr: &Expr<'_>,
1257) -> Option<&'tcx Expr<'tcx>> {
1258 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1259 match node {
1260 Node::Expr(e) => match e.kind {
1261 ExprKind::Closure { .. }
1262 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1263 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1264
1265 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1267 _ => (),
1268 },
1269 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1270 _ => break,
1271 }
1272 }
1273 None
1274}
1275
1276pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1278 match tcx.hir_parent_iter(id).next() {
1279 Some((
1280 _,
1281 Node::Item(Item {
1282 kind: ItemKind::Impl(imp),
1283 ..
1284 }),
1285 )) => Some(imp),
1286 _ => None,
1287 }
1288}
1289
1290pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1301 while let ExprKind::Block(
1302 Block {
1303 stmts: [],
1304 expr: Some(inner),
1305 rules: BlockCheckMode::DefaultBlock,
1306 ..
1307 },
1308 _,
1309 ) = expr.kind
1310 {
1311 expr = inner;
1312 }
1313 expr
1314}
1315
1316pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1327 while let ExprKind::Block(
1328 Block {
1329 stmts: [],
1330 expr: Some(inner),
1331 rules: BlockCheckMode::DefaultBlock,
1332 ..
1333 }
1334 | Block {
1335 stmts:
1336 [
1337 Stmt {
1338 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1339 ..
1340 },
1341 ],
1342 expr: None,
1343 rules: BlockCheckMode::DefaultBlock,
1344 ..
1345 },
1346 _,
1347 ) = expr.kind
1348 {
1349 expr = inner;
1350 }
1351 expr
1352}
1353
1354pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1356 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1357 match iter.next() {
1358 Some((
1359 _,
1360 Node::Expr(Expr {
1361 kind: ExprKind::If(_, _, Some(else_expr)),
1362 ..
1363 }),
1364 )) => else_expr.hir_id == expr.hir_id,
1365 _ => false,
1366 }
1367}
1368
1369pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1372 let mut child_id = expr.hir_id;
1373 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1374 if let Node::LetStmt(LetStmt {
1375 init: Some(init),
1376 els: Some(els),
1377 ..
1378 }) = node
1379 && (init.hir_id == child_id || els.hir_id == child_id)
1380 {
1381 return true;
1382 }
1383
1384 child_id = parent_id;
1385 }
1386
1387 false
1388}
1389
1390pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1392 let mut child_id = expr.hir_id;
1393 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1394 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1395 && els.hir_id == child_id
1396 {
1397 return true;
1398 }
1399
1400 child_id = parent_id;
1401 }
1402
1403 false
1404}
1405
1406pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1421 let ty = cx.typeck_results().expr_ty(expr);
1422 if let Some(Range { start, end, limits }) = Range::hir(expr) {
1423 let start_is_none_or_min = start.is_none_or(|start| {
1424 if let rustc_ty::Adt(_, subst) = ty.kind()
1425 && let bnd_ty = subst.type_at(0)
1426 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1427 {
1428 start_const.is_numeric_min(cx.tcx, bnd_ty)
1429 } else {
1430 false
1431 }
1432 });
1433 let end_is_none_or_max = end.is_none_or(|end| match limits {
1434 RangeLimits::Closed => {
1435 if let rustc_ty::Adt(_, subst) = ty.kind()
1436 && let bnd_ty = subst.type_at(0)
1437 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1438 {
1439 end_const.is_numeric_max(cx.tcx, bnd_ty)
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 get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1856 if let ExprKind::Closure(&Closure {
1857 body,
1858 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1859 ..
1860 }) = expr.kind
1861 && let ExprKind::Block(
1862 Block {
1863 expr:
1864 Some(Expr {
1865 kind: ExprKind::DropTemps(inner_expr),
1866 ..
1867 }),
1868 ..
1869 },
1870 _,
1871 ) = tcx.hir_body(body).value.kind
1872 {
1873 Some(inner_expr)
1874 } else {
1875 None
1876 }
1877}
1878
1879pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1881 get_async_closure_expr(tcx, body.value)
1882}
1883
1884pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1886 let did = match expr.kind {
1887 ExprKind::Call(path, _) => {
1888 if let ExprKind::Path(ref qpath) = path.kind
1889 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1890 {
1891 Some(did)
1892 } else {
1893 None
1894 }
1895 },
1896 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1897 _ => None,
1898 };
1899
1900 did.is_some_and(|did| find_attr!(cx.tcx.get_all_attrs(did), AttributeKind::MustUse { .. }))
1901}
1902
1903fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1915 let [param] = func.params else {
1916 return false;
1917 };
1918
1919 let mut expr = func.value;
1920 loop {
1921 match expr.kind {
1922 ExprKind::Block(
1923 &Block {
1924 stmts: [],
1925 expr: Some(e),
1926 ..
1927 },
1928 _,
1929 )
1930 | ExprKind::Ret(Some(e)) => expr = e,
1931 ExprKind::Block(
1932 &Block {
1933 stmts: [stmt],
1934 expr: None,
1935 ..
1936 },
1937 _,
1938 ) => {
1939 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1940 && let ExprKind::Ret(Some(ret_val)) = e.kind
1941 {
1942 expr = ret_val;
1943 } else {
1944 return false;
1945 }
1946 },
1947 _ => return is_expr_identity_of_pat(cx, param.pat, expr, true),
1948 }
1949 }
1950}
1951
1952pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1962 if cx
1963 .typeck_results()
1964 .pat_binding_modes()
1965 .get(pat.hir_id)
1966 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
1967 {
1968 return false;
1972 }
1973
1974 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1976
1977 match (pat.kind, expr.kind) {
1978 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1979 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1980 },
1981 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1982 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1983 },
1984 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1985 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1986 {
1987 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1988 },
1989 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1990 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1991 },
1992 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1993 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1994 {
1995 if let ExprKind::Path(ident) = &ident.kind
1997 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1998 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
2000 {
2001 true
2002 } else {
2003 false
2004 }
2005 },
2006 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
2007 if field_pats.len() == fields.len() =>
2008 {
2009 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
2011 && unordered_over(field_pats, fields, |field_pat, field| {
2013 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
2014 })
2015 },
2016 _ => false,
2017 }
2018}
2019
2020pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2025 match expr.kind {
2026 ExprKind::Closure(&Closure { body, fn_decl, .. })
2027 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
2028 {
2029 is_body_identity_function(cx, cx.tcx.hir_body(body))
2030 },
2031 ExprKind::Path(QPath::Resolved(_, path))
2032 if path.segments.iter().all(|seg| seg.infer_args)
2033 && let Some(did) = path.res.opt_def_id() =>
2034 {
2035 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
2036 },
2037 _ => false,
2038 }
2039}
2040
2041pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2050 match expr.kind {
2051 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
2052 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
2053 }
2054}
2055
2056pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2059 let mut child_id = expr.hir_id;
2060 let mut iter = tcx.hir_parent_iter(child_id);
2061 loop {
2062 match iter.next() {
2063 None => break None,
2064 Some((id, Node::Block(_))) => child_id = id,
2065 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2066 Some((_, Node::Expr(expr))) => match expr.kind {
2067 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2068 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2069 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2070 _ => break Some((Node::Expr(expr), child_id)),
2071 },
2072 Some((_, node)) => break Some((node, child_id)),
2073 }
2074 }
2075}
2076
2077pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2079 !matches!(
2080 get_expr_use_or_unification_node(tcx, expr),
2081 None | Some((
2082 Node::Stmt(Stmt {
2083 kind: StmtKind::Expr(_)
2084 | StmtKind::Semi(_)
2085 | StmtKind::Let(LetStmt {
2086 pat: Pat {
2087 kind: PatKind::Wild,
2088 ..
2089 },
2090 ..
2091 }),
2092 ..
2093 }),
2094 _
2095 ))
2096 )
2097}
2098
2099pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2101 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2102}
2103
2104pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2108 !expr.is_place_expr(|base| {
2109 cx.typeck_results()
2110 .adjustments()
2111 .get(base.hir_id)
2112 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2113 })
2114}
2115
2116pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2117 if !is_no_std_crate(cx) {
2118 Some("std")
2119 } else if !is_no_core_crate(cx) {
2120 Some("core")
2121 } else {
2122 None
2123 }
2124}
2125
2126pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2127 find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoStd(..))
2128}
2129
2130pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2131 find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoCore(..))
2132}
2133
2134pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2144 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2145 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2146 } else {
2147 false
2148 }
2149}
2150
2151pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2161 use rustc_trait_selection::traits;
2162 let predicates = cx
2163 .tcx
2164 .predicates_of(did)
2165 .predicates
2166 .iter()
2167 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2168 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2169}
2170
2171pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2173 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2174}
2175
2176pub fn fn_def_id_with_node_args<'tcx>(
2179 cx: &LateContext<'tcx>,
2180 expr: &Expr<'_>,
2181) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2182 let typeck = cx.typeck_results();
2183 match &expr.kind {
2184 ExprKind::MethodCall(..) => Some((
2185 typeck.type_dependent_def_id(expr.hir_id)?,
2186 typeck.node_args(expr.hir_id),
2187 )),
2188 ExprKind::Call(
2189 Expr {
2190 kind: ExprKind::Path(qpath),
2191 hir_id: path_hir_id,
2192 ..
2193 },
2194 ..,
2195 ) => {
2196 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2199 typeck.qpath_res(qpath, *path_hir_id)
2200 {
2201 Some((id, typeck.node_args(*path_hir_id)))
2202 } else {
2203 None
2204 }
2205 },
2206 _ => None,
2207 }
2208}
2209
2210pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2215 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2216 let expr_kind = expr_type.kind();
2217 let is_primitive = match expr_kind {
2218 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2219 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2220 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2221 is_recursively_primitive_type(*element_type)
2222 } else {
2223 unreachable!()
2224 }
2225 },
2226 _ => false,
2227 };
2228
2229 if is_primitive {
2230 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2233 rustc_ty::Slice(..) => return Some("slice".into()),
2234 rustc_ty::Array(..) => return Some("array".into()),
2235 rustc_ty::Tuple(..) => return Some("tuple".into()),
2236 _ => {
2237 let refs_peeled = expr_type.peel_refs();
2240 return Some(refs_peeled.walk().last().unwrap().to_string());
2241 },
2242 }
2243 }
2244 None
2245}
2246
2247pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2255where
2256 Hash: FnMut(&T) -> u64,
2257 Eq: FnMut(&T, &T) -> bool,
2258{
2259 match exprs {
2260 [a, b] if eq(a, b) => return vec![vec![a, b]],
2261 _ if exprs.len() <= 2 => return vec![],
2262 _ => {},
2263 }
2264
2265 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2266
2267 for expr in exprs {
2268 match buckets.entry(hash(expr)) {
2269 indexmap::map::Entry::Occupied(mut o) => {
2270 let bucket = o.get_mut();
2271 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2272 Some(group) => group.push(expr),
2273 None => bucket.push(vec![expr]),
2274 }
2275 },
2276 indexmap::map::Entry::Vacant(v) => {
2277 v.insert(vec![vec![expr]]);
2278 },
2279 }
2280 }
2281
2282 buckets
2283 .into_values()
2284 .flatten()
2285 .filter(|group| group.len() > 1)
2286 .collect()
2287}
2288
2289pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2292 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2293 if let PatKind::Ref(pat, _) = pat.kind {
2294 peel(pat, count + 1)
2295 } else {
2296 (pat, count)
2297 }
2298 }
2299 peel(pat, 0)
2300}
2301
2302pub fn peel_hir_expr_while<'tcx>(
2304 mut expr: &'tcx Expr<'tcx>,
2305 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2306) -> &'tcx Expr<'tcx> {
2307 while let Some(e) = f(expr) {
2308 expr = e;
2309 }
2310 expr
2311}
2312
2313pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2316 let mut remaining = count;
2317 let e = peel_hir_expr_while(expr, |e| match e.kind {
2318 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2319 remaining -= 1;
2320 Some(e)
2321 },
2322 _ => None,
2323 });
2324 (e, count - remaining)
2325}
2326
2327pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2330 let mut count: usize = 0;
2331 let mut curr_expr = expr;
2332 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2333 count = count.wrapping_add(1);
2334 curr_expr = local_expr;
2335 }
2336 (curr_expr, count)
2337}
2338
2339pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2342 let mut count = 0;
2343 let e = peel_hir_expr_while(expr, |e| match e.kind {
2344 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2345 count += 1;
2346 Some(e)
2347 },
2348 _ => None,
2349 });
2350 (e, count)
2351}
2352
2353pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2356 let mut count = 0;
2357 loop {
2358 match &ty.kind {
2359 TyKind::Ref(_, ref_ty) => {
2360 ty = ref_ty.ty;
2361 count += 1;
2362 },
2363 _ => break (ty, count),
2364 }
2365 }
2366}
2367
2368pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
2370 match &ty.kind {
2371 TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty),
2372 _ => ty,
2373 }
2374}
2375
2376pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2379 loop {
2380 match expr.kind {
2381 ExprKind::AddrOf(_, _, e) => expr = e,
2382 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2383 _ => break,
2384 }
2385 }
2386 expr
2387}
2388
2389pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2392 let mut operators = Vec::new();
2393 peel_hir_expr_while(expr, |expr| match expr.kind {
2394 ExprKind::AddrOf(_, _, e) => {
2395 operators.push(expr);
2396 Some(e)
2397 },
2398 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2399 operators.push(expr);
2400 Some(e)
2401 },
2402 _ => None,
2403 });
2404 operators
2405}
2406
2407pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2408 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2409 && let Res::Def(_, def_id) = path.res
2410 {
2411 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2412 }
2413 false
2414}
2415
2416static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2417
2418fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2421 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2422 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2423 let value = map.entry(module);
2424 match value {
2425 Entry::Occupied(entry) => f(entry.get()),
2426 Entry::Vacant(entry) => {
2427 let mut names = Vec::new();
2428 for id in tcx.hir_module_free_items(module) {
2429 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2430 && let item = tcx.hir_item(id)
2431 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2432 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2433 && let Res::Def(DefKind::Struct, _) = path.res
2435 {
2436 let has_test_marker = tcx
2437 .hir_attrs(item.hir_id())
2438 .iter()
2439 .any(|a| a.has_name(sym::rustc_test_marker));
2440 if has_test_marker {
2441 names.push(ident.name);
2442 }
2443 }
2444 }
2445 names.sort_unstable();
2446 f(entry.insert(names))
2447 },
2448 }
2449}
2450
2451pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2455 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2456 let node = tcx.hir_node(id);
2457 once((id, node))
2458 .chain(tcx.hir_parent_iter(id))
2459 .any(|(_id, node)| {
2462 if let Node::Item(item) = node
2463 && let ItemKind::Fn { ident, .. } = item.kind
2464 {
2465 return names.binary_search(&ident.name).is_ok();
2468 }
2469 false
2470 })
2471 })
2472}
2473
2474pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2481 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2482 if let Node::Item(item) = tcx.hir_node(id)
2483 && let ItemKind::Fn { ident, .. } = item.kind
2484 {
2485 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2486 names.binary_search(&ident.name).is_ok()
2487 })
2488 } else {
2489 false
2490 }
2491}
2492
2493pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2498 tcx.hir_attrs(id).iter().any(|attr| {
2499 if attr.has_name(sym::cfg_trace)
2500 && let Some(items) = attr.meta_item_list()
2501 && let [item] = &*items
2502 && item.has_name(sym::test)
2503 {
2504 true
2505 } else {
2506 false
2507 }
2508 })
2509}
2510
2511pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2513 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2514}
2515
2516pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2518 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2519}
2520
2521pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2523 tcx.has_attr(def_id, sym::cfg_trace)
2524 || tcx
2525 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2526 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2527 .any(|attr| attr.has_name(sym::cfg_trace))
2528}
2529
2530pub fn walk_to_expr_usage<'tcx, T>(
2541 cx: &LateContext<'tcx>,
2542 e: &Expr<'tcx>,
2543 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2544) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2545 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2546 let mut child_id = e.hir_id;
2547
2548 while let Some((parent_id, parent)) = iter.next() {
2549 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2550 return Some(ControlFlow::Break(x));
2551 }
2552 let parent_expr = match parent {
2553 Node::Expr(e) => e,
2554 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2555 child_id = parent_id;
2556 continue;
2557 },
2558 Node::Arm(a) if a.body.hir_id == child_id => {
2559 child_id = parent_id;
2560 continue;
2561 },
2562 _ => return Some(ControlFlow::Continue((parent, child_id))),
2563 };
2564 match parent_expr.kind {
2565 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2566 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2567 child_id = id;
2568 iter = cx.tcx.hir_parent_iter(id);
2569 },
2570 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2571 _ => return Some(ControlFlow::Continue((parent, child_id))),
2572 }
2573 }
2574 debug_assert!(false, "no parent node found for `{child_id:?}`");
2575 None
2576}
2577
2578#[derive(Clone, Copy)]
2580pub enum DefinedTy<'tcx> {
2581 Hir(&'tcx hir::Ty<'tcx>),
2583 Mir {
2591 def_site_def_id: Option<DefId>,
2592 ty: Binder<'tcx, Ty<'tcx>>,
2593 },
2594}
2595
2596pub struct ExprUseCtxt<'tcx> {
2598 pub node: Node<'tcx>,
2600 pub child_id: HirId,
2602 pub adjustments: &'tcx [Adjustment<'tcx>],
2604 pub is_ty_unified: bool,
2606 pub moved_before_use: bool,
2608 pub same_ctxt: bool,
2610}
2611impl<'tcx> ExprUseCtxt<'tcx> {
2612 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2613 match self.node {
2614 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2615 Node::ExprField(field) => ExprUseNode::Field(field),
2616
2617 Node::Item(&Item {
2618 kind: ItemKind::Static(..) | ItemKind::Const(..),
2619 owner_id,
2620 ..
2621 })
2622 | Node::TraitItem(&TraitItem {
2623 kind: TraitItemKind::Const(..),
2624 owner_id,
2625 ..
2626 })
2627 | Node::ImplItem(&ImplItem {
2628 kind: ImplItemKind::Const(..),
2629 owner_id,
2630 ..
2631 }) => ExprUseNode::ConstStatic(owner_id),
2632
2633 Node::Item(&Item {
2634 kind: ItemKind::Fn { .. },
2635 owner_id,
2636 ..
2637 })
2638 | Node::TraitItem(&TraitItem {
2639 kind: TraitItemKind::Fn(..),
2640 owner_id,
2641 ..
2642 })
2643 | Node::ImplItem(&ImplItem {
2644 kind: ImplItemKind::Fn(..),
2645 owner_id,
2646 ..
2647 }) => ExprUseNode::Return(owner_id),
2648
2649 Node::Expr(use_expr) => match use_expr.kind {
2650 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2651 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2652 }),
2653
2654 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2655 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2656 Some(i) => ExprUseNode::FnArg(func, i),
2657 None => ExprUseNode::Callee,
2658 },
2659 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2660 use_expr.hir_id,
2661 name.args,
2662 args.iter()
2663 .position(|arg| arg.hir_id == self.child_id)
2664 .map_or(0, |i| i + 1),
2665 ),
2666 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2667 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2668 _ => ExprUseNode::Other,
2669 },
2670 _ => ExprUseNode::Other,
2671 }
2672 }
2673}
2674
2675pub enum ExprUseNode<'tcx> {
2677 LetStmt(&'tcx LetStmt<'tcx>),
2679 ConstStatic(OwnerId),
2681 Return(OwnerId),
2683 Field(&'tcx ExprField<'tcx>),
2685 FnArg(&'tcx Expr<'tcx>, usize),
2687 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2689 Callee,
2691 FieldAccess(Ident),
2693 AddrOf(ast::BorrowKind, Mutability),
2695 Other,
2696}
2697impl<'tcx> ExprUseNode<'tcx> {
2698 pub fn is_return(&self) -> bool {
2700 matches!(self, Self::Return(_))
2701 }
2702
2703 pub fn is_recv(&self) -> bool {
2705 matches!(self, Self::MethodArg(_, _, 0))
2706 }
2707
2708 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2710 match *self {
2711 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2712 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2713 def_site_def_id: Some(id.def_id.to_def_id()),
2714 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2715 }),
2716 Self::Return(id) => {
2717 if let Node::Expr(Expr {
2718 kind: ExprKind::Closure(c),
2719 ..
2720 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2721 {
2722 match c.fn_decl.output {
2723 FnRetTy::DefaultReturn(_) => None,
2724 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2725 }
2726 } else {
2727 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2728 Some(DefinedTy::Mir {
2729 def_site_def_id: Some(id.def_id.to_def_id()),
2730 ty,
2731 })
2732 }
2733 },
2734 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2735 Some(Expr {
2736 hir_id,
2737 kind: ExprKind::Struct(path, ..),
2738 ..
2739 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2740 .and_then(|(adt, variant)| {
2741 variant
2742 .fields
2743 .iter()
2744 .find(|f| f.name == field.ident.name)
2745 .map(|f| (adt, f))
2746 })
2747 .map(|(adt, field_def)| DefinedTy::Mir {
2748 def_site_def_id: Some(adt.did()),
2749 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2750 }),
2751 _ => None,
2752 },
2753 Self::FnArg(callee, i) => {
2754 let sig = expr_sig(cx, callee)?;
2755 let (hir_ty, ty) = sig.input_with_hir(i)?;
2756 Some(match hir_ty {
2757 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2758 None => DefinedTy::Mir {
2759 def_site_def_id: sig.predicates_id(),
2760 ty,
2761 },
2762 })
2763 },
2764 Self::MethodArg(id, _, i) => {
2765 let id = cx.typeck_results().type_dependent_def_id(id)?;
2766 let sig = cx.tcx.fn_sig(id).skip_binder();
2767 Some(DefinedTy::Mir {
2768 def_site_def_id: Some(id),
2769 ty: sig.input(i),
2770 })
2771 },
2772 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2773 }
2774 }
2775}
2776
2777pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2779 let mut adjustments = [].as_slice();
2780 let mut is_ty_unified = false;
2781 let mut moved_before_use = false;
2782 let mut same_ctxt = true;
2783 let ctxt = e.span.ctxt();
2784 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2785 if adjustments.is_empty()
2786 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2787 {
2788 adjustments = cx.typeck_results().expr_adjustments(e);
2789 }
2790 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2791 if let Node::Expr(e) = parent {
2792 match e.kind {
2793 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2794 is_ty_unified = true;
2795 moved_before_use = true;
2796 },
2797 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2798 is_ty_unified = true;
2799 moved_before_use = true;
2800 },
2801 ExprKind::Block(..) => moved_before_use = true,
2802 _ => {},
2803 }
2804 }
2805 ControlFlow::Continue(())
2806 });
2807 match node {
2808 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2809 node,
2810 child_id,
2811 adjustments,
2812 is_ty_unified,
2813 moved_before_use,
2814 same_ctxt,
2815 },
2816 #[allow(unreachable_patterns)]
2817 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
2818 None => ExprUseCtxt {
2819 node: Node::Crate(cx.tcx.hir_root_module()),
2820 child_id: HirId::INVALID,
2821 adjustments: &[],
2822 is_ty_unified: true,
2823 moved_before_use: true,
2824 same_ctxt: false,
2825 },
2826 }
2827}
2828
2829pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2831 let mut pos = 0;
2832 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2833 let end = pos + t.len;
2834 let range = pos as usize..end as usize;
2835 let inner = InnerSpan::new(range.start, range.end);
2836 pos = end;
2837 (t.kind, s.get(range).unwrap_or_default(), inner)
2838 })
2839}
2840
2841pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2844 let Ok(snippet) = sm.span_to_snippet(span) else {
2845 return false;
2846 };
2847 return tokenize(&snippet, FrontmatterAllowed::No).any(|token| {
2848 matches!(
2849 token.kind,
2850 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2851 )
2852 });
2853}
2854
2855pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2860 matches!(span.get_source_text(cx), Some(snippet) if tokenize_with_text(&snippet).any(|(token, _, _)|
2861 match token {
2862 TokenKind::Whitespace => false,
2863 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2864 _ => true,
2865 }
2866 ))
2867}
2868pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2872 span_extract_comments(sm, span).join("\n")
2873}
2874
2875pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2879 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2880 tokenize_with_text(&snippet)
2881 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2882 .map(|(_, s, _)| s.to_string())
2883 .collect::<Vec<_>>()
2884}
2885
2886pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2887 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2888}
2889
2890pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2915 cx: &LateContext<'_>,
2916 pat: &'a Pat<'hir>,
2917 else_body: &Expr<'_>,
2918) -> Option<&'a Pat<'hir>> {
2919 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
2920 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
2921 && !is_refutable(cx, inner_pat)
2922 && let else_body = peel_blocks(else_body)
2923 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2924 && let ExprKind::Path(ret_path) = ret_val.kind
2925 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
2926 {
2927 Some(inner_pat)
2928 } else {
2929 None
2930 }
2931}
2932
2933macro_rules! op_utils {
2934 ($($name:ident $assign:ident)*) => {
2935 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2937
2938 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2940
2941 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2943 match kind {
2944 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2945 _ => None,
2946 }
2947 }
2948 };
2949}
2950
2951op_utils! {
2952 Add AddAssign
2953 Sub SubAssign
2954 Mul MulAssign
2955 Div DivAssign
2956 Rem RemAssign
2957 BitXor BitXorAssign
2958 BitAnd BitAndAssign
2959 BitOr BitOrAssign
2960 Shl ShlAssign
2961 Shr ShrAssign
2962}
2963
2964pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2967 match *pat {
2968 PatKind::Wild => true,
2969 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2970 !visitors::is_local_used(cx, body, id)
2971 },
2972 _ => false,
2973 }
2974}
2975
2976#[derive(Clone, Copy)]
2977pub enum RequiresSemi {
2978 Yes,
2979 No,
2980}
2981impl RequiresSemi {
2982 pub fn requires_semi(self) -> bool {
2983 matches!(self, Self::Yes)
2984 }
2985}
2986
2987#[expect(clippy::too_many_lines)]
2990pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2991 struct BreakTarget {
2992 id: HirId,
2993 unused: bool,
2994 }
2995
2996 struct V<'cx, 'tcx> {
2997 cx: &'cx LateContext<'tcx>,
2998 break_targets: Vec<BreakTarget>,
2999 break_targets_for_result_ty: u32,
3000 in_final_expr: bool,
3001 requires_semi: bool,
3002 is_never: bool,
3003 }
3004
3005 impl V<'_, '_> {
3006 fn push_break_target(&mut self, id: HirId) {
3007 self.break_targets.push(BreakTarget { id, unused: true });
3008 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3009 }
3010 }
3011
3012 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3013 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3014 if self.is_never && self.break_targets.is_empty() {
3031 if self.in_final_expr && !self.requires_semi {
3032 match e.kind {
3035 ExprKind::DropTemps(e) => self.visit_expr(e),
3036 ExprKind::If(_, then, Some(else_)) => {
3037 self.visit_expr(then);
3038 self.visit_expr(else_);
3039 },
3040 ExprKind::Match(_, arms, _) => {
3041 for arm in arms {
3042 self.visit_expr(arm.body);
3043 }
3044 },
3045 ExprKind::Loop(b, ..) => {
3046 self.push_break_target(e.hir_id);
3047 self.in_final_expr = false;
3048 self.visit_block(b);
3049 self.break_targets.pop();
3050 },
3051 ExprKind::Block(b, _) => {
3052 if b.targeted_by_break {
3053 self.push_break_target(b.hir_id);
3054 self.visit_block(b);
3055 self.break_targets.pop();
3056 } else {
3057 self.visit_block(b);
3058 }
3059 },
3060 _ => {
3061 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3062 },
3063 }
3064 }
3065 return;
3066 }
3067 match e.kind {
3068 ExprKind::DropTemps(e) => self.visit_expr(e),
3069 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3070 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3071 self.in_final_expr = false;
3072 self.visit_expr(e);
3073 self.is_never = true;
3074 },
3075 ExprKind::Break(dest, e) => {
3076 if let Some(e) = e {
3077 self.in_final_expr = false;
3078 self.visit_expr(e);
3079 }
3080 if let Ok(id) = dest.target_id
3081 && let Some((i, target)) = self
3082 .break_targets
3083 .iter_mut()
3084 .enumerate()
3085 .find(|(_, target)| target.id == id)
3086 {
3087 target.unused &= self.is_never;
3088 if i < self.break_targets_for_result_ty as usize {
3089 self.requires_semi = true;
3090 }
3091 }
3092 self.is_never = true;
3093 },
3094 ExprKind::If(cond, then, else_) => {
3095 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3096 self.visit_expr(cond);
3097 self.in_final_expr = in_final_expr;
3098
3099 if self.is_never {
3100 self.visit_expr(then);
3101 if let Some(else_) = else_ {
3102 self.visit_expr(else_);
3103 }
3104 } else {
3105 self.visit_expr(then);
3106 let is_never = mem::replace(&mut self.is_never, false);
3107 if let Some(else_) = else_ {
3108 self.visit_expr(else_);
3109 self.is_never &= is_never;
3110 }
3111 }
3112 },
3113 ExprKind::Match(scrutinee, arms, _) => {
3114 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3115 self.visit_expr(scrutinee);
3116 self.in_final_expr = in_final_expr;
3117
3118 if self.is_never {
3119 for arm in arms {
3120 self.visit_arm(arm);
3121 }
3122 } else {
3123 let mut is_never = true;
3124 for arm in arms {
3125 self.is_never = false;
3126 if let Some(guard) = arm.guard {
3127 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3128 self.visit_expr(guard);
3129 self.in_final_expr = in_final_expr;
3130 self.is_never = false;
3132 }
3133 self.visit_expr(arm.body);
3134 is_never &= self.is_never;
3135 }
3136 self.is_never = is_never;
3137 }
3138 },
3139 ExprKind::Loop(b, _, _, _) => {
3140 self.push_break_target(e.hir_id);
3141 self.in_final_expr = false;
3142 self.visit_block(b);
3143 self.is_never = self.break_targets.pop().unwrap().unused;
3144 },
3145 ExprKind::Block(b, _) => {
3146 if b.targeted_by_break {
3147 self.push_break_target(b.hir_id);
3148 self.visit_block(b);
3149 self.is_never &= self.break_targets.pop().unwrap().unused;
3150 } else {
3151 self.visit_block(b);
3152 }
3153 },
3154 _ => {
3155 self.in_final_expr = false;
3156 walk_expr(self, e);
3157 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3158 },
3159 }
3160 }
3161
3162 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3163 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3164 for s in b.stmts {
3165 self.visit_stmt(s);
3166 }
3167 self.in_final_expr = in_final_expr;
3168 if let Some(e) = b.expr {
3169 self.visit_expr(e);
3170 }
3171 }
3172
3173 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3174 if let Some(e) = l.init {
3175 self.visit_expr(e);
3176 }
3177 if let Some(else_) = l.els {
3178 let is_never = self.is_never;
3179 self.visit_block(else_);
3180 self.is_never = is_never;
3181 }
3182 }
3183
3184 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3185 if let Some(guard) = arm.guard {
3186 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3187 self.visit_expr(guard);
3188 self.in_final_expr = in_final_expr;
3189 }
3190 self.visit_expr(arm.body);
3191 }
3192 }
3193
3194 if cx.typeck_results().expr_ty(e).is_never() {
3195 Some(RequiresSemi::No)
3196 } else if let ExprKind::Block(b, _) = e.kind
3197 && !b.targeted_by_break
3198 && b.expr.is_none()
3199 {
3200 None
3202 } else {
3203 let mut v = V {
3204 cx,
3205 break_targets: Vec::new(),
3206 break_targets_for_result_ty: 0,
3207 in_final_expr: true,
3208 requires_semi: false,
3209 is_never: false,
3210 };
3211 v.visit_expr(e);
3212 v.is_never
3213 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3214 RequiresSemi::Yes
3215 } else {
3216 RequiresSemi::No
3217 })
3218 }
3219}
3220
3221pub fn get_path_from_caller_to_method_type<'tcx>(
3227 tcx: TyCtxt<'tcx>,
3228 from: LocalDefId,
3229 method: DefId,
3230 args: GenericArgsRef<'tcx>,
3231) -> String {
3232 let assoc_item = tcx.associated_item(method);
3233 let def_id = assoc_item.container_id(tcx);
3234 match assoc_item.container {
3235 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3236 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3237 let ty = tcx.type_of(def_id).instantiate_identity();
3238 get_path_to_ty(tcx, from, ty, args)
3239 },
3240 }
3241}
3242
3243fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3244 match ty.kind() {
3245 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3246 rustc_ty::Array(..)
3248 | rustc_ty::Dynamic(..)
3249 | rustc_ty::Never
3250 | rustc_ty::RawPtr(_, _)
3251 | rustc_ty::Ref(..)
3252 | rustc_ty::Slice(_)
3253 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3254 _ => ty.to_string(),
3255 }
3256}
3257
3258fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3260 if callee.is_local() {
3262 let callee_path = tcx.def_path(callee);
3263 let caller_path = tcx.def_path(from.to_def_id());
3264 maybe_get_relative_path(&caller_path, &callee_path, 2)
3265 } else {
3266 tcx.def_path_str(callee)
3267 }
3268}
3269
3270fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3283 use itertools::EitherOrBoth::{Both, Left, Right};
3284
3285 let unique_parts = to
3287 .data
3288 .iter()
3289 .zip_longest(from.data.iter())
3290 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3291 .map(|el| match el {
3292 Both(l, r) => Both(l.data, r.data),
3293 Left(l) => Left(l.data),
3294 Right(r) => Right(r.data),
3295 });
3296
3297 let mut go_up_by = 0;
3299 let mut path = Vec::new();
3300 for el in unique_parts {
3301 match el {
3302 Both(l, r) => {
3303 if let DefPathData::TypeNs(sym) = l {
3313 path.push(sym);
3314 }
3315 if let DefPathData::TypeNs(_) = r {
3316 go_up_by += 1;
3317 }
3318 },
3319 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3324 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3329 _ => {},
3330 }
3331 }
3332
3333 if go_up_by > max_super {
3334 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3336 if let DefPathData::TypeNs(sym) = el.data {
3337 Some(sym)
3338 } else {
3339 None
3340 }
3341 })))
3342 } else {
3343 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3344 }
3345}
3346
3347pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3350 matches!(
3351 cx.tcx.parent_hir_node(id),
3352 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3353 )
3354}
3355
3356pub fn is_block_like(expr: &Expr<'_>) -> bool {
3359 matches!(
3360 expr.kind,
3361 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3362 )
3363}
3364
3365pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3367 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3368 match expr.kind {
3369 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3370 _ if is_block_like(expr) => is_operand,
3371 _ => false,
3372 }
3373 }
3374
3375 contains_block(expr, false)
3376}
3377
3378pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3380 if let Some(parent_expr) = get_parent_expr(cx, expr)
3381 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3382 && receiver.hir_id == expr.hir_id
3383 {
3384 return true;
3385 }
3386 false
3387}
3388
3389pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3392 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3393 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3394 && temporary_ty
3395 .walk()
3396 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3397 {
3398 ControlFlow::Break(())
3399 } else {
3400 ControlFlow::Continue(())
3401 }
3402 })
3403 .is_break()
3404}
3405
3406pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3417 let expr_ty_is_adjusted = cx
3418 .typeck_results()
3419 .expr_adjustments(expr)
3420 .iter()
3421 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3423 if expr_ty_is_adjusted {
3424 return true;
3425 }
3426
3427 match expr.kind {
3430 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3431 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3432
3433 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3434 return false;
3435 }
3436
3437 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3438 let mut args_with_ty_param = {
3439 fn_sig
3440 .inputs()
3441 .skip_binder()
3442 .iter()
3443 .skip(self_arg_count)
3444 .zip(args)
3445 .filter_map(|(arg_ty, arg)| {
3446 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3447 Some(arg)
3448 } else {
3449 None
3450 }
3451 })
3452 };
3453 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3454 },
3455 ExprKind::Struct(qpath, _, _) => {
3457 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3458 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3459 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3460 return true;
3462 };
3463 v_def
3464 .fields
3465 .iter()
3466 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3467 } else {
3468 false
3469 }
3470 },
3471 ExprKind::Block(
3473 &Block {
3474 expr: Some(ret_expr), ..
3475 },
3476 _,
3477 )
3478 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3479
3480 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3482 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3484 ExprKind::If(_, then, maybe_else) => {
3486 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3487 },
3488 ExprKind::Match(_, arms, _) => arms
3489 .iter()
3490 .map(|arm| arm.body)
3491 .any(|body| expr_requires_coercion(cx, body)),
3492 _ => false,
3493 }
3494}
3495
3496pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3499 if let Some(hir_id) = path_to_local(expr)
3500 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3501 {
3502 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3503 } else if let ExprKind::Path(p) = &expr.kind
3504 && let Some(mutability) = cx
3505 .qpath_res(p, expr.hir_id)
3506 .opt_def_id()
3507 .and_then(|id| cx.tcx.static_mutability(id))
3508 {
3509 mutability == Mutability::Mut
3510 } else if let ExprKind::Field(parent, _) = expr.kind {
3511 is_mutable(cx, parent)
3512 } else {
3513 true
3514 }
3515}
3516
3517pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3520 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3521 return hir_ty;
3522 };
3523 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3524 && let Some(segment) = path.segments.last()
3525 && segment.ident.name == sym::Option
3526 && let Res::Def(DefKind::Enum, def_id) = segment.res
3527 && def_id == option_def_id
3528 && let [GenericArg::Type(arg_ty)] = segment.args().args
3529 {
3530 hir_ty = arg_ty.as_unambig_ty();
3531 }
3532 hir_ty
3533}
3534
3535pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3538 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3539 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3540 && let ctxt = expr.span.ctxt()
3541 && for_each_expr_without_closures(into_future_arg, |e| {
3542 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3543 })
3544 .is_none()
3545 {
3546 Some(into_future_arg)
3547 } else {
3548 None
3549 }
3550}
3551
3552pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3554 if let ExprKind::Call(fn_expr, []) = &expr.kind
3555 && let ExprKind::Path(qpath) = &fn_expr.kind
3556 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3557 {
3558 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3559 } else {
3560 false
3561 }
3562}
3563
3564pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3581 let enclosing_body_owner = cx
3582 .tcx
3583 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3584 let mut prev_id = expr.hir_id;
3585 let mut skip_until_id = None;
3586 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3587 if hir_id == enclosing_body_owner {
3588 return true;
3589 }
3590 if let Some(id) = skip_until_id {
3591 prev_id = hir_id;
3592 if id == hir_id {
3593 skip_until_id = None;
3594 }
3595 continue;
3596 }
3597 match node {
3598 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3599 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3600 Node::Expr(expr) => match expr.kind {
3601 ExprKind::Ret(_) => return true,
3602 ExprKind::If(_, then, opt_else)
3603 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3604 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3605 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3606 ExprKind::Break(
3607 Destination {
3608 target_id: Ok(target_id),
3609 ..
3610 },
3611 _,
3612 ) => skip_until_id = Some(target_id),
3613 _ => break,
3614 },
3615 _ => break,
3616 }
3617 prev_id = hir_id;
3618 }
3619
3620 false
3623}
3624
3625pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3628 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3629 matches!(
3630 adj.kind,
3631 Adjust::Deref(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny
3632 )
3633 })
3634}
3635
3636pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3638 matches!(
3639 expr.kind,
3640 ExprKind::Closure(Closure {
3641 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3642 CoroutineDesugaring::Async,
3643 CoroutineSource::Block
3644 )),
3645 ..
3646 })
3647 )
3648}
3649
3650pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3652 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3653}