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, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl,
106 ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
107 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::consts::{ConstEvalCtxt, Constant, mir_to_const};
130use crate::higher::Range;
131use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
132use crate::visitors::for_each_expr_without_closures;
133
134#[macro_export]
135macro_rules! extract_msrv_attr {
136 () => {
137 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
138 let sess = rustc_lint::LintContext::sess(cx);
139 self.msrv.check_attributes(sess, attrs);
140 }
141
142 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
143 let sess = rustc_lint::LintContext::sess(cx);
144 self.msrv.check_attributes_post(sess, attrs);
145 }
146 };
147}
148
149pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
172 while let Some(init) = path_to_local(expr)
173 .and_then(|id| find_binding_init(cx, id))
174 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
175 {
176 expr = init;
177 }
178 expr
179}
180
181pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
190 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
191 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
192 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
193 {
194 return local.init;
195 }
196 None
197}
198
199pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
203 for (_, node) in cx.tcx.hir_parent_iter(local) {
204 match node {
205 Node::Pat(..) | Node::PatField(..) => {},
206 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
207 _ => return true,
208 }
209 }
210
211 false
212}
213
214pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
225 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
226 cx.enclosing_body.is_some_and(|id| {
227 cx.tcx
228 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
229 .is_some()
230 })
231}
232
233pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
240 use rustc_hir::ConstContext::{Const, ConstFn, Static};
241 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
242 return false;
243 };
244 match ctx {
245 ConstFn => false,
246 Static(_) | Const { inline: _ } => true,
247 }
248}
249
250pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
253 if let Res::Def(DefKind::Ctor(..), id) = res
254 && let Some(lang_id) = cx.tcx.lang_items().get(lang_item)
255 && let Some(id) = cx.tcx.opt_parent(id)
256 {
257 id == lang_id
258 } else {
259 false
260 }
261}
262
263pub fn is_enum_variant_ctor(
265 cx: &LateContext<'_>,
266 enum_item: Symbol,
267 variant_name: Symbol,
268 ctor_call_id: DefId,
269) -> bool {
270 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
271 return false;
272 };
273
274 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
275 variants
276 .filter(|variant| variant.name == variant_name)
277 .filter_map(|variant| variant.ctor.as_ref())
278 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
279}
280
281pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
283 let did = match cx.tcx.def_kind(did) {
284 DefKind::Ctor(..) => cx.tcx.parent(did),
285 DefKind::Variant => match cx.tcx.opt_parent(did) {
287 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
288 _ => did,
289 },
290 _ => did,
291 };
292
293 cx.tcx.is_diagnostic_item(item, did)
294}
295
296pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
298 let did = match cx.tcx.def_kind(did) {
299 DefKind::Ctor(..) => cx.tcx.parent(did),
300 DefKind::Variant => match cx.tcx.opt_parent(did) {
302 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
303 _ => did,
304 },
305 _ => did,
306 };
307
308 cx.tcx.lang_items().get(item) == Some(did)
309}
310
311pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
312 matches!(
313 expr.kind,
314 ExprKind::Block(
315 Block {
316 stmts: [],
317 expr: None,
318 ..
319 },
320 _
321 ) | ExprKind::Tup([])
322 )
323}
324
325pub fn is_wild(pat: &Pat<'_>) -> bool {
327 matches!(pat.kind, PatKind::Wild)
328}
329
330pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
332 matches!(
333 arm.pat.kind,
334 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
335 if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone)
336 )
337}
338
339pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
341 match *qpath {
342 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
343 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
344 _ => false,
345 }
346}
347
348pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
350 if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
351 cx.tcx.trait_of_assoc(method_id).is_none()
352 } else {
353 false
354 }
355}
356
357pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
359 if let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
360 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
361 {
362 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
363 }
364 false
365}
366
367pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
369 if let Some(trait_did) = cx.tcx.trait_of_assoc(def_id) {
370 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
371 }
372 false
373}
374
375pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
377 cx.typeck_results()
378 .type_dependent_def_id(expr.hir_id)
379 .is_some_and(|did| is_diag_trait_item(cx, did, diag_item))
380}
381
382pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
384 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
385 && let ItemKind::Impl(imp) = item.kind
386 {
387 imp.of_trait.is_some()
388 } else {
389 false
390 }
391}
392
393pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
403 if let ExprKind::Path(ref qpath) = expr.kind {
404 cx.qpath_res(qpath, expr.hir_id)
405 .opt_def_id()
406 .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item))
407 } else {
408 false
409 }
410}
411
412pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
413 match *path {
414 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
415 QPath::TypeRelative(_, seg) => seg,
416 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
417 }
418}
419
420pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
421 last_path_segment(qpath)
422 .args
423 .map_or(&[][..], |a| a.args)
424 .iter()
425 .filter_map(|a| match a {
426 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
427 _ => None,
428 })
429}
430
431pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
434 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id))
435}
436
437pub fn is_path_diagnostic_item<'tcx>(
440 cx: &LateContext<'_>,
441 maybe_path: &impl MaybePath<'tcx>,
442 diag_item: Symbol,
443) -> bool {
444 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
445}
446
447pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
449 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
450 && let Res::Local(id) = path.res
451 {
452 return Some(id);
453 }
454 None
455}
456
457pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
460 path_to_local(expr) == Some(id)
461}
462
463pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
468 match expr.kind {
469 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
470 ExprKind::Path(QPath::Resolved(
471 _,
472 Path {
473 res: Res::Local(local), ..
474 },
475 )) => Some(*local),
476 _ => None,
477 }
478}
479
480pub trait MaybePath<'hir> {
481 fn hir_id(&self) -> HirId;
482 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
483}
484
485macro_rules! maybe_path {
486 ($ty:ident, $kind:ident) => {
487 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
488 fn hir_id(&self) -> HirId {
489 self.hir_id
490 }
491 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
492 match &self.kind {
493 hir::$kind::Path(qpath) => Some(qpath),
494 _ => None,
495 }
496 }
497 }
498 };
499}
500maybe_path!(Expr, ExprKind);
501impl<'hir> MaybePath<'hir> for Pat<'hir> {
502 fn hir_id(&self) -> HirId {
503 self.hir_id
504 }
505 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
506 match &self.kind {
507 PatKind::Expr(PatExpr {
508 kind: PatExprKind::Path(qpath),
509 ..
510 }) => Some(qpath),
511 _ => None,
512 }
513 }
514}
515maybe_path!(Ty, TyKind);
516
517pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
519 match maybe_path.qpath_opt() {
520 None => Res::Err,
521 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
522 }
523}
524
525pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
527 path_res(cx, maybe_path).opt_def_id()
528}
529
530pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
546 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
547 && let ItemKind::Impl(impl_) = &item.kind
548 && let Some(of_trait) = impl_.of_trait
549 {
550 return Some(&of_trait.trait_ref);
551 }
552 None
553}
554
555fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
563 let mut result = vec![];
564 let root = loop {
565 match e.kind {
566 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
567 result.push(e);
568 e = ep;
569 },
570 _ => break e,
571 }
572 };
573 result.reverse();
574 (result, root)
575}
576
577pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
579 cx.typeck_results()
580 .expr_adjustments(e)
581 .iter()
582 .find_map(|a| match a.kind {
583 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
584 Adjust::Deref(None) => None,
585 _ => Some(None),
586 })
587 .and_then(|x| x)
588}
589
590pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
593 let (s1, r1) = projection_stack(e1);
594 let (s2, r2) = projection_stack(e2);
595 if !eq_expr_value(cx, r1, r2) {
596 return true;
597 }
598 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
599 return false;
600 }
601
602 for (x1, x2) in zip(&s1, &s2) {
603 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
604 return false;
605 }
606
607 match (&x1.kind, &x2.kind) {
608 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
609 if i1 != i2 {
610 return true;
611 }
612 },
613 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
614 if !eq_expr_value(cx, i1, i2) {
615 return false;
616 }
617 },
618 _ => return false,
619 }
620 }
621 false
622}
623
624fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
627 let std_types_symbols = &[
628 sym::Vec,
629 sym::VecDeque,
630 sym::LinkedList,
631 sym::HashMap,
632 sym::BTreeMap,
633 sym::HashSet,
634 sym::BTreeSet,
635 sym::BinaryHeap,
636 ];
637
638 if let QPath::TypeRelative(_, method) = path
639 && method.ident.name == sym::new
640 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
641 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
642 {
643 return std_types_symbols.iter().any(|&symbol| {
644 cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
645 });
646 }
647 false
648}
649
650pub fn is_default_equivalent_call(
652 cx: &LateContext<'_>,
653 repl_func: &Expr<'_>,
654 whole_call_expr: Option<&Expr<'_>>,
655) -> bool {
656 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
657 && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
658 && (is_diag_trait_item(cx, repl_def_id, sym::Default)
659 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath))
660 {
661 return true;
662 }
663
664 let Some(e) = whole_call_expr else { return false };
667 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
668 return false;
669 };
670 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
671 return false;
672 };
673 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
674 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
675 cx.tcx.lifetimes.re_erased.into()
676 } else if param.index == 0 && param.name == kw::SelfUpper {
677 ty.into()
678 } else {
679 param.to_error(cx.tcx)
680 }
681 });
682 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
683
684 let Ok(Some(instance)) = instance else { return false };
685 if let rustc_ty::InstanceKind::Item(def) = instance.def
686 && !cx.tcx.is_mir_available(def)
687 {
688 return false;
689 }
690 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
691 return false;
692 };
693 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
694 return false;
695 };
696
697 let body = cx.tcx.instance_mir(instance.def);
703 for block_data in body.basic_blocks.iter() {
704 if block_data.statements.len() == 1
705 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
706 && assign.0.local == RETURN_PLACE
707 && let Rvalue::Aggregate(kind, _places) = &assign.1
708 && let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
709 && let def = cx.tcx.adt_def(did)
710 && let variant = &def.variant(*variant_index)
711 && variant.fields.is_empty()
712 && let Some((_, did)) = variant.ctor
713 && did == repl_def_id
714 {
715 return true;
716 } else if block_data.statements.is_empty()
717 && let Some(term) = &block_data.terminator
718 {
719 match &term.kind {
720 TerminatorKind::Call {
721 func: Operand::Constant(c),
722 ..
723 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
724 && *did == repl_def_id =>
725 {
726 return true;
727 },
728 TerminatorKind::TailCall {
729 func: Operand::Constant(c),
730 ..
731 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
732 && *did == repl_def_id =>
733 {
734 return true;
735 },
736 _ => {},
737 }
738 }
739 }
740 false
741}
742
743pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
747 match &e.kind {
748 ExprKind::Lit(lit) => match lit.node {
749 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
750 LitKind::Str(s, _) => s.is_empty(),
751 _ => false,
752 },
753 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
754 ExprKind::Repeat(x, len) => {
755 if let ConstArgKind::Anon(anon_const) = len.kind
756 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
757 && let LitKind::Int(v, _) = const_lit.node
758 && v <= 32
759 && is_default_equivalent(cx, x)
760 {
761 true
762 } else {
763 false
764 }
765 },
766 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
767 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
768 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
769 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
770 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
771 _ => false,
772 }
773}
774
775fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
776 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
777 && seg.ident.name == sym::from
778 {
779 match arg.kind {
780 ExprKind::Lit(hir::Lit {
781 node: LitKind::Str(sym, _),
782 ..
783 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
784 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
785 ExprKind::Repeat(_, len) => {
786 if let ConstArgKind::Anon(anon_const) = len.kind
787 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
788 && let LitKind::Int(v, _) = const_lit.node
789 {
790 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
791 }
792 },
793 _ => (),
794 }
795 }
796 false
797}
798
799pub fn can_move_expr_to_closure_no_visit<'tcx>(
831 cx: &LateContext<'tcx>,
832 expr: &'tcx Expr<'_>,
833 loop_ids: &[HirId],
834 ignore_locals: &HirIdSet,
835) -> bool {
836 match expr.kind {
837 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
838 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
839 if loop_ids.contains(&id) =>
840 {
841 true
842 },
843 ExprKind::Break(..)
844 | ExprKind::Continue(_)
845 | ExprKind::Ret(_)
846 | ExprKind::Yield(..)
847 | ExprKind::InlineAsm(_) => false,
848 ExprKind::Field(
851 &Expr {
852 hir_id,
853 kind:
854 ExprKind::Path(QPath::Resolved(
855 _,
856 Path {
857 res: Res::Local(local_id),
858 ..
859 },
860 )),
861 ..
862 },
863 _,
864 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
865 false
867 },
868 _ => true,
869 }
870}
871
872#[derive(Debug, Clone, Copy, PartialEq, Eq)]
874pub enum CaptureKind {
875 Value,
876 Use,
877 Ref(Mutability),
878}
879impl CaptureKind {
880 pub fn is_imm_ref(self) -> bool {
881 self == Self::Ref(Mutability::Not)
882 }
883}
884impl std::ops::BitOr for CaptureKind {
885 type Output = Self;
886 fn bitor(self, rhs: Self) -> Self::Output {
887 match (self, rhs) {
888 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
889 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
890 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
891 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
892 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
893 }
894 }
895}
896impl std::ops::BitOrAssign for CaptureKind {
897 fn bitor_assign(&mut self, rhs: Self) {
898 *self = *self | rhs;
899 }
900}
901
902pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
908 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
909 let mut capture = CaptureKind::Ref(Mutability::Not);
910 pat.each_binding_or_first(&mut |_, id, span, _| match cx
911 .typeck_results()
912 .extract_binding_mode(cx.sess(), id, span)
913 .0
914 {
915 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
916 capture = CaptureKind::Value;
917 },
918 ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
919 capture = CaptureKind::Ref(Mutability::Mut);
920 },
921 _ => (),
922 });
923 capture
924 }
925
926 debug_assert!(matches!(
927 e.kind,
928 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
929 ));
930
931 let mut child_id = e.hir_id;
932 let mut capture = CaptureKind::Value;
933 let mut capture_expr_ty = e;
934
935 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
936 if let [
937 Adjustment {
938 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
939 target,
940 },
941 ref adjust @ ..,
942 ] = *cx
943 .typeck_results()
944 .adjustments()
945 .get(child_id)
946 .map_or(&[][..], |x| &**x)
947 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
948 *adjust.last().map_or(target, |a| a.target).kind()
949 {
950 return CaptureKind::Ref(mutability);
951 }
952
953 match parent {
954 Node::Expr(e) => match e.kind {
955 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
956 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
957 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
958 return CaptureKind::Ref(Mutability::Mut);
959 },
960 ExprKind::Field(..) => {
961 if capture == CaptureKind::Value {
962 capture_expr_ty = e;
963 }
964 },
965 ExprKind::Let(let_expr) => {
966 let mutability = match pat_capture_kind(cx, let_expr.pat) {
967 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
968 CaptureKind::Ref(m) => m,
969 };
970 return CaptureKind::Ref(mutability);
971 },
972 ExprKind::Match(_, arms, _) => {
973 let mut mutability = Mutability::Not;
974 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
975 match capture {
976 CaptureKind::Value | CaptureKind::Use => break,
977 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
978 CaptureKind::Ref(Mutability::Not) => (),
979 }
980 }
981 return CaptureKind::Ref(mutability);
982 },
983 _ => break,
984 },
985 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
986 CaptureKind::Value | CaptureKind::Use => break,
987 capture @ CaptureKind::Ref(_) => return capture,
988 },
989 _ => break,
990 }
991
992 child_id = parent_id;
993 }
994
995 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
996 CaptureKind::Ref(Mutability::Not)
998 } else {
999 capture
1000 }
1001}
1002
1003pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
1006 struct V<'cx, 'tcx> {
1007 cx: &'cx LateContext<'tcx>,
1008 loops: Vec<HirId>,
1010 locals: HirIdSet,
1012 allow_closure: bool,
1014 captures: HirIdMap<CaptureKind>,
1017 }
1018 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1019 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1020 if !self.allow_closure {
1021 return;
1022 }
1023
1024 match e.kind {
1025 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1026 if !self.locals.contains(&l) {
1027 let cap = capture_local_usage(self.cx, e);
1028 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1029 }
1030 },
1031 ExprKind::Closure(closure) => {
1032 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
1033 let local_id = match capture.place.base {
1034 PlaceBase::Local(id) => id,
1035 PlaceBase::Upvar(var) => var.var_path.hir_id,
1036 _ => continue,
1037 };
1038 if !self.locals.contains(&local_id) {
1039 let capture = match capture.info.capture_kind {
1040 UpvarCapture::ByValue => CaptureKind::Value,
1041 UpvarCapture::ByUse => CaptureKind::Use,
1042 UpvarCapture::ByRef(kind) => match kind {
1043 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
1044 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
1045 CaptureKind::Ref(Mutability::Mut)
1046 },
1047 },
1048 };
1049 self.captures
1050 .entry(local_id)
1051 .and_modify(|e| *e |= capture)
1052 .or_insert(capture);
1053 }
1054 }
1055 },
1056 ExprKind::Loop(b, ..) => {
1057 self.loops.push(e.hir_id);
1058 self.visit_block(b);
1059 self.loops.pop();
1060 },
1061 _ => {
1062 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1063 walk_expr(self, e);
1064 },
1065 }
1066 }
1067
1068 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1069 p.each_binding_or_first(&mut |_, id, _, _| {
1070 self.locals.insert(id);
1071 });
1072 }
1073 }
1074
1075 let mut v = V {
1076 cx,
1077 loops: Vec::new(),
1078 locals: HirIdSet::default(),
1079 allow_closure: true,
1080 captures: HirIdMap::default(),
1081 };
1082 v.visit_expr(expr);
1083 v.allow_closure.then_some(v.captures)
1084}
1085
1086pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1088
1089pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1092 let mut method_names = Vec::with_capacity(max_depth);
1093 let mut arg_lists = Vec::with_capacity(max_depth);
1094 let mut spans = Vec::with_capacity(max_depth);
1095
1096 let mut current = expr;
1097 for _ in 0..max_depth {
1098 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1099 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1100 break;
1101 }
1102 method_names.push(path.ident.name);
1103 arg_lists.push((*receiver, &**args));
1104 spans.push(path.ident.span);
1105 current = receiver;
1106 } else {
1107 break;
1108 }
1109 }
1110
1111 (method_names, arg_lists, spans)
1112}
1113
1114pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1121 let mut current = expr;
1122 let mut matched = Vec::with_capacity(methods.len());
1123 for method_name in methods.iter().rev() {
1124 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1126 if path.ident.name == *method_name {
1127 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1128 return None;
1129 }
1130 matched.push((receiver, args)); current = receiver; } else {
1133 return None;
1134 }
1135 } else {
1136 return None;
1137 }
1138 }
1139 matched.reverse();
1141 Some(matched)
1142}
1143
1144pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1146 cx.tcx
1147 .entry_fn(())
1148 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1149}
1150
1151pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1153 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1154 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1155}
1156
1157pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1159 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1160 match cx.tcx.hir_node_by_def_id(parent_id) {
1161 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1162 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1163 _ => None,
1164 }
1165}
1166
1167pub struct ContainsName<'a, 'tcx> {
1168 pub cx: &'a LateContext<'tcx>,
1169 pub name: Symbol,
1170}
1171
1172impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1173 type Result = ControlFlow<()>;
1174 type NestedFilter = nested_filter::OnlyBodies;
1175
1176 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1177 if self.name == name {
1178 ControlFlow::Break(())
1179 } else {
1180 ControlFlow::Continue(())
1181 }
1182 }
1183
1184 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1185 self.cx.tcx
1186 }
1187}
1188
1189pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1191 let mut cn = ContainsName { cx, name };
1192 cn.visit_expr(expr).is_break()
1193}
1194
1195pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1197 for_each_expr_without_closures(expr, |e| {
1198 if matches!(e.kind, ExprKind::Ret(..)) {
1199 ControlFlow::Break(())
1200 } else {
1201 ControlFlow::Continue(())
1202 }
1203 })
1204 .is_some()
1205}
1206
1207pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1209 get_parent_expr_for_hir(cx, e.hir_id)
1210}
1211
1212pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1215 match cx.tcx.parent_hir_node(hir_id) {
1216 Node::Expr(parent) => Some(parent),
1217 _ => None,
1218 }
1219}
1220
1221pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1223 let enclosing_node = cx
1224 .tcx
1225 .hir_get_enclosing_scope(hir_id)
1226 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1227 enclosing_node.and_then(|node| match node {
1228 Node::Block(block) => Some(block),
1229 Node::Item(&Item {
1230 kind: ItemKind::Fn { body: eid, .. },
1231 ..
1232 })
1233 | Node::ImplItem(&ImplItem {
1234 kind: ImplItemKind::Fn(_, eid),
1235 ..
1236 })
1237 | Node::TraitItem(&TraitItem {
1238 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1239 ..
1240 }) => match cx.tcx.hir_body(eid).value.kind {
1241 ExprKind::Block(block, _) => Some(block),
1242 _ => None,
1243 },
1244 _ => None,
1245 })
1246}
1247
1248pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1250 cx: &LateContext<'tcx>,
1251 expr: &Expr<'_>,
1252) -> Option<&'tcx Expr<'tcx>> {
1253 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1254 match node {
1255 Node::Expr(e) => match e.kind {
1256 ExprKind::Closure { .. }
1257 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1258 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1259
1260 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1262 _ => (),
1263 },
1264 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1265 _ => break,
1266 }
1267 }
1268 None
1269}
1270
1271pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1273 match tcx.hir_parent_iter(id).next() {
1274 Some((
1275 _,
1276 Node::Item(Item {
1277 kind: ItemKind::Impl(imp),
1278 ..
1279 }),
1280 )) => Some(imp),
1281 _ => None,
1282 }
1283}
1284
1285pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1296 while let ExprKind::Block(
1297 Block {
1298 stmts: [],
1299 expr: Some(inner),
1300 rules: BlockCheckMode::DefaultBlock,
1301 ..
1302 },
1303 _,
1304 ) = expr.kind
1305 {
1306 expr = inner;
1307 }
1308 expr
1309}
1310
1311pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1322 while let ExprKind::Block(
1323 Block {
1324 stmts: [],
1325 expr: Some(inner),
1326 rules: BlockCheckMode::DefaultBlock,
1327 ..
1328 }
1329 | Block {
1330 stmts:
1331 [
1332 Stmt {
1333 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1334 ..
1335 },
1336 ],
1337 expr: None,
1338 rules: BlockCheckMode::DefaultBlock,
1339 ..
1340 },
1341 _,
1342 ) = expr.kind
1343 {
1344 expr = inner;
1345 }
1346 expr
1347}
1348
1349pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1351 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1352 match iter.next() {
1353 Some((
1354 _,
1355 Node::Expr(Expr {
1356 kind: ExprKind::If(_, _, Some(else_expr)),
1357 ..
1358 }),
1359 )) => else_expr.hir_id == expr.hir_id,
1360 _ => false,
1361 }
1362}
1363
1364pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1367 let mut child_id = expr.hir_id;
1368 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1369 if let Node::LetStmt(LetStmt {
1370 init: Some(init),
1371 els: Some(els),
1372 ..
1373 }) = node
1374 && (init.hir_id == child_id || els.hir_id == child_id)
1375 {
1376 return true;
1377 }
1378
1379 child_id = parent_id;
1380 }
1381
1382 false
1383}
1384
1385pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1387 let mut child_id = expr.hir_id;
1388 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1389 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1390 && els.hir_id == child_id
1391 {
1392 return true;
1393 }
1394
1395 child_id = parent_id;
1396 }
1397
1398 false
1399}
1400
1401pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1416 let ty = cx.typeck_results().expr_ty(expr);
1417 if let Some(Range { start, end, limits }) = Range::hir(expr) {
1418 let start_is_none_or_min = start.is_none_or(|start| {
1419 if let rustc_ty::Adt(_, subst) = ty.kind()
1420 && let bnd_ty = subst.type_at(0)
1421 && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx)
1422 && let Some(min_const) = mir_to_const(cx.tcx, min_const)
1423 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1424 {
1425 start_const == min_const
1426 } else {
1427 false
1428 }
1429 });
1430 let end_is_none_or_max = end.is_none_or(|end| match limits {
1431 RangeLimits::Closed => {
1432 if let rustc_ty::Adt(_, subst) = ty.kind()
1433 && let bnd_ty = subst.type_at(0)
1434 && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx)
1435 && let Some(max_const) = mir_to_const(cx.tcx, max_const)
1436 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1437 {
1438 end_const == max_const
1439 } else {
1440 false
1441 }
1442 },
1443 RangeLimits::HalfOpen => {
1444 if let Some(container_path) = container_path
1445 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1446 && name.ident.name == sym::len
1447 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1448 {
1449 container_path.res == path.res
1450 } else {
1451 false
1452 }
1453 },
1454 });
1455 return start_is_none_or_min && end_is_none_or_max;
1456 }
1457 false
1458}
1459
1460pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1463 if is_integer_literal(e, value) {
1464 return true;
1465 }
1466 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1467 if let Some(Constant::Int(v)) =
1468 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1469 {
1470 return value == v;
1471 }
1472 false
1473}
1474
1475pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1477 if let ExprKind::Lit(spanned) = expr.kind
1479 && let LitKind::Int(v, _) = spanned.node
1480 {
1481 return v == value;
1482 }
1483 false
1484}
1485
1486pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1488 if let ExprKind::Lit(spanned) = expr.kind
1489 && let LitKind::Float(v, _) = spanned.node
1490 {
1491 v.as_str().parse() == Ok(value)
1492 } else {
1493 false
1494 }
1495}
1496
1497pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1505 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1506}
1507
1508#[must_use]
1512pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1513 loop {
1514 if span.from_expansion() {
1515 let data = span.ctxt().outer_expn_data();
1516 let new_span = data.call_site;
1517
1518 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1519 && mac_name == name
1520 {
1521 return Some(new_span);
1522 }
1523
1524 span = new_span;
1525 } else {
1526 return None;
1527 }
1528 }
1529}
1530
1531#[must_use]
1542pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1543 if span.from_expansion() {
1544 let data = span.ctxt().outer_expn_data();
1545 let new_span = data.call_site;
1546
1547 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1548 && mac_name == name
1549 {
1550 return Some(new_span);
1551 }
1552 }
1553
1554 None
1555}
1556
1557pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1559 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1560 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1561}
1562
1563pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1565 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1566 cx.tcx.instantiate_bound_regions_with_erased(arg)
1567}
1568
1569pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1571 if let ExprKind::Call(fun, _) = expr.kind
1572 && let ExprKind::Path(ref qp) = fun.kind
1573 {
1574 let res = cx.qpath_res(qp, fun.hir_id);
1575 return match res {
1576 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1577 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1578 _ => false,
1579 };
1580 }
1581 false
1582}
1583
1584pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1587 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1588 !matches!(
1589 cx.qpath_res(qpath, id),
1590 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1591 )
1592 }
1593
1594 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1595 i.into_iter().any(|pat| is_refutable(cx, pat))
1596 }
1597
1598 match pat.kind {
1599 PatKind::Missing => unreachable!(),
1600 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1602 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1603 PatKind::Expr(PatExpr {
1604 kind: PatExprKind::Path(qpath),
1605 hir_id,
1606 ..
1607 }) => is_qpath_refutable(cx, qpath, *hir_id),
1608 PatKind::Or(pats) => {
1609 are_refutable(cx, pats)
1611 },
1612 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1613 PatKind::Struct(ref qpath, fields, _) => {
1614 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1615 },
1616 PatKind::TupleStruct(ref qpath, pats, _) => {
1617 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1618 },
1619 PatKind::Slice(head, middle, tail) => {
1620 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1621 rustc_ty::Slice(..) => {
1622 !head.is_empty() || middle.is_none() || !tail.is_empty()
1624 },
1625 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1626 _ => {
1627 true
1629 },
1630 }
1631 },
1632 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1633 }
1634}
1635
1636pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1639 if let PatKind::Or(pats) = pat.kind {
1640 pats.iter().for_each(f);
1641 } else {
1642 f(pat);
1643 }
1644}
1645
1646pub fn is_self(slf: &Param<'_>) -> bool {
1647 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1648 name.name == kw::SelfLower
1649 } else {
1650 false
1651 }
1652}
1653
1654pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1655 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1656 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1657 {
1658 return true;
1659 }
1660 false
1661}
1662
1663pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1664 (0..decl.inputs.len()).map(move |i| &body.params[i])
1665}
1666
1667pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1670 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1671 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1672 && ddpos.as_opt_usize().is_none()
1673 && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk)
1674 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1675 && path_to_local_id(arm.body, hir_id)
1676 {
1677 return true;
1678 }
1679 false
1680 }
1681
1682 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1683 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1684 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1685 } else {
1686 false
1687 }
1688 }
1689
1690 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1691 if let MatchSource::TryDesugar(_) = *source {
1693 return Some(expr);
1694 }
1695
1696 if arms.len() == 2
1697 && arms[0].guard.is_none()
1698 && arms[1].guard.is_none()
1699 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1700 {
1701 return Some(expr);
1702 }
1703 }
1704
1705 None
1706}
1707
1708pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1718 let mut suppress_lint = false;
1719
1720 for id in ids {
1721 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1722 if let Some(expectation) = lint_id {
1723 cx.fulfill_expectation(expectation);
1724 }
1725
1726 match level {
1727 Level::Allow | Level::Expect => suppress_lint = true,
1728 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1729 }
1730 }
1731
1732 suppress_lint
1733}
1734
1735pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1743 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1744}
1745
1746pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1747 while let PatKind::Ref(subpat, _) = pat.kind {
1748 pat = subpat;
1749 }
1750 pat
1751}
1752
1753pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1754 Integer::from_int_ty(&tcx, ity).size().bits()
1755}
1756
1757#[expect(clippy::cast_possible_wrap)]
1758pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1760 let amt = 128 - int_bits(tcx, ity);
1761 ((u as i128) << amt) >> amt
1762}
1763
1764#[expect(clippy::cast_sign_loss)]
1765pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1767 let amt = 128 - int_bits(tcx, ity);
1768 ((u as u128) << amt) >> amt
1769}
1770
1771pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1773 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1774 let amt = 128 - bits;
1775 (u << amt) >> amt
1776}
1777
1778pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1779 attrs.iter().any(|attr| attr.has_name(symbol))
1780}
1781
1782pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1783 find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr { .. })
1784}
1785
1786pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1787 let mut prev_enclosing_node = None;
1788 let mut enclosing_node = node;
1789 while Some(enclosing_node) != prev_enclosing_node {
1790 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1791 return true;
1792 }
1793 prev_enclosing_node = Some(enclosing_node);
1794 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1795 }
1796
1797 false
1798}
1799
1800pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1803 tcx.hir_parent_owner_iter(id)
1804 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1805 .any(|(id, _)| {
1806 find_attr!(
1807 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1808 AttributeKind::AutomaticallyDerived(..)
1809 )
1810 })
1811}
1812
1813pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1815 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1818}
1819
1820pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1825 let mut conds = Vec::new();
1826 let mut blocks: Vec<&Block<'_>> = Vec::new();
1827
1828 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1829 conds.push(cond);
1830 if let ExprKind::Block(block, _) = then.kind {
1831 blocks.push(block);
1832 } else {
1833 panic!("ExprKind::If node is not an ExprKind::Block");
1834 }
1835
1836 if let Some(else_expr) = r#else {
1837 expr = else_expr;
1838 } else {
1839 break;
1840 }
1841 }
1842
1843 if !blocks.is_empty()
1845 && let ExprKind::Block(block, _) = expr.kind
1846 {
1847 blocks.push(block);
1848 }
1849
1850 (conds, blocks)
1851}
1852
1853pub fn is_async_fn(kind: FnKind<'_>) -> bool {
1855 match kind {
1856 FnKind::ItemFn(_, _, header) => header.asyncness.is_async(),
1857 FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
1858 FnKind::Closure => false,
1859 }
1860}
1861
1862pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1864 if let ExprKind::Closure(&Closure {
1865 body,
1866 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1867 ..
1868 }) = expr.kind
1869 && let ExprKind::Block(
1870 Block {
1871 expr:
1872 Some(Expr {
1873 kind: ExprKind::DropTemps(inner_expr),
1874 ..
1875 }),
1876 ..
1877 },
1878 _,
1879 ) = tcx.hir_body(body).value.kind
1880 {
1881 Some(inner_expr)
1882 } else {
1883 None
1884 }
1885}
1886
1887pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1889 get_async_closure_expr(tcx, body.value)
1890}
1891
1892pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1894 let did = match expr.kind {
1895 ExprKind::Call(path, _) => {
1896 if let ExprKind::Path(ref qpath) = path.kind
1897 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1898 {
1899 Some(did)
1900 } else {
1901 None
1902 }
1903 },
1904 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1905 _ => None,
1906 };
1907
1908 did.is_some_and(|did| find_attr!(cx.tcx.get_all_attrs(did), AttributeKind::MustUse { .. }))
1909}
1910
1911fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1923 let [param] = func.params else {
1924 return false;
1925 };
1926
1927 let mut expr = func.value;
1928 loop {
1929 match expr.kind {
1930 ExprKind::Block(
1931 &Block {
1932 stmts: [],
1933 expr: Some(e),
1934 ..
1935 },
1936 _,
1937 )
1938 | ExprKind::Ret(Some(e)) => expr = e,
1939 ExprKind::Block(
1940 &Block {
1941 stmts: [stmt],
1942 expr: None,
1943 ..
1944 },
1945 _,
1946 ) => {
1947 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1948 && let ExprKind::Ret(Some(ret_val)) = e.kind
1949 {
1950 expr = ret_val;
1951 } else {
1952 return false;
1953 }
1954 },
1955 _ => return is_expr_identity_of_pat(cx, param.pat, expr, true),
1956 }
1957 }
1958}
1959
1960pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1970 if cx
1971 .typeck_results()
1972 .pat_binding_modes()
1973 .get(pat.hir_id)
1974 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
1975 {
1976 return false;
1980 }
1981
1982 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1984
1985 match (pat.kind, expr.kind) {
1986 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1987 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1988 },
1989 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1990 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1991 },
1992 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1993 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1994 {
1995 zip(pats, tup).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1996 },
1997 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1998 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1999 },
2000 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
2001 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
2002 {
2003 if let ExprKind::Path(ident) = &ident.kind
2005 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
2006 && zip(field_pats, fields).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr,by_hir))
2008 {
2009 true
2010 } else {
2011 false
2012 }
2013 },
2014 (PatKind::Struct(pat_ident, field_pats, false), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
2015 if field_pats.len() == fields.len() =>
2016 {
2017 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
2019 && field_pats.iter().all(|field_pat| {
2021 fields.iter().any(|field| {
2022 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
2023 })
2024 })
2025 },
2026 _ => false,
2027 }
2028}
2029
2030pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2035 match expr.kind {
2036 ExprKind::Closure(&Closure { body, fn_decl, .. })
2037 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
2038 {
2039 is_body_identity_function(cx, cx.tcx.hir_body(body))
2040 },
2041 ExprKind::Path(QPath::Resolved(_, path))
2042 if path.segments.iter().all(|seg| seg.infer_args)
2043 && let Some(did) = path.res.opt_def_id() =>
2044 {
2045 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
2046 },
2047 _ => false,
2048 }
2049}
2050
2051pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2060 match expr.kind {
2061 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
2062 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
2063 }
2064}
2065
2066pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2069 let mut child_id = expr.hir_id;
2070 let mut iter = tcx.hir_parent_iter(child_id);
2071 loop {
2072 match iter.next() {
2073 None => break None,
2074 Some((id, Node::Block(_))) => child_id = id,
2075 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2076 Some((_, Node::Expr(expr))) => match expr.kind {
2077 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2078 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2079 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2080 _ => break Some((Node::Expr(expr), child_id)),
2081 },
2082 Some((_, node)) => break Some((node, child_id)),
2083 }
2084 }
2085}
2086
2087pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2089 !matches!(
2090 get_expr_use_or_unification_node(tcx, expr),
2091 None | Some((
2092 Node::Stmt(Stmt {
2093 kind: StmtKind::Expr(_)
2094 | StmtKind::Semi(_)
2095 | StmtKind::Let(LetStmt {
2096 pat: Pat {
2097 kind: PatKind::Wild,
2098 ..
2099 },
2100 ..
2101 }),
2102 ..
2103 }),
2104 _
2105 ))
2106 )
2107}
2108
2109pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2111 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2112}
2113
2114pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2118 !expr.is_place_expr(|base| {
2119 cx.typeck_results()
2120 .adjustments()
2121 .get(base.hir_id)
2122 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2123 })
2124}
2125
2126pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2127 if !is_no_std_crate(cx) {
2128 Some("std")
2129 } else if !is_no_core_crate(cx) {
2130 Some("core")
2131 } else {
2132 None
2133 }
2134}
2135
2136pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2137 cx.tcx
2138 .hir_attrs(hir::CRATE_HIR_ID)
2139 .iter()
2140 .any(|attr| attr.has_name(sym::no_std))
2141}
2142
2143pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2144 cx.tcx
2145 .hir_attrs(hir::CRATE_HIR_ID)
2146 .iter()
2147 .any(|attr| attr.has_name(sym::no_core))
2148}
2149
2150pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2160 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2161 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2162 } else {
2163 false
2164 }
2165}
2166
2167pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2177 use rustc_trait_selection::traits;
2178 let predicates = cx
2179 .tcx
2180 .predicates_of(did)
2181 .predicates
2182 .iter()
2183 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2184 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2185}
2186
2187pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2189 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2190}
2191
2192pub fn fn_def_id_with_node_args<'tcx>(
2195 cx: &LateContext<'tcx>,
2196 expr: &Expr<'_>,
2197) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2198 let typeck = cx.typeck_results();
2199 match &expr.kind {
2200 ExprKind::MethodCall(..) => Some((
2201 typeck.type_dependent_def_id(expr.hir_id)?,
2202 typeck.node_args(expr.hir_id),
2203 )),
2204 ExprKind::Call(
2205 Expr {
2206 kind: ExprKind::Path(qpath),
2207 hir_id: path_hir_id,
2208 ..
2209 },
2210 ..,
2211 ) => {
2212 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2215 typeck.qpath_res(qpath, *path_hir_id)
2216 {
2217 Some((id, typeck.node_args(*path_hir_id)))
2218 } else {
2219 None
2220 }
2221 },
2222 _ => None,
2223 }
2224}
2225
2226pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2231 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2232 let expr_kind = expr_type.kind();
2233 let is_primitive = match expr_kind {
2234 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2235 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2236 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2237 is_recursively_primitive_type(*element_type)
2238 } else {
2239 unreachable!()
2240 }
2241 },
2242 _ => false,
2243 };
2244
2245 if is_primitive {
2246 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2249 rustc_ty::Slice(..) => return Some("slice".into()),
2250 rustc_ty::Array(..) => return Some("array".into()),
2251 rustc_ty::Tuple(..) => return Some("tuple".into()),
2252 _ => {
2253 let refs_peeled = expr_type.peel_refs();
2256 return Some(refs_peeled.walk().last().unwrap().to_string());
2257 },
2258 }
2259 }
2260 None
2261}
2262
2263pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2271where
2272 Hash: FnMut(&T) -> u64,
2273 Eq: FnMut(&T, &T) -> bool,
2274{
2275 match exprs {
2276 [a, b] if eq(a, b) => return vec![vec![a, b]],
2277 _ if exprs.len() <= 2 => return vec![],
2278 _ => {},
2279 }
2280
2281 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2282
2283 for expr in exprs {
2284 match buckets.entry(hash(expr)) {
2285 indexmap::map::Entry::Occupied(mut o) => {
2286 let bucket = o.get_mut();
2287 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2288 Some(group) => group.push(expr),
2289 None => bucket.push(vec![expr]),
2290 }
2291 },
2292 indexmap::map::Entry::Vacant(v) => {
2293 v.insert(vec![vec![expr]]);
2294 },
2295 }
2296 }
2297
2298 buckets
2299 .into_values()
2300 .flatten()
2301 .filter(|group| group.len() > 1)
2302 .collect()
2303}
2304
2305pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2308 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2309 if let PatKind::Ref(pat, _) = pat.kind {
2310 peel(pat, count + 1)
2311 } else {
2312 (pat, count)
2313 }
2314 }
2315 peel(pat, 0)
2316}
2317
2318pub fn peel_hir_expr_while<'tcx>(
2320 mut expr: &'tcx Expr<'tcx>,
2321 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2322) -> &'tcx Expr<'tcx> {
2323 while let Some(e) = f(expr) {
2324 expr = e;
2325 }
2326 expr
2327}
2328
2329pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2332 let mut remaining = count;
2333 let e = peel_hir_expr_while(expr, |e| match e.kind {
2334 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2335 remaining -= 1;
2336 Some(e)
2337 },
2338 _ => None,
2339 });
2340 (e, count - remaining)
2341}
2342
2343pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2346 let mut count: usize = 0;
2347 let mut curr_expr = expr;
2348 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2349 count = count.wrapping_add(1);
2350 curr_expr = local_expr;
2351 }
2352 (curr_expr, count)
2353}
2354
2355pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2358 let mut count = 0;
2359 let e = peel_hir_expr_while(expr, |e| match e.kind {
2360 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2361 count += 1;
2362 Some(e)
2363 },
2364 _ => None,
2365 });
2366 (e, count)
2367}
2368
2369pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2372 let mut count = 0;
2373 loop {
2374 match &ty.kind {
2375 TyKind::Ref(_, ref_ty) => {
2376 ty = ref_ty.ty;
2377 count += 1;
2378 },
2379 _ => break (ty, count),
2380 }
2381 }
2382}
2383
2384pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2387 let mut count = 0;
2388 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2389 ty = *dest_ty;
2390 count += 1;
2391 }
2392 (ty, count)
2393}
2394
2395pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2398 loop {
2399 match expr.kind {
2400 ExprKind::AddrOf(_, _, e) => expr = e,
2401 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2402 _ => break,
2403 }
2404 }
2405 expr
2406}
2407
2408pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2409 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2410 && let Res::Def(_, def_id) = path.res
2411 {
2412 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2413 }
2414 false
2415}
2416
2417static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2418
2419fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2422 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2423 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2424 let value = map.entry(module);
2425 match value {
2426 Entry::Occupied(entry) => f(entry.get()),
2427 Entry::Vacant(entry) => {
2428 let mut names = Vec::new();
2429 for id in tcx.hir_module_free_items(module) {
2430 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2431 && let item = tcx.hir_item(id)
2432 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2433 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2434 && let Res::Def(DefKind::Struct, _) = path.res
2436 {
2437 let has_test_marker = tcx
2438 .hir_attrs(item.hir_id())
2439 .iter()
2440 .any(|a| a.has_name(sym::rustc_test_marker));
2441 if has_test_marker {
2442 names.push(ident.name);
2443 }
2444 }
2445 }
2446 names.sort_unstable();
2447 f(entry.insert(names))
2448 },
2449 }
2450}
2451
2452pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2456 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2457 let node = tcx.hir_node(id);
2458 once((id, node))
2459 .chain(tcx.hir_parent_iter(id))
2460 .any(|(_id, node)| {
2463 if let Node::Item(item) = node
2464 && let ItemKind::Fn { ident, .. } = item.kind
2465 {
2466 return names.binary_search(&ident.name).is_ok();
2469 }
2470 false
2471 })
2472 })
2473}
2474
2475pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2482 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2483 if let Node::Item(item) = tcx.hir_node(id)
2484 && let ItemKind::Fn { ident, .. } = item.kind
2485 {
2486 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2487 names.binary_search(&ident.name).is_ok()
2488 })
2489 } else {
2490 false
2491 }
2492}
2493
2494pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2499 tcx.hir_attrs(id).iter().any(|attr| {
2500 if attr.has_name(sym::cfg_trace)
2501 && let Some(items) = attr.meta_item_list()
2502 && let [item] = &*items
2503 && item.has_name(sym::test)
2504 {
2505 true
2506 } else {
2507 false
2508 }
2509 })
2510}
2511
2512pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2514 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2515}
2516
2517pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2519 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2520}
2521
2522pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2524 tcx.has_attr(def_id, sym::cfg_trace)
2525 || tcx
2526 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2527 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2528 .any(|attr| attr.has_name(sym::cfg_trace))
2529}
2530
2531pub fn walk_to_expr_usage<'tcx, T>(
2542 cx: &LateContext<'tcx>,
2543 e: &Expr<'tcx>,
2544 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2545) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2546 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2547 let mut child_id = e.hir_id;
2548
2549 while let Some((parent_id, parent)) = iter.next() {
2550 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2551 return Some(ControlFlow::Break(x));
2552 }
2553 let parent_expr = match parent {
2554 Node::Expr(e) => e,
2555 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2556 child_id = parent_id;
2557 continue;
2558 },
2559 Node::Arm(a) if a.body.hir_id == child_id => {
2560 child_id = parent_id;
2561 continue;
2562 },
2563 _ => return Some(ControlFlow::Continue((parent, child_id))),
2564 };
2565 match parent_expr.kind {
2566 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2567 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2568 child_id = id;
2569 iter = cx.tcx.hir_parent_iter(id);
2570 },
2571 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2572 _ => return Some(ControlFlow::Continue((parent, child_id))),
2573 }
2574 }
2575 debug_assert!(false, "no parent node found for `{child_id:?}`");
2576 None
2577}
2578
2579#[derive(Clone, Copy)]
2581pub enum DefinedTy<'tcx> {
2582 Hir(&'tcx hir::Ty<'tcx>),
2584 Mir {
2592 def_site_def_id: Option<DefId>,
2593 ty: Binder<'tcx, Ty<'tcx>>,
2594 },
2595}
2596
2597pub struct ExprUseCtxt<'tcx> {
2599 pub node: Node<'tcx>,
2601 pub child_id: HirId,
2603 pub adjustments: &'tcx [Adjustment<'tcx>],
2605 pub is_ty_unified: bool,
2607 pub moved_before_use: bool,
2609 pub same_ctxt: bool,
2611}
2612impl<'tcx> ExprUseCtxt<'tcx> {
2613 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2614 match self.node {
2615 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2616 Node::ExprField(field) => ExprUseNode::Field(field),
2617
2618 Node::Item(&Item {
2619 kind: ItemKind::Static(..) | ItemKind::Const(..),
2620 owner_id,
2621 ..
2622 })
2623 | Node::TraitItem(&TraitItem {
2624 kind: TraitItemKind::Const(..),
2625 owner_id,
2626 ..
2627 })
2628 | Node::ImplItem(&ImplItem {
2629 kind: ImplItemKind::Const(..),
2630 owner_id,
2631 ..
2632 }) => ExprUseNode::ConstStatic(owner_id),
2633
2634 Node::Item(&Item {
2635 kind: ItemKind::Fn { .. },
2636 owner_id,
2637 ..
2638 })
2639 | Node::TraitItem(&TraitItem {
2640 kind: TraitItemKind::Fn(..),
2641 owner_id,
2642 ..
2643 })
2644 | Node::ImplItem(&ImplItem {
2645 kind: ImplItemKind::Fn(..),
2646 owner_id,
2647 ..
2648 }) => ExprUseNode::Return(owner_id),
2649
2650 Node::Expr(use_expr) => match use_expr.kind {
2651 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2652 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2653 }),
2654
2655 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2656 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2657 Some(i) => ExprUseNode::FnArg(func, i),
2658 None => ExprUseNode::Callee,
2659 },
2660 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2661 use_expr.hir_id,
2662 name.args,
2663 args.iter()
2664 .position(|arg| arg.hir_id == self.child_id)
2665 .map_or(0, |i| i + 1),
2666 ),
2667 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2668 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2669 _ => ExprUseNode::Other,
2670 },
2671 _ => ExprUseNode::Other,
2672 }
2673 }
2674}
2675
2676pub enum ExprUseNode<'tcx> {
2678 LetStmt(&'tcx LetStmt<'tcx>),
2680 ConstStatic(OwnerId),
2682 Return(OwnerId),
2684 Field(&'tcx ExprField<'tcx>),
2686 FnArg(&'tcx Expr<'tcx>, usize),
2688 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2690 Callee,
2692 FieldAccess(Ident),
2694 AddrOf(ast::BorrowKind, Mutability),
2696 Other,
2697}
2698impl<'tcx> ExprUseNode<'tcx> {
2699 pub fn is_return(&self) -> bool {
2701 matches!(self, Self::Return(_))
2702 }
2703
2704 pub fn is_recv(&self) -> bool {
2706 matches!(self, Self::MethodArg(_, _, 0))
2707 }
2708
2709 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2711 match *self {
2712 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2713 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2714 def_site_def_id: Some(id.def_id.to_def_id()),
2715 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2716 }),
2717 Self::Return(id) => {
2718 if let Node::Expr(Expr {
2719 kind: ExprKind::Closure(c),
2720 ..
2721 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2722 {
2723 match c.fn_decl.output {
2724 FnRetTy::DefaultReturn(_) => None,
2725 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2726 }
2727 } else {
2728 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2729 Some(DefinedTy::Mir {
2730 def_site_def_id: Some(id.def_id.to_def_id()),
2731 ty,
2732 })
2733 }
2734 },
2735 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2736 Some(Expr {
2737 hir_id,
2738 kind: ExprKind::Struct(path, ..),
2739 ..
2740 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2741 .and_then(|(adt, variant)| {
2742 variant
2743 .fields
2744 .iter()
2745 .find(|f| f.name == field.ident.name)
2746 .map(|f| (adt, f))
2747 })
2748 .map(|(adt, field_def)| DefinedTy::Mir {
2749 def_site_def_id: Some(adt.did()),
2750 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2751 }),
2752 _ => None,
2753 },
2754 Self::FnArg(callee, i) => {
2755 let sig = expr_sig(cx, callee)?;
2756 let (hir_ty, ty) = sig.input_with_hir(i)?;
2757 Some(match hir_ty {
2758 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2759 None => DefinedTy::Mir {
2760 def_site_def_id: sig.predicates_id(),
2761 ty,
2762 },
2763 })
2764 },
2765 Self::MethodArg(id, _, i) => {
2766 let id = cx.typeck_results().type_dependent_def_id(id)?;
2767 let sig = cx.tcx.fn_sig(id).skip_binder();
2768 Some(DefinedTy::Mir {
2769 def_site_def_id: Some(id),
2770 ty: sig.input(i),
2771 })
2772 },
2773 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2774 }
2775 }
2776}
2777
2778pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2780 let mut adjustments = [].as_slice();
2781 let mut is_ty_unified = false;
2782 let mut moved_before_use = false;
2783 let mut same_ctxt = true;
2784 let ctxt = e.span.ctxt();
2785 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2786 if adjustments.is_empty()
2787 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2788 {
2789 adjustments = cx.typeck_results().expr_adjustments(e);
2790 }
2791 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2792 if let Node::Expr(e) = parent {
2793 match e.kind {
2794 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2795 is_ty_unified = true;
2796 moved_before_use = true;
2797 },
2798 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2799 is_ty_unified = true;
2800 moved_before_use = true;
2801 },
2802 ExprKind::Block(..) => moved_before_use = true,
2803 _ => {},
2804 }
2805 }
2806 ControlFlow::Continue(())
2807 });
2808 match node {
2809 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2810 node,
2811 child_id,
2812 adjustments,
2813 is_ty_unified,
2814 moved_before_use,
2815 same_ctxt,
2816 },
2817 #[allow(unreachable_patterns)]
2818 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
2819 None => ExprUseCtxt {
2820 node: Node::Crate(cx.tcx.hir_root_module()),
2821 child_id: HirId::INVALID,
2822 adjustments: &[],
2823 is_ty_unified: true,
2824 moved_before_use: true,
2825 same_ctxt: false,
2826 },
2827 }
2828}
2829
2830pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2832 let mut pos = 0;
2833 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2834 let end = pos + t.len;
2835 let range = pos as usize..end as usize;
2836 let inner = InnerSpan::new(range.start, range.end);
2837 pos = end;
2838 (t.kind, s.get(range).unwrap_or_default(), inner)
2839 })
2840}
2841
2842pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2845 let Ok(snippet) = sm.span_to_snippet(span) else {
2846 return false;
2847 };
2848 return tokenize(&snippet, FrontmatterAllowed::No).any(|token| {
2849 matches!(
2850 token.kind,
2851 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2852 )
2853 });
2854}
2855
2856pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2861 matches!(span.get_source_text(cx), Some(snippet) if tokenize_with_text(&snippet).any(|(token, _, _)|
2862 match token {
2863 TokenKind::Whitespace => false,
2864 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2865 _ => true,
2866 }
2867 ))
2868}
2869pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2873 span_extract_comments(sm, span).join("\n")
2874}
2875
2876pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2880 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2881 tokenize_with_text(&snippet)
2882 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2883 .map(|(_, s, _)| s.to_string())
2884 .collect::<Vec<_>>()
2885}
2886
2887pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2888 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2889}
2890
2891pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2916 cx: &LateContext<'_>,
2917 pat: &'a Pat<'hir>,
2918 else_body: &Expr<'_>,
2919) -> Option<&'a Pat<'hir>> {
2920 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
2921 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
2922 && !is_refutable(cx, inner_pat)
2923 && let else_body = peel_blocks(else_body)
2924 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2925 && let ExprKind::Path(ret_path) = ret_val.kind
2926 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
2927 {
2928 Some(inner_pat)
2929 } else {
2930 None
2931 }
2932}
2933
2934macro_rules! op_utils {
2935 ($($name:ident $assign:ident)*) => {
2936 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2938
2939 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2941
2942 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2944 match kind {
2945 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2946 _ => None,
2947 }
2948 }
2949 };
2950}
2951
2952op_utils! {
2953 Add AddAssign
2954 Sub SubAssign
2955 Mul MulAssign
2956 Div DivAssign
2957 Rem RemAssign
2958 BitXor BitXorAssign
2959 BitAnd BitAndAssign
2960 BitOr BitOrAssign
2961 Shl ShlAssign
2962 Shr ShrAssign
2963}
2964
2965pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2968 match *pat {
2969 PatKind::Wild => true,
2970 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2971 !visitors::is_local_used(cx, body, id)
2972 },
2973 _ => false,
2974 }
2975}
2976
2977#[derive(Clone, Copy)]
2978pub enum RequiresSemi {
2979 Yes,
2980 No,
2981}
2982impl RequiresSemi {
2983 pub fn requires_semi(self) -> bool {
2984 matches!(self, Self::Yes)
2985 }
2986}
2987
2988#[expect(clippy::too_many_lines)]
2991pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2992 struct BreakTarget {
2993 id: HirId,
2994 unused: bool,
2995 }
2996
2997 struct V<'cx, 'tcx> {
2998 cx: &'cx LateContext<'tcx>,
2999 break_targets: Vec<BreakTarget>,
3000 break_targets_for_result_ty: u32,
3001 in_final_expr: bool,
3002 requires_semi: bool,
3003 is_never: bool,
3004 }
3005
3006 impl V<'_, '_> {
3007 fn push_break_target(&mut self, id: HirId) {
3008 self.break_targets.push(BreakTarget { id, unused: true });
3009 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3010 }
3011 }
3012
3013 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3014 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3015 if self.is_never && self.break_targets.is_empty() {
3032 if self.in_final_expr && !self.requires_semi {
3033 match e.kind {
3036 ExprKind::DropTemps(e) => self.visit_expr(e),
3037 ExprKind::If(_, then, Some(else_)) => {
3038 self.visit_expr(then);
3039 self.visit_expr(else_);
3040 },
3041 ExprKind::Match(_, arms, _) => {
3042 for arm in arms {
3043 self.visit_expr(arm.body);
3044 }
3045 },
3046 ExprKind::Loop(b, ..) => {
3047 self.push_break_target(e.hir_id);
3048 self.in_final_expr = false;
3049 self.visit_block(b);
3050 self.break_targets.pop();
3051 },
3052 ExprKind::Block(b, _) => {
3053 if b.targeted_by_break {
3054 self.push_break_target(b.hir_id);
3055 self.visit_block(b);
3056 self.break_targets.pop();
3057 } else {
3058 self.visit_block(b);
3059 }
3060 },
3061 _ => {
3062 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3063 },
3064 }
3065 }
3066 return;
3067 }
3068 match e.kind {
3069 ExprKind::DropTemps(e) => self.visit_expr(e),
3070 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3071 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3072 self.in_final_expr = false;
3073 self.visit_expr(e);
3074 self.is_never = true;
3075 },
3076 ExprKind::Break(dest, e) => {
3077 if let Some(e) = e {
3078 self.in_final_expr = false;
3079 self.visit_expr(e);
3080 }
3081 if let Ok(id) = dest.target_id
3082 && let Some((i, target)) = self
3083 .break_targets
3084 .iter_mut()
3085 .enumerate()
3086 .find(|(_, target)| target.id == id)
3087 {
3088 target.unused &= self.is_never;
3089 if i < self.break_targets_for_result_ty as usize {
3090 self.requires_semi = true;
3091 }
3092 }
3093 self.is_never = true;
3094 },
3095 ExprKind::If(cond, then, else_) => {
3096 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3097 self.visit_expr(cond);
3098 self.in_final_expr = in_final_expr;
3099
3100 if self.is_never {
3101 self.visit_expr(then);
3102 if let Some(else_) = else_ {
3103 self.visit_expr(else_);
3104 }
3105 } else {
3106 self.visit_expr(then);
3107 let is_never = mem::replace(&mut self.is_never, false);
3108 if let Some(else_) = else_ {
3109 self.visit_expr(else_);
3110 self.is_never &= is_never;
3111 }
3112 }
3113 },
3114 ExprKind::Match(scrutinee, arms, _) => {
3115 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3116 self.visit_expr(scrutinee);
3117 self.in_final_expr = in_final_expr;
3118
3119 if self.is_never {
3120 for arm in arms {
3121 self.visit_arm(arm);
3122 }
3123 } else {
3124 let mut is_never = true;
3125 for arm in arms {
3126 self.is_never = false;
3127 if let Some(guard) = arm.guard {
3128 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3129 self.visit_expr(guard);
3130 self.in_final_expr = in_final_expr;
3131 self.is_never = false;
3133 }
3134 self.visit_expr(arm.body);
3135 is_never &= self.is_never;
3136 }
3137 self.is_never = is_never;
3138 }
3139 },
3140 ExprKind::Loop(b, _, _, _) => {
3141 self.push_break_target(e.hir_id);
3142 self.in_final_expr = false;
3143 self.visit_block(b);
3144 self.is_never = self.break_targets.pop().unwrap().unused;
3145 },
3146 ExprKind::Block(b, _) => {
3147 if b.targeted_by_break {
3148 self.push_break_target(b.hir_id);
3149 self.visit_block(b);
3150 self.is_never &= self.break_targets.pop().unwrap().unused;
3151 } else {
3152 self.visit_block(b);
3153 }
3154 },
3155 _ => {
3156 self.in_final_expr = false;
3157 walk_expr(self, e);
3158 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3159 },
3160 }
3161 }
3162
3163 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3164 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3165 for s in b.stmts {
3166 self.visit_stmt(s);
3167 }
3168 self.in_final_expr = in_final_expr;
3169 if let Some(e) = b.expr {
3170 self.visit_expr(e);
3171 }
3172 }
3173
3174 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3175 if let Some(e) = l.init {
3176 self.visit_expr(e);
3177 }
3178 if let Some(else_) = l.els {
3179 let is_never = self.is_never;
3180 self.visit_block(else_);
3181 self.is_never = is_never;
3182 }
3183 }
3184
3185 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3186 if let Some(guard) = arm.guard {
3187 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3188 self.visit_expr(guard);
3189 self.in_final_expr = in_final_expr;
3190 }
3191 self.visit_expr(arm.body);
3192 }
3193 }
3194
3195 if cx.typeck_results().expr_ty(e).is_never() {
3196 Some(RequiresSemi::No)
3197 } else if let ExprKind::Block(b, _) = e.kind
3198 && !b.targeted_by_break
3199 && b.expr.is_none()
3200 {
3201 None
3203 } else {
3204 let mut v = V {
3205 cx,
3206 break_targets: Vec::new(),
3207 break_targets_for_result_ty: 0,
3208 in_final_expr: true,
3209 requires_semi: false,
3210 is_never: false,
3211 };
3212 v.visit_expr(e);
3213 v.is_never
3214 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3215 RequiresSemi::Yes
3216 } else {
3217 RequiresSemi::No
3218 })
3219 }
3220}
3221
3222pub fn get_path_from_caller_to_method_type<'tcx>(
3228 tcx: TyCtxt<'tcx>,
3229 from: LocalDefId,
3230 method: DefId,
3231 args: GenericArgsRef<'tcx>,
3232) -> String {
3233 let assoc_item = tcx.associated_item(method);
3234 let def_id = assoc_item.container_id(tcx);
3235 match assoc_item.container {
3236 rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
3237 rustc_ty::AssocItemContainer::Impl => {
3238 let ty = tcx.type_of(def_id).instantiate_identity();
3239 get_path_to_ty(tcx, from, ty, args)
3240 },
3241 }
3242}
3243
3244fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3245 match ty.kind() {
3246 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3247 rustc_ty::Array(..)
3249 | rustc_ty::Dynamic(..)
3250 | rustc_ty::Never
3251 | rustc_ty::RawPtr(_, _)
3252 | rustc_ty::Ref(..)
3253 | rustc_ty::Slice(_)
3254 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3255 _ => ty.to_string(),
3256 }
3257}
3258
3259fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3261 if callee.is_local() {
3263 let callee_path = tcx.def_path(callee);
3264 let caller_path = tcx.def_path(from.to_def_id());
3265 maybe_get_relative_path(&caller_path, &callee_path, 2)
3266 } else {
3267 tcx.def_path_str(callee)
3268 }
3269}
3270
3271fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3284 use itertools::EitherOrBoth::{Both, Left, Right};
3285
3286 let unique_parts = to
3288 .data
3289 .iter()
3290 .zip_longest(from.data.iter())
3291 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3292 .map(|el| match el {
3293 Both(l, r) => Both(l.data, r.data),
3294 Left(l) => Left(l.data),
3295 Right(r) => Right(r.data),
3296 });
3297
3298 let mut go_up_by = 0;
3300 let mut path = Vec::new();
3301 for el in unique_parts {
3302 match el {
3303 Both(l, r) => {
3304 if let DefPathData::TypeNs(sym) = l {
3314 path.push(sym);
3315 }
3316 if let DefPathData::TypeNs(_) = r {
3317 go_up_by += 1;
3318 }
3319 },
3320 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3325 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3330 _ => {},
3331 }
3332 }
3333
3334 if go_up_by > max_super {
3335 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3337 if let DefPathData::TypeNs(sym) = el.data {
3338 Some(sym)
3339 } else {
3340 None
3341 }
3342 })))
3343 } else {
3344 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3345 }
3346}
3347
3348pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3351 matches!(
3352 cx.tcx.parent_hir_node(id),
3353 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3354 )
3355}
3356
3357pub fn is_block_like(expr: &Expr<'_>) -> bool {
3360 matches!(
3361 expr.kind,
3362 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3363 )
3364}
3365
3366pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3368 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3369 match expr.kind {
3370 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3371 _ if is_block_like(expr) => is_operand,
3372 _ => false,
3373 }
3374 }
3375
3376 contains_block(expr, false)
3377}
3378
3379pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3381 if let Some(parent_expr) = get_parent_expr(cx, expr)
3382 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3383 && receiver.hir_id == expr.hir_id
3384 {
3385 return true;
3386 }
3387 false
3388}
3389
3390pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3393 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3394 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3395 && temporary_ty
3396 .walk()
3397 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3398 {
3399 ControlFlow::Break(())
3400 } else {
3401 ControlFlow::Continue(())
3402 }
3403 })
3404 .is_break()
3405}
3406
3407pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3418 let expr_ty_is_adjusted = cx
3419 .typeck_results()
3420 .expr_adjustments(expr)
3421 .iter()
3422 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3424 if expr_ty_is_adjusted {
3425 return true;
3426 }
3427
3428 match expr.kind {
3431 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3432 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3433
3434 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3435 return false;
3436 }
3437
3438 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3439 let mut args_with_ty_param = {
3440 fn_sig
3441 .inputs()
3442 .skip_binder()
3443 .iter()
3444 .skip(self_arg_count)
3445 .zip(args)
3446 .filter_map(|(arg_ty, arg)| {
3447 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3448 Some(arg)
3449 } else {
3450 None
3451 }
3452 })
3453 };
3454 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3455 },
3456 ExprKind::Struct(qpath, _, _) => {
3458 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3459 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3460 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3461 return true;
3463 };
3464 v_def
3465 .fields
3466 .iter()
3467 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3468 } else {
3469 false
3470 }
3471 },
3472 ExprKind::Block(
3474 &Block {
3475 expr: Some(ret_expr), ..
3476 },
3477 _,
3478 )
3479 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3480
3481 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3483 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3485 ExprKind::If(_, then, maybe_else) => {
3487 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3488 },
3489 ExprKind::Match(_, arms, _) => arms
3490 .iter()
3491 .map(|arm| arm.body)
3492 .any(|body| expr_requires_coercion(cx, body)),
3493 _ => false,
3494 }
3495}
3496
3497pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3500 if let Some(hir_id) = path_to_local(expr)
3501 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3502 {
3503 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3504 } else if let ExprKind::Path(p) = &expr.kind
3505 && let Some(mutability) = cx
3506 .qpath_res(p, expr.hir_id)
3507 .opt_def_id()
3508 .and_then(|id| cx.tcx.static_mutability(id))
3509 {
3510 mutability == Mutability::Mut
3511 } else if let ExprKind::Field(parent, _) = expr.kind {
3512 is_mutable(cx, parent)
3513 } else {
3514 true
3515 }
3516}
3517
3518pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3521 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3522 return hir_ty;
3523 };
3524 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3525 && let Some(segment) = path.segments.last()
3526 && segment.ident.name == sym::Option
3527 && let Res::Def(DefKind::Enum, def_id) = segment.res
3528 && def_id == option_def_id
3529 && let [GenericArg::Type(arg_ty)] = segment.args().args
3530 {
3531 hir_ty = arg_ty.as_unambig_ty();
3532 }
3533 hir_ty
3534}
3535
3536pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3539 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3540 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3541 && let ctxt = expr.span.ctxt()
3542 && for_each_expr_without_closures(into_future_arg, |e| {
3543 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3544 })
3545 .is_none()
3546 {
3547 Some(into_future_arg)
3548 } else {
3549 None
3550 }
3551}
3552
3553pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3555 if let ExprKind::Call(fn_expr, []) = &expr.kind
3556 && let ExprKind::Path(qpath) = &fn_expr.kind
3557 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3558 {
3559 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3560 } else {
3561 false
3562 }
3563}
3564
3565pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3582 let enclosing_body_owner = cx
3583 .tcx
3584 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3585 let mut prev_id = expr.hir_id;
3586 let mut skip_until_id = None;
3587 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3588 if hir_id == enclosing_body_owner {
3589 return true;
3590 }
3591 if let Some(id) = skip_until_id {
3592 prev_id = hir_id;
3593 if id == hir_id {
3594 skip_until_id = None;
3595 }
3596 continue;
3597 }
3598 match node {
3599 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3600 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3601 Node::Expr(expr) => match expr.kind {
3602 ExprKind::Ret(_) => return true,
3603 ExprKind::If(_, then, opt_else)
3604 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3605 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3606 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3607 ExprKind::Break(
3608 Destination {
3609 target_id: Ok(target_id),
3610 ..
3611 },
3612 _,
3613 ) => skip_until_id = Some(target_id),
3614 _ => break,
3615 },
3616 _ => break,
3617 }
3618 prev_id = hir_id;
3619 }
3620
3621 false
3624}
3625
3626pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3629 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3630 matches!(
3631 adj.kind,
3632 Adjust::Deref(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny
3633 )
3634 })
3635}