1#![feature(array_chunks)]
2#![feature(box_patterns)]
3#![feature(f128)]
4#![feature(f16)]
5#![feature(if_let_guard)]
6#![feature(macro_metavar_expr_concat)]
7#![feature(let_chains)]
8#![feature(never_type)]
9#![feature(rustc_private)]
10#![feature(assert_matches)]
11#![feature(unwrap_infallible)]
12#![feature(array_windows)]
13#![recursion_limit = "512"]
14#![allow(
15 clippy::missing_errors_doc,
16 clippy::missing_panics_doc,
17 clippy::must_use_candidate,
18 rustc::diagnostic_outside_of_impl,
19 rustc::untranslatable_diagnostic
20)]
21#![warn(
22 trivial_casts,
23 trivial_numeric_casts,
24 rust_2018_idioms,
25 unused_lifetimes,
26 unused_qualifications,
27 rustc::internal
28)]
29
30extern crate rustc_abi;
33extern crate rustc_ast;
34extern crate rustc_attr_parsing;
35extern crate rustc_const_eval;
36extern crate rustc_data_structures;
37#[allow(unused_extern_crates)]
39extern crate rustc_driver;
40extern crate rustc_errors;
41extern crate rustc_hir;
42extern crate rustc_hir_analysis;
43extern crate rustc_hir_typeck;
44extern crate rustc_index;
45extern crate rustc_infer;
46extern crate rustc_lexer;
47extern crate rustc_lint;
48extern crate rustc_middle;
49extern crate rustc_mir_dataflow;
50extern crate rustc_session;
51extern crate rustc_span;
52extern crate rustc_trait_selection;
53extern crate smallvec;
54
55#[macro_use]
56pub mod sym_helper;
57
58pub mod ast_utils;
59pub mod attrs;
60mod check_proc_macro;
61pub mod comparisons;
62pub mod consts;
63pub mod diagnostics;
64pub mod eager_or_lazy;
65pub mod higher;
66mod hir_utils;
67pub mod macros;
68pub mod mir;
69pub mod msrvs;
70pub mod numeric_literal;
71pub mod paths;
72pub mod ptr;
73pub mod qualify_min_const_fn;
74pub mod source;
75pub mod str_utils;
76pub mod sugg;
77pub mod ty;
78pub mod usage;
79pub mod visitors;
80
81pub use self::attrs::*;
82pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
83pub use self::hir_utils::{
84 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over,
85};
86
87use core::mem;
88use core::ops::ControlFlow;
89use std::collections::hash_map::Entry;
90use std::hash::BuildHasherDefault;
91use std::iter::{once, repeat_n};
92use std::sync::{Mutex, MutexGuard, OnceLock};
93
94use itertools::Itertools;
95use rustc_abi::Integer;
96use rustc_ast::ast::{self, LitKind, RangeLimits};
97use rustc_attr_parsing::{AttributeKind, find_attr};
98use rustc_data_structures::fx::FxHashMap;
99use rustc_data_structures::packed::Pu128;
100use rustc_data_structures::unhash::UnhashMap;
101use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
102use rustc_hir::def::{DefKind, Res};
103use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
104use rustc_hir::definitions::{DefPath, DefPathData};
105use rustc_hir::hir_id::{HirIdMap, HirIdSet};
106use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
107use rustc_hir::{
108 self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext,
109 Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind,
110 ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat,
111 PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind,
112 TraitItemRef, TraitRef, TyKind, UnOp, def,
113};
114use rustc_lexer::{TokenKind, tokenize};
115use rustc_lint::{LateContext, Level, Lint, LintContext};
116use rustc_middle::hir::place::PlaceBase;
117use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
118use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
119use rustc_middle::ty::fast_reject::SimplifiedType;
120use rustc_middle::ty::layout::IntegerExt;
121use rustc_middle::ty::{
122 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty,
123 TyCtxt, TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
124};
125use rustc_span::hygiene::{ExpnKind, MacroKind};
126use rustc_span::source_map::SourceMap;
127use rustc_span::symbol::{Ident, Symbol, kw};
128use rustc_span::{InnerSpan, Span, sym};
129use visitors::{Visitable, for_each_unconsumed_temporary};
130
131use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
132use crate::higher::Range;
133use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
134use crate::visitors::for_each_expr_without_closures;
135use rustc_middle::hir::nested_filter;
136
137#[macro_export]
138macro_rules! extract_msrv_attr {
139 () => {
140 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
141 let sess = rustc_lint::LintContext::sess(cx);
142 self.msrv.check_attributes(sess, attrs);
143 }
144
145 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
146 let sess = rustc_lint::LintContext::sess(cx);
147 self.msrv.check_attributes_post(sess, attrs);
148 }
149 };
150}
151
152pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
175 while let Some(init) = path_to_local(expr)
176 .and_then(|id| find_binding_init(cx, id))
177 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
178 {
179 expr = init;
180 }
181 expr
182}
183
184pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
193 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
194 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
195 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
196 {
197 return local.init;
198 }
199 None
200}
201
202pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
206 for (_, node) in cx.tcx.hir_parent_iter(local) {
207 match node {
208 Node::Pat(..) | Node::PatField(..) => {},
209 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
210 _ => return true,
211 }
212 }
213
214 false
215}
216
217pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
228 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
229 cx.enclosing_body.is_some_and(|id| {
230 cx.tcx
231 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
232 .is_some()
233 })
234}
235
236pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
243 use ConstContext::{Const, ConstFn, Static};
244 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
245 return false;
246 };
247 match ctx {
248 ConstFn => false,
249 Static(_) | Const { inline: _ } => true,
250 }
251}
252
253pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
256 if let Res::Def(DefKind::Ctor(..), id) = res
257 && let Some(lang_id) = cx.tcx.lang_items().get(lang_item)
258 && let Some(id) = cx.tcx.opt_parent(id)
259 {
260 id == lang_id
261 } else {
262 false
263 }
264}
265
266pub fn is_enum_variant_ctor(
268 cx: &LateContext<'_>,
269 enum_item: Symbol,
270 variant_name: Symbol,
271 ctor_call_id: DefId,
272) -> bool {
273 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
274 return false;
275 };
276
277 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
278 variants
279 .filter(|variant| variant.name == variant_name)
280 .filter_map(|variant| variant.ctor.as_ref())
281 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
282}
283
284pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
286 let did = match cx.tcx.def_kind(did) {
287 DefKind::Ctor(..) => cx.tcx.parent(did),
288 DefKind::Variant => match cx.tcx.opt_parent(did) {
290 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
291 _ => did,
292 },
293 _ => did,
294 };
295
296 cx.tcx.is_diagnostic_item(item, did)
297}
298
299pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
301 let did = match cx.tcx.def_kind(did) {
302 DefKind::Ctor(..) => cx.tcx.parent(did),
303 DefKind::Variant => match cx.tcx.opt_parent(did) {
305 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
306 _ => did,
307 },
308 _ => did,
309 };
310
311 cx.tcx.lang_items().get(item) == Some(did)
312}
313
314pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
315 matches!(
316 expr.kind,
317 ExprKind::Block(
318 Block {
319 stmts: [],
320 expr: None,
321 ..
322 },
323 _
324 ) | ExprKind::Tup([])
325 )
326}
327
328pub fn is_wild(pat: &Pat<'_>) -> bool {
330 matches!(pat.kind, PatKind::Wild)
331}
332
333pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
335 matches!(
336 arm.pat.kind,
337 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
338 if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone)
339 )
340}
341
342pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
344 match *qpath {
345 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
346 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
347 _ => false,
348 }
349}
350
351pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
354 let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
355 let trt_id = cx.tcx.trait_of_item(def_id);
356 trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path))
357}
358
359pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
361 if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
362 cx.tcx.trait_of_item(method_id).is_none()
363 } else {
364 false
365 }
366}
367
368pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
370 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
371 if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() {
372 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
373 }
374 }
375 false
376}
377
378pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
380 if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
381 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
382 }
383 false
384}
385
386pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
388 cx.typeck_results()
389 .type_dependent_def_id(expr.hir_id)
390 .is_some_and(|did| is_diag_trait_item(cx, did, diag_item))
391}
392
393pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
395 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
396 && let ItemKind::Impl(imp) = item.kind
397 {
398 imp.of_trait.is_some()
399 } else {
400 false
401 }
402}
403
404pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
414 if let ExprKind::Path(ref qpath) = expr.kind {
415 cx.qpath_res(qpath, expr.hir_id)
416 .opt_def_id()
417 .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item))
418 } else {
419 false
420 }
421}
422
423pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
424 match *path {
425 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
426 QPath::TypeRelative(_, seg) => seg,
427 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
428 }
429}
430
431pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
432 last_path_segment(qpath)
433 .args
434 .map_or(&[][..], |a| a.args)
435 .iter()
436 .filter_map(|a| match a {
437 hir::GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
438 _ => None,
439 })
440}
441
442pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
456 match *path {
457 QPath::Resolved(_, path) => match_path(path, segments),
458 QPath::TypeRelative(ty, segment) => match ty.kind {
459 TyKind::Path(ref inner_path) => {
460 if let [prefix @ .., end] = segments {
461 if match_qpath(inner_path, prefix) {
462 return segment.ident.name.as_str() == *end;
463 }
464 }
465 false
466 },
467 _ => false,
468 },
469 QPath::LangItem(..) => false,
470 }
471}
472
473pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
477 path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments))
478}
479
480pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
483 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id))
484}
485
486pub fn is_path_diagnostic_item<'tcx>(
489 cx: &LateContext<'_>,
490 maybe_path: &impl MaybePath<'tcx>,
491 diag_item: Symbol,
492) -> bool {
493 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
494}
495
496pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
517 path.segments
518 .iter()
519 .rev()
520 .zip(segments.iter().rev())
521 .all(|(a, b)| a.ident.name.as_str() == *b)
522}
523
524pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
526 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
527 if let Res::Local(id) = path.res {
528 return Some(id);
529 }
530 }
531 None
532}
533
534pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
537 path_to_local(expr) == Some(id)
538}
539
540pub trait MaybePath<'hir> {
541 fn hir_id(&self) -> HirId;
542 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
543}
544
545macro_rules! maybe_path {
546 ($ty:ident, $kind:ident) => {
547 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
548 fn hir_id(&self) -> HirId {
549 self.hir_id
550 }
551 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
552 match &self.kind {
553 hir::$kind::Path(qpath) => Some(qpath),
554 _ => None,
555 }
556 }
557 }
558 };
559}
560maybe_path!(Expr, ExprKind);
561impl<'hir> MaybePath<'hir> for Pat<'hir> {
562 fn hir_id(&self) -> HirId {
563 self.hir_id
564 }
565 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
566 match &self.kind {
567 PatKind::Expr(PatExpr {
568 kind: PatExprKind::Path(qpath),
569 ..
570 }) => Some(qpath),
571 _ => None,
572 }
573 }
574}
575maybe_path!(Ty, TyKind);
576
577pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
579 match maybe_path.qpath_opt() {
580 None => Res::Err,
581 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
582 }
583}
584
585pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
587 path_res(cx, maybe_path).opt_def_id()
588}
589
590fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
591 let ty = match name {
592 "bool" => SimplifiedType::Bool,
593 "char" => SimplifiedType::Char,
594 "str" => SimplifiedType::Str,
595 "array" => SimplifiedType::Array,
596 "slice" => SimplifiedType::Slice,
597 "const_ptr" => SimplifiedType::Ptr(Mutability::Not),
601 "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut),
602 "isize" => SimplifiedType::Int(IntTy::Isize),
603 "i8" => SimplifiedType::Int(IntTy::I8),
604 "i16" => SimplifiedType::Int(IntTy::I16),
605 "i32" => SimplifiedType::Int(IntTy::I32),
606 "i64" => SimplifiedType::Int(IntTy::I64),
607 "i128" => SimplifiedType::Int(IntTy::I128),
608 "usize" => SimplifiedType::Uint(UintTy::Usize),
609 "u8" => SimplifiedType::Uint(UintTy::U8),
610 "u16" => SimplifiedType::Uint(UintTy::U16),
611 "u32" => SimplifiedType::Uint(UintTy::U32),
612 "u64" => SimplifiedType::Uint(UintTy::U64),
613 "u128" => SimplifiedType::Uint(UintTy::U128),
614 "f32" => SimplifiedType::Float(FloatTy::F32),
615 "f64" => SimplifiedType::Float(FloatTy::F64),
616 _ => {
617 return [].iter().copied();
618 },
619 };
620
621 tcx.incoherent_impls(ty).iter().copied()
622}
623
624fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
625 match tcx.def_kind(def_id) {
626 DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
627 .module_children(def_id)
628 .iter()
629 .filter(|item| item.ident.name == name)
630 .map(|child| child.res.expect_non_local())
631 .collect(),
632 DefKind::Impl { .. } => tcx
633 .associated_item_def_ids(def_id)
634 .iter()
635 .copied()
636 .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
637 .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
638 .collect(),
639 _ => Vec::new(),
640 }
641}
642
643fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
644 let root_mod;
645 let item_kind = match tcx.hir_node_by_def_id(local_id) {
646 Node::Crate(r#mod) => {
647 root_mod = ItemKind::Mod(r#mod);
648 &root_mod
649 },
650 Node::Item(item) => &item.kind,
651 _ => return Vec::new(),
652 };
653
654 let res = |ident: Ident, owner_id: OwnerId| {
655 if ident.name == name {
656 let def_id = owner_id.to_def_id();
657 Some(Res::Def(tcx.def_kind(def_id), def_id))
658 } else {
659 None
660 }
661 };
662
663 match item_kind {
664 ItemKind::Mod(r#mod) => r#mod
665 .item_ids
666 .iter()
667 .filter_map(|&item_id| res(tcx.hir_item(item_id).ident, item_id.owner_id))
668 .collect(),
669 ItemKind::Impl(r#impl) => r#impl
670 .items
671 .iter()
672 .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
673 .collect(),
674 ItemKind::Trait(.., trait_item_refs) => trait_item_refs
675 .iter()
676 .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
677 .collect(),
678 _ => Vec::new(),
679 }
680}
681
682fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
683 if let Some(local_id) = def_id.as_local() {
684 local_item_children_by_name(tcx, local_id, name)
685 } else {
686 non_local_item_children_by_name(tcx, def_id, name)
687 }
688}
689
690pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec<Res> {
692 tcx.crates(())
693 .iter()
694 .copied()
695 .filter(move |&num| tcx.crate_name(num) == name)
696 .map(CrateNum::as_def_id)
697 .map(|id| Res::Def(tcx.def_kind(id), id))
698 .collect()
699}
700
701pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec<Res> {
711 let (base, path) = match path {
712 [primitive] => {
713 return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
714 },
715 [base, path @ ..] => (base, path),
716 _ => return Vec::new(),
717 };
718
719 let base_sym = Symbol::intern(base);
720
721 let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
722 Some(LOCAL_CRATE.as_def_id())
723 } else {
724 None
725 };
726
727 let crates = find_primitive_impls(tcx, base)
728 .chain(local_crate)
729 .map(|id| Res::Def(tcx.def_kind(id), id))
730 .chain(find_crates(tcx, base_sym))
731 .collect();
732
733 def_path_res_with_base(tcx, crates, path)
734}
735
736pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec<Res>, mut path: &[&str]) -> Vec<Res> {
741 while let [segment, rest @ ..] = path {
742 path = rest;
743 let segment = Symbol::intern(segment);
744
745 base = base
746 .into_iter()
747 .filter_map(|res| res.opt_def_id())
748 .flat_map(|def_id| {
749 let inherent_impl_children = tcx
752 .inherent_impls(def_id)
753 .iter()
754 .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
755
756 let direct_children = item_children_by_name(tcx, def_id, segment);
757
758 inherent_impl_children.chain(direct_children)
759 })
760 .collect();
761 }
762
763 base
764}
765
766pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> + use<> {
768 def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id())
769}
770
771pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option<DefId> {
776 def_path_res(tcx, path).into_iter().find_map(|res| match res {
777 Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
778 _ => None,
779 })
780}
781
782pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
798 let hir_id = cx.tcx.local_def_id_to_hir_id(def_id);
800 let parent_impl = cx.tcx.hir_get_parent_item(hir_id);
801 if parent_impl != hir::CRATE_OWNER_ID
802 && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_impl.def_id)
803 && let ItemKind::Impl(impl_) = &item.kind
804 {
805 return impl_.of_trait.as_ref();
806 }
807 None
808}
809
810fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
818 let mut result = vec![];
819 let root = loop {
820 match e.kind {
821 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
822 result.push(e);
823 e = ep;
824 },
825 _ => break e,
826 }
827 };
828 result.reverse();
829 (result, root)
830}
831
832pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
834 cx.typeck_results()
835 .expr_adjustments(e)
836 .iter()
837 .find_map(|a| match a.kind {
838 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
839 Adjust::Deref(None) => None,
840 _ => Some(None),
841 })
842 .and_then(|x| x)
843}
844
845pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
848 let (s1, r1) = projection_stack(e1);
849 let (s2, r2) = projection_stack(e2);
850 if !eq_expr_value(cx, r1, r2) {
851 return true;
852 }
853 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
854 return false;
855 }
856
857 for (x1, x2) in s1.iter().zip(s2.iter()) {
858 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
859 return false;
860 }
861
862 match (&x1.kind, &x2.kind) {
863 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
864 if i1 != i2 {
865 return true;
866 }
867 },
868 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
869 if !eq_expr_value(cx, i1, i2) {
870 return false;
871 }
872 },
873 _ => return false,
874 }
875 }
876 false
877}
878
879fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
882 let std_types_symbols = &[
883 sym::Vec,
884 sym::VecDeque,
885 sym::LinkedList,
886 sym::HashMap,
887 sym::BTreeMap,
888 sym::HashSet,
889 sym::BTreeSet,
890 sym::BinaryHeap,
891 ];
892
893 if let QPath::TypeRelative(_, method) = path {
894 if method.ident.name == sym::new {
895 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
896 if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() {
897 return std_types_symbols.iter().any(|&symbol| {
898 cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
899 });
900 }
901 }
902 }
903 }
904 false
905}
906
907pub fn is_default_equivalent_call(
909 cx: &LateContext<'_>,
910 repl_func: &Expr<'_>,
911 whole_call_expr: Option<&Expr<'_>>,
912) -> bool {
913 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
914 && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
915 && (is_diag_trait_item(cx, repl_def_id, sym::Default)
916 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath))
917 {
918 return true;
919 }
920
921 let Some(e) = whole_call_expr else { return false };
924 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
925 return false;
926 };
927 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
928 return false;
929 };
930 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
931 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
932 cx.tcx.lifetimes.re_erased.into()
933 } else if param.index == 0 && param.name == kw::SelfUpper {
934 ty.into()
935 } else {
936 param.to_error(cx.tcx)
937 }
938 });
939 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
940
941 let Ok(Some(instance)) = instance else { return false };
942 if let rustc_ty::InstanceKind::Item(def) = instance.def
943 && !cx.tcx.is_mir_available(def)
944 {
945 return false;
946 }
947 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
948 return false;
949 };
950 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
951 return false;
952 };
953
954 let body = cx.tcx.instance_mir(instance.def);
960 for block_data in body.basic_blocks.iter() {
961 if block_data.statements.len() == 1
962 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
963 && assign.0.local == RETURN_PLACE
964 && let Rvalue::Aggregate(kind, _places) = &assign.1
965 && let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
966 && let def = cx.tcx.adt_def(did)
967 && let variant = &def.variant(*variant_index)
968 && variant.fields.is_empty()
969 && let Some((_, did)) = variant.ctor
970 && did == repl_def_id
971 {
972 return true;
973 } else if block_data.statements.is_empty()
974 && let Some(term) = &block_data.terminator
975 {
976 match &term.kind {
977 TerminatorKind::Call {
978 func: Operand::Constant(c),
979 ..
980 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
981 && *did == repl_def_id =>
982 {
983 return true;
984 },
985 TerminatorKind::TailCall {
986 func: Operand::Constant(c),
987 ..
988 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
989 && *did == repl_def_id =>
990 {
991 return true;
992 },
993 _ => {},
994 }
995 }
996 }
997 false
998}
999
1000pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1004 match &e.kind {
1005 ExprKind::Lit(lit) => match lit.node {
1006 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
1007 LitKind::Str(s, _) => s.is_empty(),
1008 _ => false,
1009 },
1010 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
1011 ExprKind::Repeat(x, len) => {
1012 if let ConstArgKind::Anon(anon_const) = len.kind
1013 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
1014 && let LitKind::Int(v, _) = const_lit.node
1015 && v <= 32
1016 && is_default_equivalent(cx, x)
1017 {
1018 true
1019 } else {
1020 false
1021 }
1022 },
1023 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
1024 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
1025 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
1026 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
1027 _ => false,
1028 }
1029}
1030
1031fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
1032 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
1033 && seg.ident.name == sym::from
1034 {
1035 match arg.kind {
1036 ExprKind::Lit(hir::Lit {
1037 node: LitKind::Str(sym, _),
1038 ..
1039 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
1040 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
1041 ExprKind::Repeat(_, len) => {
1042 if let ConstArgKind::Anon(anon_const) = len.kind
1043 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
1044 && let LitKind::Int(v, _) = const_lit.node
1045 {
1046 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
1047 }
1048 },
1049 _ => (),
1050 }
1051 }
1052 false
1053}
1054
1055pub fn can_move_expr_to_closure_no_visit<'tcx>(
1087 cx: &LateContext<'tcx>,
1088 expr: &'tcx Expr<'_>,
1089 loop_ids: &[HirId],
1090 ignore_locals: &HirIdSet,
1091) -> bool {
1092 match expr.kind {
1093 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
1094 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
1095 if loop_ids.contains(&id) =>
1096 {
1097 true
1098 },
1099 ExprKind::Break(..)
1100 | ExprKind::Continue(_)
1101 | ExprKind::Ret(_)
1102 | ExprKind::Yield(..)
1103 | ExprKind::InlineAsm(_) => false,
1104 ExprKind::Field(
1107 &Expr {
1108 hir_id,
1109 kind:
1110 ExprKind::Path(QPath::Resolved(
1111 _,
1112 Path {
1113 res: Res::Local(local_id),
1114 ..
1115 },
1116 )),
1117 ..
1118 },
1119 _,
1120 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
1121 false
1123 },
1124 _ => true,
1125 }
1126}
1127
1128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1130pub enum CaptureKind {
1131 Value,
1132 Use,
1133 Ref(Mutability),
1134}
1135impl CaptureKind {
1136 pub fn is_imm_ref(self) -> bool {
1137 self == Self::Ref(Mutability::Not)
1138 }
1139}
1140impl std::ops::BitOr for CaptureKind {
1141 type Output = Self;
1142 fn bitor(self, rhs: Self) -> Self::Output {
1143 match (self, rhs) {
1144 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
1145 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
1146 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
1147 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
1148 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
1149 }
1150 }
1151}
1152impl std::ops::BitOrAssign for CaptureKind {
1153 fn bitor_assign(&mut self, rhs: Self) {
1154 *self = *self | rhs;
1155 }
1156}
1157
1158pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
1164 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
1165 let mut capture = CaptureKind::Ref(Mutability::Not);
1166 pat.each_binding_or_first(&mut |_, id, span, _| match cx
1167 .typeck_results()
1168 .extract_binding_mode(cx.sess(), id, span)
1169 .0
1170 {
1171 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
1172 capture = CaptureKind::Value;
1173 },
1174 ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
1175 capture = CaptureKind::Ref(Mutability::Mut);
1176 },
1177 _ => (),
1178 });
1179 capture
1180 }
1181
1182 debug_assert!(matches!(
1183 e.kind,
1184 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
1185 ));
1186
1187 let mut child_id = e.hir_id;
1188 let mut capture = CaptureKind::Value;
1189 let mut capture_expr_ty = e;
1190
1191 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
1192 if let [
1193 Adjustment {
1194 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
1195 target,
1196 },
1197 ref adjust @ ..,
1198 ] = *cx
1199 .typeck_results()
1200 .adjustments()
1201 .get(child_id)
1202 .map_or(&[][..], |x| &**x)
1203 {
1204 if let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
1205 *adjust.last().map_or(target, |a| a.target).kind()
1206 {
1207 return CaptureKind::Ref(mutability);
1208 }
1209 }
1210
1211 match parent {
1212 Node::Expr(e) => match e.kind {
1213 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
1214 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
1215 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
1216 return CaptureKind::Ref(Mutability::Mut);
1217 },
1218 ExprKind::Field(..) => {
1219 if capture == CaptureKind::Value {
1220 capture_expr_ty = e;
1221 }
1222 },
1223 ExprKind::Let(let_expr) => {
1224 let mutability = match pat_capture_kind(cx, let_expr.pat) {
1225 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
1226 CaptureKind::Ref(m) => m,
1227 };
1228 return CaptureKind::Ref(mutability);
1229 },
1230 ExprKind::Match(_, arms, _) => {
1231 let mut mutability = Mutability::Not;
1232 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
1233 match capture {
1234 CaptureKind::Value | CaptureKind::Use => break,
1235 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
1236 CaptureKind::Ref(Mutability::Not) => (),
1237 }
1238 }
1239 return CaptureKind::Ref(mutability);
1240 },
1241 _ => break,
1242 },
1243 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
1244 CaptureKind::Value | CaptureKind::Use => break,
1245 capture @ CaptureKind::Ref(_) => return capture,
1246 },
1247 _ => break,
1248 }
1249
1250 child_id = parent_id;
1251 }
1252
1253 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
1254 CaptureKind::Ref(Mutability::Not)
1256 } else {
1257 capture
1258 }
1259}
1260
1261pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
1264 struct V<'cx, 'tcx> {
1265 cx: &'cx LateContext<'tcx>,
1266 loops: Vec<HirId>,
1268 locals: HirIdSet,
1270 allow_closure: bool,
1272 captures: HirIdMap<CaptureKind>,
1275 }
1276 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1277 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1278 if !self.allow_closure {
1279 return;
1280 }
1281
1282 match e.kind {
1283 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1284 if !self.locals.contains(&l) {
1285 let cap = capture_local_usage(self.cx, e);
1286 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1287 }
1288 },
1289 ExprKind::Closure(closure) => {
1290 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
1291 let local_id = match capture.place.base {
1292 PlaceBase::Local(id) => id,
1293 PlaceBase::Upvar(var) => var.var_path.hir_id,
1294 _ => continue,
1295 };
1296 if !self.locals.contains(&local_id) {
1297 let capture = match capture.info.capture_kind {
1298 UpvarCapture::ByValue => CaptureKind::Value,
1299 UpvarCapture::ByUse => CaptureKind::Use,
1300 UpvarCapture::ByRef(kind) => match kind {
1301 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
1302 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
1303 CaptureKind::Ref(Mutability::Mut)
1304 },
1305 },
1306 };
1307 self.captures
1308 .entry(local_id)
1309 .and_modify(|e| *e |= capture)
1310 .or_insert(capture);
1311 }
1312 }
1313 },
1314 ExprKind::Loop(b, ..) => {
1315 self.loops.push(e.hir_id);
1316 self.visit_block(b);
1317 self.loops.pop();
1318 },
1319 _ => {
1320 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1321 walk_expr(self, e);
1322 },
1323 }
1324 }
1325
1326 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1327 p.each_binding_or_first(&mut |_, id, _, _| {
1328 self.locals.insert(id);
1329 });
1330 }
1331 }
1332
1333 let mut v = V {
1334 cx,
1335 loops: Vec::new(),
1336 locals: HirIdSet::default(),
1337 allow_closure: true,
1338 captures: HirIdMap::default(),
1339 };
1340 v.visit_expr(expr);
1341 v.allow_closure.then_some(v.captures)
1342}
1343
1344pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1346
1347pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1350 let mut method_names = Vec::with_capacity(max_depth);
1351 let mut arg_lists = Vec::with_capacity(max_depth);
1352 let mut spans = Vec::with_capacity(max_depth);
1353
1354 let mut current = expr;
1355 for _ in 0..max_depth {
1356 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1357 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1358 break;
1359 }
1360 method_names.push(path.ident.name);
1361 arg_lists.push((*receiver, &**args));
1362 spans.push(path.ident.span);
1363 current = receiver;
1364 } else {
1365 break;
1366 }
1367 }
1368
1369 (method_names, arg_lists, spans)
1370}
1371
1372pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1379 let mut current = expr;
1380 let mut matched = Vec::with_capacity(methods.len());
1381 for method_name in methods.iter().rev() {
1382 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1384 if path.ident.name.as_str() == *method_name {
1385 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1386 return None;
1387 }
1388 matched.push((receiver, args)); current = receiver; } else {
1391 return None;
1392 }
1393 } else {
1394 return None;
1395 }
1396 }
1397 matched.reverse();
1399 Some(matched)
1400}
1401
1402pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1404 cx.tcx
1405 .entry_fn(())
1406 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1407}
1408
1409pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1411 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1412 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1413}
1414
1415pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1417 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1418 match cx.tcx.hir_node_by_def_id(parent_id) {
1419 Node::Item(Item { ident, .. })
1420 | Node::TraitItem(TraitItem { ident, .. })
1421 | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1422 _ => None,
1423 }
1424}
1425
1426pub struct ContainsName<'a, 'tcx> {
1427 pub cx: &'a LateContext<'tcx>,
1428 pub name: Symbol,
1429}
1430
1431impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1432 type Result = ControlFlow<()>;
1433 type NestedFilter = nested_filter::OnlyBodies;
1434
1435 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1436 if self.name == name {
1437 ControlFlow::Break(())
1438 } else {
1439 ControlFlow::Continue(())
1440 }
1441 }
1442
1443 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1444 self.cx.tcx
1445 }
1446}
1447
1448pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1450 let mut cn = ContainsName { cx, name };
1451 cn.visit_expr(expr).is_break()
1452}
1453
1454pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1456 for_each_expr_without_closures(expr, |e| {
1457 if matches!(e.kind, ExprKind::Ret(..)) {
1458 ControlFlow::Break(())
1459 } else {
1460 ControlFlow::Continue(())
1461 }
1462 })
1463 .is_some()
1464}
1465
1466pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1468 get_parent_expr_for_hir(cx, e.hir_id)
1469}
1470
1471pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1474 match cx.tcx.parent_hir_node(hir_id) {
1475 Node::Expr(parent) => Some(parent),
1476 _ => None,
1477 }
1478}
1479
1480pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1482 let enclosing_node = cx
1483 .tcx
1484 .hir_get_enclosing_scope(hir_id)
1485 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1486 enclosing_node.and_then(|node| match node {
1487 Node::Block(block) => Some(block),
1488 Node::Item(&Item {
1489 kind: ItemKind::Fn { body: eid, .. },
1490 ..
1491 })
1492 | Node::ImplItem(&ImplItem {
1493 kind: ImplItemKind::Fn(_, eid),
1494 ..
1495 })
1496 | Node::TraitItem(&TraitItem {
1497 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1498 ..
1499 }) => match cx.tcx.hir_body(eid).value.kind {
1500 ExprKind::Block(block, _) => Some(block),
1501 _ => None,
1502 },
1503 _ => None,
1504 })
1505}
1506
1507pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1509 cx: &LateContext<'tcx>,
1510 expr: &Expr<'_>,
1511) -> Option<&'tcx Expr<'tcx>> {
1512 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1513 match node {
1514 Node::Expr(e) => match e.kind {
1515 ExprKind::Closure { .. }
1516 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1517 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1518
1519 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1521 _ => (),
1522 },
1523 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1524 _ => break,
1525 }
1526 }
1527 None
1528}
1529
1530pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1532 match tcx.hir_parent_iter(id).next() {
1533 Some((
1534 _,
1535 Node::Item(Item {
1536 kind: ItemKind::Impl(imp),
1537 ..
1538 }),
1539 )) => Some(imp),
1540 _ => None,
1541 }
1542}
1543
1544pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1555 while let ExprKind::Block(
1556 Block {
1557 stmts: [],
1558 expr: Some(inner),
1559 rules: BlockCheckMode::DefaultBlock,
1560 ..
1561 },
1562 _,
1563 ) = expr.kind
1564 {
1565 expr = inner;
1566 }
1567 expr
1568}
1569
1570pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1581 while let ExprKind::Block(
1582 Block {
1583 stmts: [],
1584 expr: Some(inner),
1585 rules: BlockCheckMode::DefaultBlock,
1586 ..
1587 }
1588 | Block {
1589 stmts:
1590 [
1591 Stmt {
1592 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1593 ..
1594 },
1595 ],
1596 expr: None,
1597 rules: BlockCheckMode::DefaultBlock,
1598 ..
1599 },
1600 _,
1601 ) = expr.kind
1602 {
1603 expr = inner;
1604 }
1605 expr
1606}
1607
1608pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1610 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1611 match iter.next() {
1612 Some((
1613 _,
1614 Node::Expr(Expr {
1615 kind: ExprKind::If(_, _, Some(else_expr)),
1616 ..
1617 }),
1618 )) => else_expr.hir_id == expr.hir_id,
1619 _ => false,
1620 }
1621}
1622
1623pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1626 let mut child_id = expr.hir_id;
1627 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1628 if let Node::LetStmt(LetStmt {
1629 init: Some(init),
1630 els: Some(els),
1631 ..
1632 }) = node
1633 && (init.hir_id == child_id || els.hir_id == child_id)
1634 {
1635 return true;
1636 }
1637
1638 child_id = parent_id;
1639 }
1640
1641 false
1642}
1643
1644pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1646 let mut child_id = expr.hir_id;
1647 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1648 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1649 && els.hir_id == child_id
1650 {
1651 return true;
1652 }
1653
1654 child_id = parent_id;
1655 }
1656
1657 false
1658}
1659
1660pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1675 let ty = cx.typeck_results().expr_ty(expr);
1676 if let Some(Range { start, end, limits }) = Range::hir(expr) {
1677 let start_is_none_or_min = start.is_none_or(|start| {
1678 if let rustc_ty::Adt(_, subst) = ty.kind()
1679 && let bnd_ty = subst.type_at(0)
1680 && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx)
1681 && let Some(min_const) = mir_to_const(cx.tcx, min_const)
1682 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1683 {
1684 start_const == min_const
1685 } else {
1686 false
1687 }
1688 });
1689 let end_is_none_or_max = end.is_none_or(|end| match limits {
1690 RangeLimits::Closed => {
1691 if let rustc_ty::Adt(_, subst) = ty.kind()
1692 && let bnd_ty = subst.type_at(0)
1693 && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx)
1694 && let Some(max_const) = mir_to_const(cx.tcx, max_const)
1695 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1696 {
1697 end_const == max_const
1698 } else {
1699 false
1700 }
1701 },
1702 RangeLimits::HalfOpen => {
1703 if let Some(container_path) = container_path
1704 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1705 && name.ident.name == sym::len
1706 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1707 {
1708 container_path.res == path.res
1709 } else {
1710 false
1711 }
1712 },
1713 });
1714 return start_is_none_or_min && end_is_none_or_max;
1715 }
1716 false
1717}
1718
1719pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1722 if is_integer_literal(e, value) {
1723 return true;
1724 }
1725 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1726 if let Some(Constant::Int(v)) =
1727 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1728 {
1729 return value == v;
1730 }
1731 false
1732}
1733
1734pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1736 if let ExprKind::Lit(spanned) = expr.kind {
1738 if let LitKind::Int(v, _) = spanned.node {
1739 return v == value;
1740 }
1741 }
1742 false
1743}
1744
1745pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1747 if let ExprKind::Lit(spanned) = expr.kind
1748 && let LitKind::Float(v, _) = spanned.node
1749 {
1750 v.as_str().parse() == Ok(value)
1751 } else {
1752 false
1753 }
1754}
1755
1756pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1764 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1765}
1766
1767#[must_use]
1771pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
1772 loop {
1773 if span.from_expansion() {
1774 let data = span.ctxt().outer_expn_data();
1775 let new_span = data.call_site;
1776
1777 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
1778 if mac_name.as_str() == name {
1779 return Some(new_span);
1780 }
1781 }
1782
1783 span = new_span;
1784 } else {
1785 return None;
1786 }
1787 }
1788}
1789
1790#[must_use]
1801pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
1802 if span.from_expansion() {
1803 let data = span.ctxt().outer_expn_data();
1804 let new_span = data.call_site;
1805
1806 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
1807 if mac_name.as_str() == name {
1808 return Some(new_span);
1809 }
1810 }
1811 }
1812
1813 None
1814}
1815
1816pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1818 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1819 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1820}
1821
1822pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1824 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1825 cx.tcx.instantiate_bound_regions_with_erased(arg)
1826}
1827
1828pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1830 if let ExprKind::Call(fun, _) = expr.kind {
1831 if let ExprKind::Path(ref qp) = fun.kind {
1832 let res = cx.qpath_res(qp, fun.hir_id);
1833 return match res {
1834 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1835 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1836 _ => false,
1837 };
1838 }
1839 }
1840 false
1841}
1842
1843pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1846 fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1847 matches!(
1848 cx.qpath_res(qpath, id),
1849 Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
1850 )
1851 }
1852
1853 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1854 i.into_iter().any(|pat| is_refutable(cx, pat))
1855 }
1856
1857 match pat.kind {
1858 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1860 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1861 PatKind::Expr(PatExpr {
1862 kind: PatExprKind::Path(qpath),
1863 hir_id,
1864 ..
1865 }) => is_enum_variant(cx, qpath, *hir_id),
1866 PatKind::Or(pats) => {
1867 are_refutable(cx, pats)
1869 },
1870 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1871 PatKind::Struct(ref qpath, fields, _) => {
1872 is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1873 },
1874 PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
1875 PatKind::Slice(head, middle, tail) => {
1876 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1877 rustc_ty::Slice(..) => {
1878 !head.is_empty() || middle.is_none() || !tail.is_empty()
1880 },
1881 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1882 _ => {
1883 true
1885 },
1886 }
1887 },
1888 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1889 }
1890}
1891
1892pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1895 if let PatKind::Or(pats) = pat.kind {
1896 pats.iter().for_each(f);
1897 } else {
1898 f(pat);
1899 }
1900}
1901
1902pub fn is_self(slf: &Param<'_>) -> bool {
1903 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1904 name.name == kw::SelfLower
1905 } else {
1906 false
1907 }
1908}
1909
1910pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1911 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
1912 if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res {
1913 return true;
1914 }
1915 }
1916 false
1917}
1918
1919pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1920 (0..decl.inputs.len()).map(move |i| &body.params[i])
1921}
1922
1923pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1926 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1927 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1928 && ddpos.as_opt_usize().is_none()
1929 && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk)
1930 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1931 && path_to_local_id(arm.body, hir_id)
1932 {
1933 return true;
1934 }
1935 false
1936 }
1937
1938 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1939 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1940 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1941 } else {
1942 false
1943 }
1944 }
1945
1946 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1947 if let MatchSource::TryDesugar(_) = *source {
1949 return Some(expr);
1950 }
1951
1952 if arms.len() == 2
1953 && arms[0].guard.is_none()
1954 && arms[1].guard.is_none()
1955 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1956 {
1957 return Some(expr);
1958 }
1959 }
1960
1961 None
1962}
1963
1964pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1974 let mut suppress_lint = false;
1975
1976 for id in ids {
1977 let (level, _) = cx.tcx.lint_level_at_node(lint, id);
1978 if let Some(expectation) = level.get_expectation_id() {
1979 cx.fulfill_expectation(expectation);
1980 }
1981
1982 match level {
1983 Level::Allow | Level::Expect(_) => suppress_lint = true,
1984 Level::Warn | Level::ForceWarn(_) | Level::Deny | Level::Forbid => {},
1985 }
1986 }
1987
1988 suppress_lint
1989}
1990
1991pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1999 cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
2000}
2001
2002pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
2003 while let PatKind::Ref(subpat, _) = pat.kind {
2004 pat = subpat;
2005 }
2006 pat
2007}
2008
2009pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
2010 Integer::from_int_ty(&tcx, ity).size().bits()
2011}
2012
2013#[expect(clippy::cast_possible_wrap)]
2014pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
2016 let amt = 128 - int_bits(tcx, ity);
2017 ((u as i128) << amt) >> amt
2018}
2019
2020#[expect(clippy::cast_sign_loss)]
2021pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
2023 let amt = 128 - int_bits(tcx, ity);
2024 ((u as u128) << amt) >> amt
2025}
2026
2027pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
2029 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
2030 let amt = 128 - bits;
2031 (u << amt) >> amt
2032}
2033
2034pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
2035 attrs.iter().any(|attr| attr.has_name(symbol))
2036}
2037
2038pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2039 find_attr!(cx.tcx.hir().attrs(hir_id), AttributeKind::Repr(..))
2040}
2041
2042pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
2043 let map = &tcx.hir();
2044 let mut prev_enclosing_node = None;
2045 let mut enclosing_node = node;
2046 while Some(enclosing_node) != prev_enclosing_node {
2047 if has_attr(map.attrs(enclosing_node), symbol) {
2048 return true;
2049 }
2050 prev_enclosing_node = Some(enclosing_node);
2051 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
2052 }
2053
2054 false
2055}
2056
2057pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
2060 tcx.hir_parent_owner_iter(id)
2061 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
2062 .any(|(id, _)| {
2063 has_attr(
2064 tcx.hir().attrs(tcx.local_def_id_to_hir_id(id.def_id)),
2065 sym::automatically_derived,
2066 )
2067 })
2068}
2069
2070pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
2075 let search_path = cx.get_def_path(did);
2076 paths
2077 .iter()
2078 .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
2079}
2080
2081pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool {
2083 let path = cx.get_def_path(did);
2085 syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
2086}
2087
2088pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
2090 let path = cx.get_def_path(did);
2091 path.first().is_some_and(|s| s.as_str() == "libc") && path.last().is_some_and(|s| s.as_str() == name)
2094}
2095
2096pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
2101 let mut conds = Vec::new();
2102 let mut blocks: Vec<&Block<'_>> = Vec::new();
2103
2104 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
2105 conds.push(cond);
2106 if let ExprKind::Block(block, _) = then.kind {
2107 blocks.push(block);
2108 } else {
2109 panic!("ExprKind::If node is not an ExprKind::Block");
2110 }
2111
2112 if let Some(else_expr) = r#else {
2113 expr = else_expr;
2114 } else {
2115 break;
2116 }
2117 }
2118
2119 if !blocks.is_empty() {
2121 if let ExprKind::Block(block, _) = expr.kind {
2122 blocks.push(block);
2123 }
2124 }
2125
2126 (conds, blocks)
2127}
2128
2129pub fn is_async_fn(kind: FnKind<'_>) -> bool {
2131 match kind {
2132 FnKind::ItemFn(_, _, header) => header.asyncness.is_async(),
2133 FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
2134 FnKind::Closure => false,
2135 }
2136}
2137
2138pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
2140 if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind {
2141 if let ExprKind::Block(
2142 Block {
2143 stmts: [],
2144 expr:
2145 Some(Expr {
2146 kind: ExprKind::DropTemps(expr),
2147 ..
2148 }),
2149 ..
2150 },
2151 _,
2152 ) = tcx.hir_body(body).value.kind
2153 {
2154 return Some(expr);
2155 }
2156 }
2157 None
2158}
2159
2160pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2162 let did = match expr.kind {
2163 ExprKind::Call(path, _) => {
2164 if let ExprKind::Path(ref qpath) = path.kind
2165 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
2166 {
2167 Some(did)
2168 } else {
2169 None
2170 }
2171 },
2172 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
2173 _ => None,
2174 };
2175
2176 did.is_some_and(|did| cx.tcx.has_attr(did, sym::must_use))
2177}
2178
2179fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
2188 fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
2189 if cx
2190 .typeck_results()
2191 .pat_binding_modes()
2192 .get(pat.hir_id)
2193 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
2194 {
2195 return false;
2199 }
2200
2201 match (pat.kind, expr.kind) {
2202 (PatKind::Binding(_, id, _, _), _) => {
2203 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
2204 },
2205 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
2206 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
2207 {
2208 pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr))
2209 },
2210 _ => false,
2211 }
2212 }
2213
2214 let [param] = func.params else {
2215 return false;
2216 };
2217
2218 let mut expr = func.value;
2219 loop {
2220 match expr.kind {
2221 ExprKind::Block(
2222 &Block {
2223 stmts: [],
2224 expr: Some(e),
2225 ..
2226 },
2227 _,
2228 )
2229 | ExprKind::Ret(Some(e)) => expr = e,
2230 ExprKind::Block(
2231 &Block {
2232 stmts: [stmt],
2233 expr: None,
2234 ..
2235 },
2236 _,
2237 ) => {
2238 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
2239 && let ExprKind::Ret(Some(ret_val)) = e.kind
2240 {
2241 expr = ret_val;
2242 } else {
2243 return false;
2244 }
2245 },
2246 _ => return check_pat(cx, param.pat, expr),
2247 }
2248 }
2249}
2250
2251pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2256 match expr.kind {
2257 ExprKind::Closure(&Closure { body, fn_decl, .. })
2258 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
2259 {
2260 is_body_identity_function(cx, cx.tcx.hir_body(body))
2261 },
2262 ExprKind::Path(QPath::Resolved(_, path))
2263 if path.segments.iter().all(|seg| seg.infer_args)
2264 && let Some(did) = path.res.opt_def_id() =>
2265 {
2266 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
2267 },
2268 _ => false,
2269 }
2270}
2271
2272pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2281 match expr.kind {
2282 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
2283 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
2284 }
2285}
2286
2287pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2290 let mut child_id = expr.hir_id;
2291 let mut iter = tcx.hir_parent_iter(child_id);
2292 loop {
2293 match iter.next() {
2294 None => break None,
2295 Some((id, Node::Block(_))) => child_id = id,
2296 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2297 Some((_, Node::Expr(expr))) => match expr.kind {
2298 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2299 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2300 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2301 _ => break Some((Node::Expr(expr), child_id)),
2302 },
2303 Some((_, node)) => break Some((node, child_id)),
2304 }
2305 }
2306}
2307
2308pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2310 !matches!(
2311 get_expr_use_or_unification_node(tcx, expr),
2312 None | Some((
2313 Node::Stmt(Stmt {
2314 kind: StmtKind::Expr(_)
2315 | StmtKind::Semi(_)
2316 | StmtKind::Let(LetStmt {
2317 pat: Pat {
2318 kind: PatKind::Wild,
2319 ..
2320 },
2321 ..
2322 }),
2323 ..
2324 }),
2325 _
2326 ))
2327 )
2328}
2329
2330pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2332 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2333}
2334
2335pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2336 if !is_no_std_crate(cx) {
2337 Some("std")
2338 } else if !is_no_core_crate(cx) {
2339 Some("core")
2340 } else {
2341 None
2342 }
2343}
2344
2345pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2346 cx.tcx
2347 .hir()
2348 .attrs(hir::CRATE_HIR_ID)
2349 .iter()
2350 .any(|attr| attr.name_or_empty() == sym::no_std)
2351}
2352
2353pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2354 cx.tcx
2355 .hir()
2356 .attrs(hir::CRATE_HIR_ID)
2357 .iter()
2358 .any(|attr| attr.name_or_empty() == sym::no_core)
2359}
2360
2361pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2371 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2372 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2373 } else {
2374 false
2375 }
2376}
2377
2378pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2388 use rustc_trait_selection::traits;
2389 let predicates = cx
2390 .tcx
2391 .predicates_of(did)
2392 .predicates
2393 .iter()
2394 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2395 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2396}
2397
2398pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2400 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2401}
2402
2403pub fn fn_def_id_with_node_args<'tcx>(
2406 cx: &LateContext<'tcx>,
2407 expr: &Expr<'_>,
2408) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2409 let typeck = cx.typeck_results();
2410 match &expr.kind {
2411 ExprKind::MethodCall(..) => Some((
2412 typeck.type_dependent_def_id(expr.hir_id)?,
2413 typeck.node_args(expr.hir_id),
2414 )),
2415 ExprKind::Call(
2416 Expr {
2417 kind: ExprKind::Path(qpath),
2418 hir_id: path_hir_id,
2419 ..
2420 },
2421 ..,
2422 ) => {
2423 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2426 typeck.qpath_res(qpath, *path_hir_id)
2427 {
2428 Some((id, typeck.node_args(*path_hir_id)))
2429 } else {
2430 None
2431 }
2432 },
2433 _ => None,
2434 }
2435}
2436
2437pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2442 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2443 let expr_kind = expr_type.kind();
2444 let is_primitive = match expr_kind {
2445 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2446 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2447 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2448 is_recursively_primitive_type(*element_type)
2449 } else {
2450 unreachable!()
2451 }
2452 },
2453 _ => false,
2454 };
2455
2456 if is_primitive {
2457 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2460 rustc_ty::Slice(..) => return Some("slice".into()),
2461 rustc_ty::Array(..) => return Some("array".into()),
2462 rustc_ty::Tuple(..) => return Some("tuple".into()),
2463 _ => {
2464 let refs_peeled = expr_type.peel_refs();
2467 return Some(refs_peeled.walk().last().unwrap().to_string());
2468 },
2469 }
2470 }
2471 None
2472}
2473
2474pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<(&T, &T)>
2481where
2482 Hash: FnMut(&T) -> u64,
2483 Eq: FnMut(&T, &T) -> bool,
2484{
2485 match exprs {
2486 [a, b] if eq(a, b) => return vec![(a, b)],
2487 _ if exprs.len() <= 2 => return vec![],
2488 _ => {},
2489 }
2490
2491 let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
2492
2493 let mut map: UnhashMap<u64, Vec<&_>> =
2494 UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
2495
2496 for expr in exprs {
2497 match map.entry(hash(expr)) {
2498 Entry::Occupied(mut o) => {
2499 for o in o.get() {
2500 if eq(o, expr) {
2501 match_expr_list.push((o, expr));
2502 }
2503 }
2504 o.get_mut().push(expr);
2505 },
2506 Entry::Vacant(v) => {
2507 v.insert(vec![expr]);
2508 },
2509 }
2510 }
2511
2512 match_expr_list
2513}
2514
2515pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2518 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2519 if let PatKind::Ref(pat, _) = pat.kind {
2520 peel(pat, count + 1)
2521 } else {
2522 (pat, count)
2523 }
2524 }
2525 peel(pat, 0)
2526}
2527
2528pub fn peel_hir_expr_while<'tcx>(
2530 mut expr: &'tcx Expr<'tcx>,
2531 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2532) -> &'tcx Expr<'tcx> {
2533 while let Some(e) = f(expr) {
2534 expr = e;
2535 }
2536 expr
2537}
2538
2539pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2542 let mut remaining = count;
2543 let e = peel_hir_expr_while(expr, |e| match e.kind {
2544 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2545 remaining -= 1;
2546 Some(e)
2547 },
2548 _ => None,
2549 });
2550 (e, count - remaining)
2551}
2552
2553pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2556 let mut count: usize = 0;
2557 let mut curr_expr = expr;
2558 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2559 count = count.wrapping_add(1);
2560 curr_expr = local_expr;
2561 }
2562 (curr_expr, count)
2563}
2564
2565pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2568 let mut count = 0;
2569 let e = peel_hir_expr_while(expr, |e| match e.kind {
2570 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2571 count += 1;
2572 Some(e)
2573 },
2574 _ => None,
2575 });
2576 (e, count)
2577}
2578
2579pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2582 let mut count = 0;
2583 loop {
2584 match &ty.kind {
2585 TyKind::Ref(_, ref_ty) => {
2586 ty = ref_ty.ty;
2587 count += 1;
2588 },
2589 _ => break (ty, count),
2590 }
2591 }
2592}
2593
2594pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2597 let mut count = 0;
2598 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2599 ty = *dest_ty;
2600 count += 1;
2601 }
2602 (ty, count)
2603}
2604
2605pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2608 loop {
2609 match expr.kind {
2610 ExprKind::AddrOf(_, _, e) => expr = e,
2611 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2612 _ => break,
2613 }
2614 }
2615 expr
2616}
2617
2618pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2619 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2620 if let Res::Def(_, def_id) = path.res {
2621 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2622 }
2623 }
2624 false
2625}
2626
2627static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2628
2629fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
2630 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2631 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2632 let value = map.entry(module);
2633 match value {
2634 Entry::Occupied(entry) => f(entry.get()),
2635 Entry::Vacant(entry) => {
2636 let mut names = Vec::new();
2637 for id in tcx.hir_module_free_items(module) {
2638 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2639 && let item = tcx.hir_item(id)
2640 && let ItemKind::Const(ty, _generics, _body) = item.kind
2641 {
2642 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2643 if let Res::Def(DefKind::Struct, _) = path.res {
2645 let has_test_marker = tcx
2646 .hir()
2647 .attrs(item.hir_id())
2648 .iter()
2649 .any(|a| a.has_name(sym::rustc_test_marker));
2650 if has_test_marker {
2651 names.push(item.ident.name);
2652 }
2653 }
2654 }
2655 }
2656 }
2657 names.sort_unstable();
2658 f(entry.insert(names))
2659 },
2660 }
2661}
2662
2663pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2667 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2668 let node = tcx.hir_node(id);
2669 once((id, node))
2670 .chain(tcx.hir_parent_iter(id))
2671 .any(|(_id, node)| {
2674 if let Node::Item(item) = node {
2675 if let ItemKind::Fn { .. } = item.kind {
2676 return names.binary_search(&item.ident.name).is_ok();
2679 }
2680 }
2681 false
2682 })
2683 })
2684}
2685
2686pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2691 tcx.hir().attrs(id).iter().any(|attr| {
2692 if attr.has_name(sym::cfg)
2693 && let Some(items) = attr.meta_item_list()
2694 && let [item] = &*items
2695 && item.has_name(sym::test)
2696 {
2697 true
2698 } else {
2699 false
2700 }
2701 })
2702}
2703
2704pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2706 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2707}
2708
2709pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2711 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2712}
2713
2714pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2716 let hir = tcx.hir();
2717
2718 tcx.has_attr(def_id, sym::cfg)
2719 || tcx
2720 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2721 .flat_map(|(parent_id, _)| hir.attrs(parent_id))
2722 .any(|attr| attr.has_name(sym::cfg))
2723}
2724
2725pub fn walk_to_expr_usage<'tcx, T>(
2736 cx: &LateContext<'tcx>,
2737 e: &Expr<'tcx>,
2738 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2739) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2740 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2741 let mut child_id = e.hir_id;
2742
2743 while let Some((parent_id, parent)) = iter.next() {
2744 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2745 return Some(ControlFlow::Break(x));
2746 }
2747 let parent_expr = match parent {
2748 Node::Expr(e) => e,
2749 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2750 child_id = parent_id;
2751 continue;
2752 },
2753 Node::Arm(a) if a.body.hir_id == child_id => {
2754 child_id = parent_id;
2755 continue;
2756 },
2757 _ => return Some(ControlFlow::Continue((parent, child_id))),
2758 };
2759 match parent_expr.kind {
2760 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2761 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2762 child_id = id;
2763 iter = cx.tcx.hir_parent_iter(id);
2764 },
2765 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2766 _ => return Some(ControlFlow::Continue((parent, child_id))),
2767 }
2768 }
2769 debug_assert!(false, "no parent node found for `{child_id:?}`");
2770 None
2771}
2772
2773#[derive(Clone, Copy)]
2775pub enum DefinedTy<'tcx> {
2776 Hir(&'tcx hir::Ty<'tcx>),
2778 Mir {
2786 def_site_def_id: Option<DefId>,
2787 ty: Binder<'tcx, Ty<'tcx>>,
2788 },
2789}
2790
2791pub struct ExprUseCtxt<'tcx> {
2793 pub node: Node<'tcx>,
2795 pub child_id: HirId,
2797 pub adjustments: &'tcx [Adjustment<'tcx>],
2799 pub is_ty_unified: bool,
2801 pub moved_before_use: bool,
2803 pub same_ctxt: bool,
2805}
2806impl<'tcx> ExprUseCtxt<'tcx> {
2807 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2808 match self.node {
2809 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2810 Node::ExprField(field) => ExprUseNode::Field(field),
2811
2812 Node::Item(&Item {
2813 kind: ItemKind::Static(..) | ItemKind::Const(..),
2814 owner_id,
2815 ..
2816 })
2817 | Node::TraitItem(&TraitItem {
2818 kind: TraitItemKind::Const(..),
2819 owner_id,
2820 ..
2821 })
2822 | Node::ImplItem(&ImplItem {
2823 kind: ImplItemKind::Const(..),
2824 owner_id,
2825 ..
2826 }) => ExprUseNode::ConstStatic(owner_id),
2827
2828 Node::Item(&Item {
2829 kind: ItemKind::Fn { .. },
2830 owner_id,
2831 ..
2832 })
2833 | Node::TraitItem(&TraitItem {
2834 kind: TraitItemKind::Fn(..),
2835 owner_id,
2836 ..
2837 })
2838 | Node::ImplItem(&ImplItem {
2839 kind: ImplItemKind::Fn(..),
2840 owner_id,
2841 ..
2842 }) => ExprUseNode::Return(owner_id),
2843
2844 Node::Expr(use_expr) => match use_expr.kind {
2845 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2846 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2847 }),
2848
2849 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2850 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2851 Some(i) => ExprUseNode::FnArg(func, i),
2852 None => ExprUseNode::Callee,
2853 },
2854 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2855 use_expr.hir_id,
2856 name.args,
2857 args.iter()
2858 .position(|arg| arg.hir_id == self.child_id)
2859 .map_or(0, |i| i + 1),
2860 ),
2861 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2862 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2863 _ => ExprUseNode::Other,
2864 },
2865 _ => ExprUseNode::Other,
2866 }
2867 }
2868}
2869
2870pub enum ExprUseNode<'tcx> {
2872 LetStmt(&'tcx LetStmt<'tcx>),
2874 ConstStatic(OwnerId),
2876 Return(OwnerId),
2878 Field(&'tcx ExprField<'tcx>),
2880 FnArg(&'tcx Expr<'tcx>, usize),
2882 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2884 Callee,
2886 FieldAccess(Ident),
2888 AddrOf(ast::BorrowKind, Mutability),
2890 Other,
2891}
2892impl<'tcx> ExprUseNode<'tcx> {
2893 pub fn is_return(&self) -> bool {
2895 matches!(self, Self::Return(_))
2896 }
2897
2898 pub fn is_recv(&self) -> bool {
2900 matches!(self, Self::MethodArg(_, _, 0))
2901 }
2902
2903 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2905 match *self {
2906 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2907 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2908 def_site_def_id: Some(id.def_id.to_def_id()),
2909 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2910 }),
2911 Self::Return(id) => {
2912 if let Node::Expr(Expr {
2913 kind: ExprKind::Closure(c),
2914 ..
2915 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2916 {
2917 match c.fn_decl.output {
2918 FnRetTy::DefaultReturn(_) => None,
2919 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2920 }
2921 } else {
2922 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2923 Some(DefinedTy::Mir {
2924 def_site_def_id: Some(id.def_id.to_def_id()),
2925 ty,
2926 })
2927 }
2928 },
2929 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2930 Some(Expr {
2931 hir_id,
2932 kind: ExprKind::Struct(path, ..),
2933 ..
2934 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2935 .and_then(|(adt, variant)| {
2936 variant
2937 .fields
2938 .iter()
2939 .find(|f| f.name == field.ident.name)
2940 .map(|f| (adt, f))
2941 })
2942 .map(|(adt, field_def)| DefinedTy::Mir {
2943 def_site_def_id: Some(adt.did()),
2944 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2945 }),
2946 _ => None,
2947 },
2948 Self::FnArg(callee, i) => {
2949 let sig = expr_sig(cx, callee)?;
2950 let (hir_ty, ty) = sig.input_with_hir(i)?;
2951 Some(match hir_ty {
2952 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2953 None => DefinedTy::Mir {
2954 def_site_def_id: sig.predicates_id(),
2955 ty,
2956 },
2957 })
2958 },
2959 Self::MethodArg(id, _, i) => {
2960 let id = cx.typeck_results().type_dependent_def_id(id)?;
2961 let sig = cx.tcx.fn_sig(id).skip_binder();
2962 Some(DefinedTy::Mir {
2963 def_site_def_id: Some(id),
2964 ty: sig.input(i),
2965 })
2966 },
2967 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2968 }
2969 }
2970}
2971
2972pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2974 let mut adjustments = [].as_slice();
2975 let mut is_ty_unified = false;
2976 let mut moved_before_use = false;
2977 let mut same_ctxt = true;
2978 let ctxt = e.span.ctxt();
2979 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2980 if adjustments.is_empty()
2981 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2982 {
2983 adjustments = cx.typeck_results().expr_adjustments(e);
2984 }
2985 same_ctxt &= cx.tcx.hir().span(parent_id).ctxt() == ctxt;
2986 if let Node::Expr(e) = parent {
2987 match e.kind {
2988 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2989 is_ty_unified = true;
2990 moved_before_use = true;
2991 },
2992 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2993 is_ty_unified = true;
2994 moved_before_use = true;
2995 },
2996 ExprKind::Block(..) => moved_before_use = true,
2997 _ => {},
2998 }
2999 }
3000 ControlFlow::Continue(())
3001 });
3002 match node {
3003 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
3004 node,
3005 child_id,
3006 adjustments,
3007 is_ty_unified,
3008 moved_before_use,
3009 same_ctxt,
3010 },
3011 #[allow(unreachable_patterns)]
3012 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
3013 None => ExprUseCtxt {
3014 node: Node::Crate(cx.tcx.hir_root_module()),
3015 child_id: HirId::INVALID,
3016 adjustments: &[],
3017 is_ty_unified: true,
3018 moved_before_use: true,
3019 same_ctxt: false,
3020 },
3021 }
3022}
3023
3024pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
3026 let mut pos = 0;
3027 tokenize(s).map(move |t| {
3028 let end = pos + t.len;
3029 let range = pos as usize..end as usize;
3030 let inner = InnerSpan::new(range.start, range.end);
3031 pos = end;
3032 (t.kind, s.get(range).unwrap_or_default(), inner)
3033 })
3034}
3035
3036pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
3039 let Ok(snippet) = sm.span_to_snippet(span) else {
3040 return false;
3041 };
3042 return tokenize(&snippet).any(|token| {
3043 matches!(
3044 token.kind,
3045 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
3046 )
3047 });
3048}
3049
3050pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
3054 span_extract_comments(sm, span).join("\n")
3055}
3056
3057pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
3061 let snippet = sm.span_to_snippet(span).unwrap_or_default();
3062 tokenize_with_text(&snippet)
3063 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
3064 .map(|(_, s, _)| s.to_string())
3065 .collect::<Vec<_>>()
3066}
3067
3068pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
3069 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
3070}
3071
3072pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
3098 cx: &LateContext<'_>,
3099 pat: &'a Pat<'hir>,
3100 else_body: &Expr<'_>,
3101) -> Option<&'a Pat<'hir>> {
3102 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
3103 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
3104 && !is_refutable(cx, inner_pat)
3105 && let else_body = peel_blocks(else_body)
3106 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
3107 && let ExprKind::Path(ret_path) = ret_val.kind
3108 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
3109 {
3110 Some(inner_pat)
3111 } else {
3112 None
3113 }
3114}
3115
3116macro_rules! op_utils {
3117 ($($name:ident $assign:ident)*) => {
3118 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
3120
3121 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
3123
3124 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
3126 match kind {
3127 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
3128 _ => None,
3129 }
3130 }
3131 };
3132}
3133
3134op_utils! {
3135 Add AddAssign
3136 Sub SubAssign
3137 Mul MulAssign
3138 Div DivAssign
3139 Rem RemAssign
3140 BitXor BitXorAssign
3141 BitAnd BitAndAssign
3142 BitOr BitOrAssign
3143 Shl ShlAssign
3144 Shr ShrAssign
3145}
3146
3147pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
3150 match *pat {
3151 PatKind::Wild => true,
3152 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
3153 !visitors::is_local_used(cx, body, id)
3154 },
3155 _ => false,
3156 }
3157}
3158
3159#[derive(Clone, Copy)]
3160pub enum RequiresSemi {
3161 Yes,
3162 No,
3163}
3164impl RequiresSemi {
3165 pub fn requires_semi(self) -> bool {
3166 matches!(self, Self::Yes)
3167 }
3168}
3169
3170#[expect(clippy::too_many_lines)]
3173pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
3174 struct BreakTarget {
3175 id: HirId,
3176 unused: bool,
3177 }
3178
3179 struct V<'cx, 'tcx> {
3180 cx: &'cx LateContext<'tcx>,
3181 break_targets: Vec<BreakTarget>,
3182 break_targets_for_result_ty: u32,
3183 in_final_expr: bool,
3184 requires_semi: bool,
3185 is_never: bool,
3186 }
3187
3188 impl V<'_, '_> {
3189 fn push_break_target(&mut self, id: HirId) {
3190 self.break_targets.push(BreakTarget { id, unused: true });
3191 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3192 }
3193 }
3194
3195 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3196 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3197 if self.is_never && self.break_targets.is_empty() {
3214 if self.in_final_expr && !self.requires_semi {
3215 match e.kind {
3218 ExprKind::DropTemps(e) => self.visit_expr(e),
3219 ExprKind::If(_, then, Some(else_)) => {
3220 self.visit_expr(then);
3221 self.visit_expr(else_);
3222 },
3223 ExprKind::Match(_, arms, _) => {
3224 for arm in arms {
3225 self.visit_expr(arm.body);
3226 }
3227 },
3228 ExprKind::Loop(b, ..) => {
3229 self.push_break_target(e.hir_id);
3230 self.in_final_expr = false;
3231 self.visit_block(b);
3232 self.break_targets.pop();
3233 },
3234 ExprKind::Block(b, _) => {
3235 if b.targeted_by_break {
3236 self.push_break_target(b.hir_id);
3237 self.visit_block(b);
3238 self.break_targets.pop();
3239 } else {
3240 self.visit_block(b);
3241 }
3242 },
3243 _ => {
3244 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3245 },
3246 }
3247 }
3248 return;
3249 }
3250 match e.kind {
3251 ExprKind::DropTemps(e) => self.visit_expr(e),
3252 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3253 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3254 self.in_final_expr = false;
3255 self.visit_expr(e);
3256 self.is_never = true;
3257 },
3258 ExprKind::Break(dest, e) => {
3259 if let Some(e) = e {
3260 self.in_final_expr = false;
3261 self.visit_expr(e);
3262 }
3263 if let Ok(id) = dest.target_id
3264 && let Some((i, target)) = self
3265 .break_targets
3266 .iter_mut()
3267 .enumerate()
3268 .find(|(_, target)| target.id == id)
3269 {
3270 target.unused &= self.is_never;
3271 if i < self.break_targets_for_result_ty as usize {
3272 self.requires_semi = true;
3273 }
3274 }
3275 self.is_never = true;
3276 },
3277 ExprKind::If(cond, then, else_) => {
3278 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3279 self.visit_expr(cond);
3280 self.in_final_expr = in_final_expr;
3281
3282 if self.is_never {
3283 self.visit_expr(then);
3284 if let Some(else_) = else_ {
3285 self.visit_expr(else_);
3286 }
3287 } else {
3288 self.visit_expr(then);
3289 let is_never = mem::replace(&mut self.is_never, false);
3290 if let Some(else_) = else_ {
3291 self.visit_expr(else_);
3292 self.is_never &= is_never;
3293 }
3294 }
3295 },
3296 ExprKind::Match(scrutinee, arms, _) => {
3297 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3298 self.visit_expr(scrutinee);
3299 self.in_final_expr = in_final_expr;
3300
3301 if self.is_never {
3302 for arm in arms {
3303 self.visit_arm(arm);
3304 }
3305 } else {
3306 let mut is_never = true;
3307 for arm in arms {
3308 self.is_never = false;
3309 if let Some(guard) = arm.guard {
3310 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3311 self.visit_expr(guard);
3312 self.in_final_expr = in_final_expr;
3313 self.is_never = false;
3315 }
3316 self.visit_expr(arm.body);
3317 is_never &= self.is_never;
3318 }
3319 self.is_never = is_never;
3320 }
3321 },
3322 ExprKind::Loop(b, _, _, _) => {
3323 self.push_break_target(e.hir_id);
3324 self.in_final_expr = false;
3325 self.visit_block(b);
3326 self.is_never = self.break_targets.pop().unwrap().unused;
3327 },
3328 ExprKind::Block(b, _) => {
3329 if b.targeted_by_break {
3330 self.push_break_target(b.hir_id);
3331 self.visit_block(b);
3332 self.is_never &= self.break_targets.pop().unwrap().unused;
3333 } else {
3334 self.visit_block(b);
3335 }
3336 },
3337 _ => {
3338 self.in_final_expr = false;
3339 walk_expr(self, e);
3340 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3341 },
3342 }
3343 }
3344
3345 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3346 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3347 for s in b.stmts {
3348 self.visit_stmt(s);
3349 }
3350 self.in_final_expr = in_final_expr;
3351 if let Some(e) = b.expr {
3352 self.visit_expr(e);
3353 }
3354 }
3355
3356 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3357 if let Some(e) = l.init {
3358 self.visit_expr(e);
3359 }
3360 if let Some(else_) = l.els {
3361 let is_never = self.is_never;
3362 self.visit_block(else_);
3363 self.is_never = is_never;
3364 }
3365 }
3366
3367 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3368 if let Some(guard) = arm.guard {
3369 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3370 self.visit_expr(guard);
3371 self.in_final_expr = in_final_expr;
3372 }
3373 self.visit_expr(arm.body);
3374 }
3375 }
3376
3377 if cx.typeck_results().expr_ty(e).is_never() {
3378 Some(RequiresSemi::No)
3379 } else if let ExprKind::Block(b, _) = e.kind
3380 && !b.targeted_by_break
3381 && b.expr.is_none()
3382 {
3383 None
3385 } else {
3386 let mut v = V {
3387 cx,
3388 break_targets: Vec::new(),
3389 break_targets_for_result_ty: 0,
3390 in_final_expr: true,
3391 requires_semi: false,
3392 is_never: false,
3393 };
3394 v.visit_expr(e);
3395 v.is_never
3396 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3397 RequiresSemi::Yes
3398 } else {
3399 RequiresSemi::No
3400 })
3401 }
3402}
3403
3404pub fn get_path_from_caller_to_method_type<'tcx>(
3410 tcx: TyCtxt<'tcx>,
3411 from: LocalDefId,
3412 method: DefId,
3413 args: GenericArgsRef<'tcx>,
3414) -> String {
3415 let assoc_item = tcx.associated_item(method);
3416 let def_id = assoc_item.container_id(tcx);
3417 match assoc_item.container {
3418 rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
3419 rustc_ty::AssocItemContainer::Impl => {
3420 let ty = tcx.type_of(def_id).instantiate_identity();
3421 get_path_to_ty(tcx, from, ty, args)
3422 },
3423 }
3424}
3425
3426fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3427 match ty.kind() {
3428 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3429 rustc_ty::Array(..)
3431 | rustc_ty::Dynamic(..)
3432 | rustc_ty::Never
3433 | rustc_ty::RawPtr(_, _)
3434 | rustc_ty::Ref(..)
3435 | rustc_ty::Slice(_)
3436 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3437 _ => ty.to_string(),
3438 }
3439}
3440
3441fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3443 if callee.is_local() {
3445 let callee_path = tcx.def_path(callee);
3446 let caller_path = tcx.def_path(from.to_def_id());
3447 maybe_get_relative_path(&caller_path, &callee_path, 2)
3448 } else {
3449 tcx.def_path_str(callee)
3450 }
3451}
3452
3453fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3466 use itertools::EitherOrBoth::{Both, Left, Right};
3467
3468 let unique_parts = to
3470 .data
3471 .iter()
3472 .zip_longest(from.data.iter())
3473 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3474 .map(|el| match el {
3475 Both(l, r) => Both(l.data, r.data),
3476 Left(l) => Left(l.data),
3477 Right(r) => Right(r.data),
3478 });
3479
3480 let mut go_up_by = 0;
3482 let mut path = Vec::new();
3483 for el in unique_parts {
3484 match el {
3485 Both(l, r) => {
3486 if let DefPathData::TypeNs(Some(s)) = l {
3496 path.push(s.to_string());
3497 }
3498 if let DefPathData::TypeNs(_) = r {
3499 go_up_by += 1;
3500 }
3501 },
3502 Left(DefPathData::TypeNs(Some(sym))) => path.push(sym.to_string()),
3507 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3512 _ => {},
3513 }
3514 }
3515
3516 if go_up_by > max_super {
3517 once(String::from("crate"))
3519 .chain(to.data.iter().filter_map(|el| {
3520 if let DefPathData::TypeNs(Some(sym)) = el.data {
3521 Some(sym.to_string())
3522 } else {
3523 None
3524 }
3525 }))
3526 .join("::")
3527 } else {
3528 repeat_n(String::from("super"), go_up_by).chain(path).join("::")
3529 }
3530}
3531
3532pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3535 matches!(
3536 cx.tcx.parent_hir_node(id),
3537 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3538 )
3539}
3540
3541pub fn is_block_like(expr: &Expr<'_>) -> bool {
3544 matches!(
3545 expr.kind,
3546 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3547 )
3548}
3549
3550pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3552 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3553 match expr.kind {
3554 ExprKind::Binary(_, lhs, _) => contains_block(lhs, true),
3555 _ if is_block_like(expr) => is_operand,
3556 _ => false,
3557 }
3558 }
3559
3560 contains_block(expr, false)
3561}
3562
3563pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3565 if let Some(parent_expr) = get_parent_expr(cx, expr)
3566 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3567 && receiver.hir_id == expr.hir_id
3568 {
3569 return true;
3570 }
3571 false
3572}
3573
3574pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3577 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3578 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3579 && temporary_ty
3580 .walk()
3581 .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static()))
3582 {
3583 ControlFlow::Break(())
3584 } else {
3585 ControlFlow::Continue(())
3586 }
3587 })
3588 .is_break()
3589}
3590
3591pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3602 let expr_ty_is_adjusted = cx
3603 .typeck_results()
3604 .expr_adjustments(expr)
3605 .iter()
3606 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3608 if expr_ty_is_adjusted {
3609 return true;
3610 }
3611
3612 match expr.kind {
3615 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3616 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3617
3618 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3619 return false;
3620 }
3621
3622 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3623 let mut args_with_ty_param = {
3624 fn_sig
3625 .inputs()
3626 .skip_binder()
3627 .iter()
3628 .skip(self_arg_count)
3629 .zip(args)
3630 .filter_map(|(arg_ty, arg)| {
3631 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3632 Some(arg)
3633 } else {
3634 None
3635 }
3636 })
3637 };
3638 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3639 },
3640 ExprKind::Struct(qpath, _, _) => {
3642 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3643 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3644 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3645 return true;
3647 };
3648 v_def
3649 .fields
3650 .iter()
3651 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3652 } else {
3653 false
3654 }
3655 },
3656 ExprKind::Block(
3658 &Block {
3659 expr: Some(ret_expr), ..
3660 },
3661 _,
3662 )
3663 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3664
3665 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3667 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3669 ExprKind::If(_, then, maybe_else) => {
3671 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3672 },
3673 ExprKind::Match(_, arms, _) => arms
3674 .iter()
3675 .map(|arm| arm.body)
3676 .any(|body| expr_requires_coercion(cx, body)),
3677 _ => false,
3678 }
3679}
3680
3681pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3684 if let Some(hir_id) = path_to_local(expr)
3685 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3686 {
3687 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3688 } else if let ExprKind::Path(p) = &expr.kind
3689 && let Some(mutability) = cx
3690 .qpath_res(p, expr.hir_id)
3691 .opt_def_id()
3692 .and_then(|id| cx.tcx.static_mutability(id))
3693 {
3694 mutability == Mutability::Mut
3695 } else if let ExprKind::Field(parent, _) = expr.kind {
3696 is_mutable(cx, parent)
3697 } else {
3698 true
3699 }
3700}