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, GenericArg, GenericArgs, HirId, Impl, ImplItem,
110 ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
111 Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem,
112 TraitItemKind, 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 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(Ident::dummy(), 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| {
668 let ident = tcx.hir_item(item_id).kind.ident()?;
669 res(ident, item_id.owner_id)
670 })
671 .collect(),
672 ItemKind::Impl(r#impl) => r#impl
673 .items
674 .iter()
675 .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
676 .collect(),
677 ItemKind::Trait(.., trait_item_refs) => trait_item_refs
678 .iter()
679 .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
680 .collect(),
681 _ => Vec::new(),
682 }
683}
684
685fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
686 if let Some(local_id) = def_id.as_local() {
687 local_item_children_by_name(tcx, local_id, name)
688 } else {
689 non_local_item_children_by_name(tcx, def_id, name)
690 }
691}
692
693pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec<Res> {
695 tcx.crates(())
696 .iter()
697 .copied()
698 .filter(move |&num| tcx.crate_name(num) == name)
699 .map(CrateNum::as_def_id)
700 .map(|id| Res::Def(tcx.def_kind(id), id))
701 .collect()
702}
703
704pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec<Res> {
714 let (base, path) = match path {
715 [primitive] => {
716 return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
717 },
718 [base, path @ ..] => (base, path),
719 _ => return Vec::new(),
720 };
721
722 let base_sym = Symbol::intern(base);
723
724 let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
725 Some(LOCAL_CRATE.as_def_id())
726 } else {
727 None
728 };
729
730 let crates = find_primitive_impls(tcx, base)
731 .chain(local_crate)
732 .map(|id| Res::Def(tcx.def_kind(id), id))
733 .chain(find_crates(tcx, base_sym))
734 .collect();
735
736 def_path_res_with_base(tcx, crates, path)
737}
738
739pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec<Res>, mut path: &[&str]) -> Vec<Res> {
744 while let [segment, rest @ ..] = path {
745 path = rest;
746 let segment = Symbol::intern(segment);
747
748 base = base
749 .into_iter()
750 .filter_map(|res| res.opt_def_id())
751 .flat_map(|def_id| {
752 let inherent_impl_children = tcx
755 .inherent_impls(def_id)
756 .iter()
757 .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
758
759 let direct_children = item_children_by_name(tcx, def_id, segment);
760
761 inherent_impl_children.chain(direct_children)
762 })
763 .collect();
764 }
765
766 base
767}
768
769pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> + use<> {
771 def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id())
772}
773
774pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option<DefId> {
779 def_path_res(tcx, path).into_iter().find_map(|res| match res {
780 Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
781 _ => None,
782 })
783}
784
785pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
801 let hir_id = cx.tcx.local_def_id_to_hir_id(def_id);
803 let parent_impl = cx.tcx.hir_get_parent_item(hir_id);
804 if parent_impl != hir::CRATE_OWNER_ID
805 && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_impl.def_id)
806 && let ItemKind::Impl(impl_) = &item.kind
807 {
808 return impl_.of_trait.as_ref();
809 }
810 None
811}
812
813fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
821 let mut result = vec![];
822 let root = loop {
823 match e.kind {
824 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
825 result.push(e);
826 e = ep;
827 },
828 _ => break e,
829 }
830 };
831 result.reverse();
832 (result, root)
833}
834
835pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
837 cx.typeck_results()
838 .expr_adjustments(e)
839 .iter()
840 .find_map(|a| match a.kind {
841 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
842 Adjust::Deref(None) => None,
843 _ => Some(None),
844 })
845 .and_then(|x| x)
846}
847
848pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
851 let (s1, r1) = projection_stack(e1);
852 let (s2, r2) = projection_stack(e2);
853 if !eq_expr_value(cx, r1, r2) {
854 return true;
855 }
856 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
857 return false;
858 }
859
860 for (x1, x2) in s1.iter().zip(s2.iter()) {
861 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
862 return false;
863 }
864
865 match (&x1.kind, &x2.kind) {
866 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
867 if i1 != i2 {
868 return true;
869 }
870 },
871 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
872 if !eq_expr_value(cx, i1, i2) {
873 return false;
874 }
875 },
876 _ => return false,
877 }
878 }
879 false
880}
881
882fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
885 let std_types_symbols = &[
886 sym::Vec,
887 sym::VecDeque,
888 sym::LinkedList,
889 sym::HashMap,
890 sym::BTreeMap,
891 sym::HashSet,
892 sym::BTreeSet,
893 sym::BinaryHeap,
894 ];
895
896 if let QPath::TypeRelative(_, method) = path {
897 if method.ident.name == sym::new {
898 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
899 if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() {
900 return std_types_symbols.iter().any(|&symbol| {
901 cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
902 });
903 }
904 }
905 }
906 }
907 false
908}
909
910pub fn is_default_equivalent_call(
912 cx: &LateContext<'_>,
913 repl_func: &Expr<'_>,
914 whole_call_expr: Option<&Expr<'_>>,
915) -> bool {
916 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
917 && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
918 && (is_diag_trait_item(cx, repl_def_id, sym::Default)
919 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath))
920 {
921 return true;
922 }
923
924 let Some(e) = whole_call_expr else { return false };
927 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
928 return false;
929 };
930 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
931 return false;
932 };
933 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
934 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
935 cx.tcx.lifetimes.re_erased.into()
936 } else if param.index == 0 && param.name == kw::SelfUpper {
937 ty.into()
938 } else {
939 param.to_error(cx.tcx)
940 }
941 });
942 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
943
944 let Ok(Some(instance)) = instance else { return false };
945 if let rustc_ty::InstanceKind::Item(def) = instance.def
946 && !cx.tcx.is_mir_available(def)
947 {
948 return false;
949 }
950 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
951 return false;
952 };
953 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
954 return false;
955 };
956
957 let body = cx.tcx.instance_mir(instance.def);
963 for block_data in body.basic_blocks.iter() {
964 if block_data.statements.len() == 1
965 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
966 && assign.0.local == RETURN_PLACE
967 && let Rvalue::Aggregate(kind, _places) = &assign.1
968 && let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
969 && let def = cx.tcx.adt_def(did)
970 && let variant = &def.variant(*variant_index)
971 && variant.fields.is_empty()
972 && let Some((_, did)) = variant.ctor
973 && did == repl_def_id
974 {
975 return true;
976 } else if block_data.statements.is_empty()
977 && let Some(term) = &block_data.terminator
978 {
979 match &term.kind {
980 TerminatorKind::Call {
981 func: Operand::Constant(c),
982 ..
983 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
984 && *did == repl_def_id =>
985 {
986 return true;
987 },
988 TerminatorKind::TailCall {
989 func: Operand::Constant(c),
990 ..
991 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
992 && *did == repl_def_id =>
993 {
994 return true;
995 },
996 _ => {},
997 }
998 }
999 }
1000 false
1001}
1002
1003pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1007 match &e.kind {
1008 ExprKind::Lit(lit) => match lit.node {
1009 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
1010 LitKind::Str(s, _) => s.is_empty(),
1011 _ => false,
1012 },
1013 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
1014 ExprKind::Repeat(x, len) => {
1015 if let ConstArgKind::Anon(anon_const) = len.kind
1016 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
1017 && let LitKind::Int(v, _) = const_lit.node
1018 && v <= 32
1019 && is_default_equivalent(cx, x)
1020 {
1021 true
1022 } else {
1023 false
1024 }
1025 },
1026 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
1027 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
1028 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
1029 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
1030 _ => false,
1031 }
1032}
1033
1034fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
1035 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
1036 && seg.ident.name == sym::from
1037 {
1038 match arg.kind {
1039 ExprKind::Lit(hir::Lit {
1040 node: LitKind::Str(sym, _),
1041 ..
1042 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
1043 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
1044 ExprKind::Repeat(_, len) => {
1045 if let ConstArgKind::Anon(anon_const) = len.kind
1046 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
1047 && let LitKind::Int(v, _) = const_lit.node
1048 {
1049 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
1050 }
1051 },
1052 _ => (),
1053 }
1054 }
1055 false
1056}
1057
1058pub fn can_move_expr_to_closure_no_visit<'tcx>(
1090 cx: &LateContext<'tcx>,
1091 expr: &'tcx Expr<'_>,
1092 loop_ids: &[HirId],
1093 ignore_locals: &HirIdSet,
1094) -> bool {
1095 match expr.kind {
1096 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
1097 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
1098 if loop_ids.contains(&id) =>
1099 {
1100 true
1101 },
1102 ExprKind::Break(..)
1103 | ExprKind::Continue(_)
1104 | ExprKind::Ret(_)
1105 | ExprKind::Yield(..)
1106 | ExprKind::InlineAsm(_) => false,
1107 ExprKind::Field(
1110 &Expr {
1111 hir_id,
1112 kind:
1113 ExprKind::Path(QPath::Resolved(
1114 _,
1115 Path {
1116 res: Res::Local(local_id),
1117 ..
1118 },
1119 )),
1120 ..
1121 },
1122 _,
1123 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
1124 false
1126 },
1127 _ => true,
1128 }
1129}
1130
1131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1133pub enum CaptureKind {
1134 Value,
1135 Use,
1136 Ref(Mutability),
1137}
1138impl CaptureKind {
1139 pub fn is_imm_ref(self) -> bool {
1140 self == Self::Ref(Mutability::Not)
1141 }
1142}
1143impl std::ops::BitOr for CaptureKind {
1144 type Output = Self;
1145 fn bitor(self, rhs: Self) -> Self::Output {
1146 match (self, rhs) {
1147 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
1148 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
1149 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
1150 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
1151 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
1152 }
1153 }
1154}
1155impl std::ops::BitOrAssign for CaptureKind {
1156 fn bitor_assign(&mut self, rhs: Self) {
1157 *self = *self | rhs;
1158 }
1159}
1160
1161pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
1167 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
1168 let mut capture = CaptureKind::Ref(Mutability::Not);
1169 pat.each_binding_or_first(&mut |_, id, span, _| match cx
1170 .typeck_results()
1171 .extract_binding_mode(cx.sess(), id, span)
1172 .0
1173 {
1174 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
1175 capture = CaptureKind::Value;
1176 },
1177 ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
1178 capture = CaptureKind::Ref(Mutability::Mut);
1179 },
1180 _ => (),
1181 });
1182 capture
1183 }
1184
1185 debug_assert!(matches!(
1186 e.kind,
1187 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
1188 ));
1189
1190 let mut child_id = e.hir_id;
1191 let mut capture = CaptureKind::Value;
1192 let mut capture_expr_ty = e;
1193
1194 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
1195 if let [
1196 Adjustment {
1197 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
1198 target,
1199 },
1200 ref adjust @ ..,
1201 ] = *cx
1202 .typeck_results()
1203 .adjustments()
1204 .get(child_id)
1205 .map_or(&[][..], |x| &**x)
1206 {
1207 if let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
1208 *adjust.last().map_or(target, |a| a.target).kind()
1209 {
1210 return CaptureKind::Ref(mutability);
1211 }
1212 }
1213
1214 match parent {
1215 Node::Expr(e) => match e.kind {
1216 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
1217 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
1218 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
1219 return CaptureKind::Ref(Mutability::Mut);
1220 },
1221 ExprKind::Field(..) => {
1222 if capture == CaptureKind::Value {
1223 capture_expr_ty = e;
1224 }
1225 },
1226 ExprKind::Let(let_expr) => {
1227 let mutability = match pat_capture_kind(cx, let_expr.pat) {
1228 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
1229 CaptureKind::Ref(m) => m,
1230 };
1231 return CaptureKind::Ref(mutability);
1232 },
1233 ExprKind::Match(_, arms, _) => {
1234 let mut mutability = Mutability::Not;
1235 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
1236 match capture {
1237 CaptureKind::Value | CaptureKind::Use => break,
1238 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
1239 CaptureKind::Ref(Mutability::Not) => (),
1240 }
1241 }
1242 return CaptureKind::Ref(mutability);
1243 },
1244 _ => break,
1245 },
1246 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
1247 CaptureKind::Value | CaptureKind::Use => break,
1248 capture @ CaptureKind::Ref(_) => return capture,
1249 },
1250 _ => break,
1251 }
1252
1253 child_id = parent_id;
1254 }
1255
1256 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
1257 CaptureKind::Ref(Mutability::Not)
1259 } else {
1260 capture
1261 }
1262}
1263
1264pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
1267 struct V<'cx, 'tcx> {
1268 cx: &'cx LateContext<'tcx>,
1269 loops: Vec<HirId>,
1271 locals: HirIdSet,
1273 allow_closure: bool,
1275 captures: HirIdMap<CaptureKind>,
1278 }
1279 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1280 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1281 if !self.allow_closure {
1282 return;
1283 }
1284
1285 match e.kind {
1286 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1287 if !self.locals.contains(&l) {
1288 let cap = capture_local_usage(self.cx, e);
1289 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1290 }
1291 },
1292 ExprKind::Closure(closure) => {
1293 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
1294 let local_id = match capture.place.base {
1295 PlaceBase::Local(id) => id,
1296 PlaceBase::Upvar(var) => var.var_path.hir_id,
1297 _ => continue,
1298 };
1299 if !self.locals.contains(&local_id) {
1300 let capture = match capture.info.capture_kind {
1301 UpvarCapture::ByValue => CaptureKind::Value,
1302 UpvarCapture::ByUse => CaptureKind::Use,
1303 UpvarCapture::ByRef(kind) => match kind {
1304 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
1305 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
1306 CaptureKind::Ref(Mutability::Mut)
1307 },
1308 },
1309 };
1310 self.captures
1311 .entry(local_id)
1312 .and_modify(|e| *e |= capture)
1313 .or_insert(capture);
1314 }
1315 }
1316 },
1317 ExprKind::Loop(b, ..) => {
1318 self.loops.push(e.hir_id);
1319 self.visit_block(b);
1320 self.loops.pop();
1321 },
1322 _ => {
1323 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1324 walk_expr(self, e);
1325 },
1326 }
1327 }
1328
1329 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1330 p.each_binding_or_first(&mut |_, id, _, _| {
1331 self.locals.insert(id);
1332 });
1333 }
1334 }
1335
1336 let mut v = V {
1337 cx,
1338 loops: Vec::new(),
1339 locals: HirIdSet::default(),
1340 allow_closure: true,
1341 captures: HirIdMap::default(),
1342 };
1343 v.visit_expr(expr);
1344 v.allow_closure.then_some(v.captures)
1345}
1346
1347pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1349
1350pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1353 let mut method_names = Vec::with_capacity(max_depth);
1354 let mut arg_lists = Vec::with_capacity(max_depth);
1355 let mut spans = Vec::with_capacity(max_depth);
1356
1357 let mut current = expr;
1358 for _ in 0..max_depth {
1359 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1360 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1361 break;
1362 }
1363 method_names.push(path.ident.name);
1364 arg_lists.push((*receiver, &**args));
1365 spans.push(path.ident.span);
1366 current = receiver;
1367 } else {
1368 break;
1369 }
1370 }
1371
1372 (method_names, arg_lists, spans)
1373}
1374
1375pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1382 let mut current = expr;
1383 let mut matched = Vec::with_capacity(methods.len());
1384 for method_name in methods.iter().rev() {
1385 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1387 if path.ident.name.as_str() == *method_name {
1388 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1389 return None;
1390 }
1391 matched.push((receiver, args)); current = receiver; } else {
1394 return None;
1395 }
1396 } else {
1397 return None;
1398 }
1399 }
1400 matched.reverse();
1402 Some(matched)
1403}
1404
1405pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1407 cx.tcx
1408 .entry_fn(())
1409 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1410}
1411
1412pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1414 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1415 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1416}
1417
1418pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1420 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1421 match cx.tcx.hir_node_by_def_id(parent_id) {
1422 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1423 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1424 _ => None,
1425 }
1426}
1427
1428pub struct ContainsName<'a, 'tcx> {
1429 pub cx: &'a LateContext<'tcx>,
1430 pub name: Symbol,
1431}
1432
1433impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1434 type Result = ControlFlow<()>;
1435 type NestedFilter = nested_filter::OnlyBodies;
1436
1437 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1438 if self.name == name {
1439 ControlFlow::Break(())
1440 } else {
1441 ControlFlow::Continue(())
1442 }
1443 }
1444
1445 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1446 self.cx.tcx
1447 }
1448}
1449
1450pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1452 let mut cn = ContainsName { cx, name };
1453 cn.visit_expr(expr).is_break()
1454}
1455
1456pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1458 for_each_expr_without_closures(expr, |e| {
1459 if matches!(e.kind, ExprKind::Ret(..)) {
1460 ControlFlow::Break(())
1461 } else {
1462 ControlFlow::Continue(())
1463 }
1464 })
1465 .is_some()
1466}
1467
1468pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1470 get_parent_expr_for_hir(cx, e.hir_id)
1471}
1472
1473pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1476 match cx.tcx.parent_hir_node(hir_id) {
1477 Node::Expr(parent) => Some(parent),
1478 _ => None,
1479 }
1480}
1481
1482pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1484 let enclosing_node = cx
1485 .tcx
1486 .hir_get_enclosing_scope(hir_id)
1487 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1488 enclosing_node.and_then(|node| match node {
1489 Node::Block(block) => Some(block),
1490 Node::Item(&Item {
1491 kind: ItemKind::Fn { body: eid, .. },
1492 ..
1493 })
1494 | Node::ImplItem(&ImplItem {
1495 kind: ImplItemKind::Fn(_, eid),
1496 ..
1497 })
1498 | Node::TraitItem(&TraitItem {
1499 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1500 ..
1501 }) => match cx.tcx.hir_body(eid).value.kind {
1502 ExprKind::Block(block, _) => Some(block),
1503 _ => None,
1504 },
1505 _ => None,
1506 })
1507}
1508
1509pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1511 cx: &LateContext<'tcx>,
1512 expr: &Expr<'_>,
1513) -> Option<&'tcx Expr<'tcx>> {
1514 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1515 match node {
1516 Node::Expr(e) => match e.kind {
1517 ExprKind::Closure { .. }
1518 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1519 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1520
1521 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1523 _ => (),
1524 },
1525 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1526 _ => break,
1527 }
1528 }
1529 None
1530}
1531
1532pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1534 match tcx.hir_parent_iter(id).next() {
1535 Some((
1536 _,
1537 Node::Item(Item {
1538 kind: ItemKind::Impl(imp),
1539 ..
1540 }),
1541 )) => Some(imp),
1542 _ => None,
1543 }
1544}
1545
1546pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1557 while let ExprKind::Block(
1558 Block {
1559 stmts: [],
1560 expr: Some(inner),
1561 rules: BlockCheckMode::DefaultBlock,
1562 ..
1563 },
1564 _,
1565 ) = expr.kind
1566 {
1567 expr = inner;
1568 }
1569 expr
1570}
1571
1572pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1583 while let ExprKind::Block(
1584 Block {
1585 stmts: [],
1586 expr: Some(inner),
1587 rules: BlockCheckMode::DefaultBlock,
1588 ..
1589 }
1590 | Block {
1591 stmts:
1592 [
1593 Stmt {
1594 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1595 ..
1596 },
1597 ],
1598 expr: None,
1599 rules: BlockCheckMode::DefaultBlock,
1600 ..
1601 },
1602 _,
1603 ) = expr.kind
1604 {
1605 expr = inner;
1606 }
1607 expr
1608}
1609
1610pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1612 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1613 match iter.next() {
1614 Some((
1615 _,
1616 Node::Expr(Expr {
1617 kind: ExprKind::If(_, _, Some(else_expr)),
1618 ..
1619 }),
1620 )) => else_expr.hir_id == expr.hir_id,
1621 _ => false,
1622 }
1623}
1624
1625pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1628 let mut child_id = expr.hir_id;
1629 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1630 if let Node::LetStmt(LetStmt {
1631 init: Some(init),
1632 els: Some(els),
1633 ..
1634 }) = node
1635 && (init.hir_id == child_id || els.hir_id == child_id)
1636 {
1637 return true;
1638 }
1639
1640 child_id = parent_id;
1641 }
1642
1643 false
1644}
1645
1646pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1648 let mut child_id = expr.hir_id;
1649 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1650 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1651 && els.hir_id == child_id
1652 {
1653 return true;
1654 }
1655
1656 child_id = parent_id;
1657 }
1658
1659 false
1660}
1661
1662pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1677 let ty = cx.typeck_results().expr_ty(expr);
1678 if let Some(Range { start, end, limits }) = Range::hir(expr) {
1679 let start_is_none_or_min = start.is_none_or(|start| {
1680 if let rustc_ty::Adt(_, subst) = ty.kind()
1681 && let bnd_ty = subst.type_at(0)
1682 && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx)
1683 && let Some(min_const) = mir_to_const(cx.tcx, min_const)
1684 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1685 {
1686 start_const == min_const
1687 } else {
1688 false
1689 }
1690 });
1691 let end_is_none_or_max = end.is_none_or(|end| match limits {
1692 RangeLimits::Closed => {
1693 if let rustc_ty::Adt(_, subst) = ty.kind()
1694 && let bnd_ty = subst.type_at(0)
1695 && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx)
1696 && let Some(max_const) = mir_to_const(cx.tcx, max_const)
1697 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1698 {
1699 end_const == max_const
1700 } else {
1701 false
1702 }
1703 },
1704 RangeLimits::HalfOpen => {
1705 if let Some(container_path) = container_path
1706 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1707 && name.ident.name == sym::len
1708 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1709 {
1710 container_path.res == path.res
1711 } else {
1712 false
1713 }
1714 },
1715 });
1716 return start_is_none_or_min && end_is_none_or_max;
1717 }
1718 false
1719}
1720
1721pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1724 if is_integer_literal(e, value) {
1725 return true;
1726 }
1727 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1728 if let Some(Constant::Int(v)) =
1729 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1730 {
1731 return value == v;
1732 }
1733 false
1734}
1735
1736pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1738 if let ExprKind::Lit(spanned) = expr.kind {
1740 if let LitKind::Int(v, _) = spanned.node {
1741 return v == value;
1742 }
1743 }
1744 false
1745}
1746
1747pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1749 if let ExprKind::Lit(spanned) = expr.kind
1750 && let LitKind::Float(v, _) = spanned.node
1751 {
1752 v.as_str().parse() == Ok(value)
1753 } else {
1754 false
1755 }
1756}
1757
1758pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1766 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1767}
1768
1769#[must_use]
1773pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
1774 loop {
1775 if span.from_expansion() {
1776 let data = span.ctxt().outer_expn_data();
1777 let new_span = data.call_site;
1778
1779 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
1780 if mac_name.as_str() == name {
1781 return Some(new_span);
1782 }
1783 }
1784
1785 span = new_span;
1786 } else {
1787 return None;
1788 }
1789 }
1790}
1791
1792#[must_use]
1803pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
1804 if span.from_expansion() {
1805 let data = span.ctxt().outer_expn_data();
1806 let new_span = data.call_site;
1807
1808 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
1809 if mac_name.as_str() == name {
1810 return Some(new_span);
1811 }
1812 }
1813 }
1814
1815 None
1816}
1817
1818pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1820 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1821 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1822}
1823
1824pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1826 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1827 cx.tcx.instantiate_bound_regions_with_erased(arg)
1828}
1829
1830pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1832 if let ExprKind::Call(fun, _) = expr.kind {
1833 if let ExprKind::Path(ref qp) = fun.kind {
1834 let res = cx.qpath_res(qp, fun.hir_id);
1835 return match res {
1836 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1837 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1838 _ => false,
1839 };
1840 }
1841 }
1842 false
1843}
1844
1845pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1848 fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1849 matches!(
1850 cx.qpath_res(qpath, id),
1851 Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
1852 )
1853 }
1854
1855 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1856 i.into_iter().any(|pat| is_refutable(cx, pat))
1857 }
1858
1859 match pat.kind {
1860 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1862 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1863 PatKind::Expr(PatExpr {
1864 kind: PatExprKind::Path(qpath),
1865 hir_id,
1866 ..
1867 }) => is_enum_variant(cx, qpath, *hir_id),
1868 PatKind::Or(pats) => {
1869 are_refutable(cx, pats)
1871 },
1872 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1873 PatKind::Struct(ref qpath, fields, _) => {
1874 is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1875 },
1876 PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
1877 PatKind::Slice(head, middle, tail) => {
1878 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1879 rustc_ty::Slice(..) => {
1880 !head.is_empty() || middle.is_none() || !tail.is_empty()
1882 },
1883 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1884 _ => {
1885 true
1887 },
1888 }
1889 },
1890 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1891 }
1892}
1893
1894pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1897 if let PatKind::Or(pats) = pat.kind {
1898 pats.iter().for_each(f);
1899 } else {
1900 f(pat);
1901 }
1902}
1903
1904pub fn is_self(slf: &Param<'_>) -> bool {
1905 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1906 name.name == kw::SelfLower
1907 } else {
1908 false
1909 }
1910}
1911
1912pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1913 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
1914 if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res {
1915 return true;
1916 }
1917 }
1918 false
1919}
1920
1921pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1922 (0..decl.inputs.len()).map(move |i| &body.params[i])
1923}
1924
1925pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1928 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1929 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1930 && ddpos.as_opt_usize().is_none()
1931 && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk)
1932 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1933 && path_to_local_id(arm.body, hir_id)
1934 {
1935 return true;
1936 }
1937 false
1938 }
1939
1940 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1941 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1942 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1943 } else {
1944 false
1945 }
1946 }
1947
1948 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1949 if let MatchSource::TryDesugar(_) = *source {
1951 return Some(expr);
1952 }
1953
1954 if arms.len() == 2
1955 && arms[0].guard.is_none()
1956 && arms[1].guard.is_none()
1957 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1958 {
1959 return Some(expr);
1960 }
1961 }
1962
1963 None
1964}
1965
1966pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1976 let mut suppress_lint = false;
1977
1978 for id in ids {
1979 let (level, _) = cx.tcx.lint_level_at_node(lint, id);
1980 if let Some(expectation) = level.get_expectation_id() {
1981 cx.fulfill_expectation(expectation);
1982 }
1983
1984 match level {
1985 Level::Allow | Level::Expect(_) => suppress_lint = true,
1986 Level::Warn | Level::ForceWarn(_) | Level::Deny | Level::Forbid => {},
1987 }
1988 }
1989
1990 suppress_lint
1991}
1992
1993pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
2001 cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
2002}
2003
2004pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
2005 while let PatKind::Ref(subpat, _) = pat.kind {
2006 pat = subpat;
2007 }
2008 pat
2009}
2010
2011pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
2012 Integer::from_int_ty(&tcx, ity).size().bits()
2013}
2014
2015#[expect(clippy::cast_possible_wrap)]
2016pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
2018 let amt = 128 - int_bits(tcx, ity);
2019 ((u as i128) << amt) >> amt
2020}
2021
2022#[expect(clippy::cast_sign_loss)]
2023pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
2025 let amt = 128 - int_bits(tcx, ity);
2026 ((u as u128) << amt) >> amt
2027}
2028
2029pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
2031 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
2032 let amt = 128 - bits;
2033 (u << amt) >> amt
2034}
2035
2036pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
2037 attrs.iter().any(|attr| attr.has_name(symbol))
2038}
2039
2040pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2041 find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr(..))
2042}
2043
2044pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
2045 let mut prev_enclosing_node = None;
2046 let mut enclosing_node = node;
2047 while Some(enclosing_node) != prev_enclosing_node {
2048 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
2049 return true;
2050 }
2051 prev_enclosing_node = Some(enclosing_node);
2052 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
2053 }
2054
2055 false
2056}
2057
2058pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
2061 tcx.hir_parent_owner_iter(id)
2062 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
2063 .any(|(id, _)| {
2064 has_attr(
2065 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
2066 sym::automatically_derived,
2067 )
2068 })
2069}
2070
2071pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
2076 let search_path = cx.get_def_path(did);
2077 paths
2078 .iter()
2079 .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
2080}
2081
2082pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool {
2084 let path = cx.get_def_path(did);
2086 syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
2087}
2088
2089pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
2091 let path = cx.get_def_path(did);
2092 path.first().is_some_and(|s| s.as_str() == "libc") && path.last().is_some_and(|s| s.as_str() == name)
2095}
2096
2097pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
2102 let mut conds = Vec::new();
2103 let mut blocks: Vec<&Block<'_>> = Vec::new();
2104
2105 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
2106 conds.push(cond);
2107 if let ExprKind::Block(block, _) = then.kind {
2108 blocks.push(block);
2109 } else {
2110 panic!("ExprKind::If node is not an ExprKind::Block");
2111 }
2112
2113 if let Some(else_expr) = r#else {
2114 expr = else_expr;
2115 } else {
2116 break;
2117 }
2118 }
2119
2120 if !blocks.is_empty() {
2122 if let ExprKind::Block(block, _) = expr.kind {
2123 blocks.push(block);
2124 }
2125 }
2126
2127 (conds, blocks)
2128}
2129
2130pub fn is_async_fn(kind: FnKind<'_>) -> bool {
2132 match kind {
2133 FnKind::ItemFn(_, _, header) => header.asyncness.is_async(),
2134 FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
2135 FnKind::Closure => false,
2136 }
2137}
2138
2139pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
2141 if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind {
2142 if let ExprKind::Block(
2143 Block {
2144 stmts: [],
2145 expr:
2146 Some(Expr {
2147 kind: ExprKind::DropTemps(expr),
2148 ..
2149 }),
2150 ..
2151 },
2152 _,
2153 ) = tcx.hir_body(body).value.kind
2154 {
2155 return Some(expr);
2156 }
2157 }
2158 None
2159}
2160
2161pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2163 let did = match expr.kind {
2164 ExprKind::Call(path, _) => {
2165 if let ExprKind::Path(ref qpath) = path.kind
2166 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
2167 {
2168 Some(did)
2169 } else {
2170 None
2171 }
2172 },
2173 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
2174 _ => None,
2175 };
2176
2177 did.is_some_and(|did| cx.tcx.has_attr(did, sym::must_use))
2178}
2179
2180fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
2189 fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
2190 if cx
2191 .typeck_results()
2192 .pat_binding_modes()
2193 .get(pat.hir_id)
2194 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
2195 {
2196 return false;
2200 }
2201
2202 match (pat.kind, expr.kind) {
2203 (PatKind::Binding(_, id, _, _), _) => {
2204 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
2205 },
2206 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
2207 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
2208 {
2209 pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr))
2210 },
2211 _ => false,
2212 }
2213 }
2214
2215 let [param] = func.params else {
2216 return false;
2217 };
2218
2219 let mut expr = func.value;
2220 loop {
2221 match expr.kind {
2222 ExprKind::Block(
2223 &Block {
2224 stmts: [],
2225 expr: Some(e),
2226 ..
2227 },
2228 _,
2229 )
2230 | ExprKind::Ret(Some(e)) => expr = e,
2231 ExprKind::Block(
2232 &Block {
2233 stmts: [stmt],
2234 expr: None,
2235 ..
2236 },
2237 _,
2238 ) => {
2239 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
2240 && let ExprKind::Ret(Some(ret_val)) = e.kind
2241 {
2242 expr = ret_val;
2243 } else {
2244 return false;
2245 }
2246 },
2247 _ => return check_pat(cx, param.pat, expr),
2248 }
2249 }
2250}
2251
2252pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2257 match expr.kind {
2258 ExprKind::Closure(&Closure { body, fn_decl, .. })
2259 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
2260 {
2261 is_body_identity_function(cx, cx.tcx.hir_body(body))
2262 },
2263 ExprKind::Path(QPath::Resolved(_, path))
2264 if path.segments.iter().all(|seg| seg.infer_args)
2265 && let Some(did) = path.res.opt_def_id() =>
2266 {
2267 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
2268 },
2269 _ => false,
2270 }
2271}
2272
2273pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2282 match expr.kind {
2283 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
2284 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
2285 }
2286}
2287
2288pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2291 let mut child_id = expr.hir_id;
2292 let mut iter = tcx.hir_parent_iter(child_id);
2293 loop {
2294 match iter.next() {
2295 None => break None,
2296 Some((id, Node::Block(_))) => child_id = id,
2297 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2298 Some((_, Node::Expr(expr))) => match expr.kind {
2299 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2300 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2301 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2302 _ => break Some((Node::Expr(expr), child_id)),
2303 },
2304 Some((_, node)) => break Some((node, child_id)),
2305 }
2306 }
2307}
2308
2309pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2311 !matches!(
2312 get_expr_use_or_unification_node(tcx, expr),
2313 None | Some((
2314 Node::Stmt(Stmt {
2315 kind: StmtKind::Expr(_)
2316 | StmtKind::Semi(_)
2317 | StmtKind::Let(LetStmt {
2318 pat: Pat {
2319 kind: PatKind::Wild,
2320 ..
2321 },
2322 ..
2323 }),
2324 ..
2325 }),
2326 _
2327 ))
2328 )
2329}
2330
2331pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2333 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2334}
2335
2336pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2340 !expr.is_place_expr(|base| {
2341 cx.typeck_results()
2342 .adjustments()
2343 .get(base.hir_id)
2344 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2345 })
2346}
2347
2348pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2349 if !is_no_std_crate(cx) {
2350 Some("std")
2351 } else if !is_no_core_crate(cx) {
2352 Some("core")
2353 } else {
2354 None
2355 }
2356}
2357
2358pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2359 cx.tcx
2360 .hir_attrs(hir::CRATE_HIR_ID)
2361 .iter()
2362 .any(|attr| attr.name_or_empty() == sym::no_std)
2363}
2364
2365pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2366 cx.tcx
2367 .hir_attrs(hir::CRATE_HIR_ID)
2368 .iter()
2369 .any(|attr| attr.name_or_empty() == sym::no_core)
2370}
2371
2372pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2382 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2383 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2384 } else {
2385 false
2386 }
2387}
2388
2389pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2399 use rustc_trait_selection::traits;
2400 let predicates = cx
2401 .tcx
2402 .predicates_of(did)
2403 .predicates
2404 .iter()
2405 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2406 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2407}
2408
2409pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2411 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2412}
2413
2414pub fn fn_def_id_with_node_args<'tcx>(
2417 cx: &LateContext<'tcx>,
2418 expr: &Expr<'_>,
2419) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2420 let typeck = cx.typeck_results();
2421 match &expr.kind {
2422 ExprKind::MethodCall(..) => Some((
2423 typeck.type_dependent_def_id(expr.hir_id)?,
2424 typeck.node_args(expr.hir_id),
2425 )),
2426 ExprKind::Call(
2427 Expr {
2428 kind: ExprKind::Path(qpath),
2429 hir_id: path_hir_id,
2430 ..
2431 },
2432 ..,
2433 ) => {
2434 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2437 typeck.qpath_res(qpath, *path_hir_id)
2438 {
2439 Some((id, typeck.node_args(*path_hir_id)))
2440 } else {
2441 None
2442 }
2443 },
2444 _ => None,
2445 }
2446}
2447
2448pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2453 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2454 let expr_kind = expr_type.kind();
2455 let is_primitive = match expr_kind {
2456 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2457 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2458 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2459 is_recursively_primitive_type(*element_type)
2460 } else {
2461 unreachable!()
2462 }
2463 },
2464 _ => false,
2465 };
2466
2467 if is_primitive {
2468 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2471 rustc_ty::Slice(..) => return Some("slice".into()),
2472 rustc_ty::Array(..) => return Some("array".into()),
2473 rustc_ty::Tuple(..) => return Some("tuple".into()),
2474 _ => {
2475 let refs_peeled = expr_type.peel_refs();
2478 return Some(refs_peeled.walk().last().unwrap().to_string());
2479 },
2480 }
2481 }
2482 None
2483}
2484
2485pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<(&T, &T)>
2492where
2493 Hash: FnMut(&T) -> u64,
2494 Eq: FnMut(&T, &T) -> bool,
2495{
2496 match exprs {
2497 [a, b] if eq(a, b) => return vec![(a, b)],
2498 _ if exprs.len() <= 2 => return vec![],
2499 _ => {},
2500 }
2501
2502 let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
2503
2504 let mut map: UnhashMap<u64, Vec<&_>> =
2505 UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
2506
2507 for expr in exprs {
2508 match map.entry(hash(expr)) {
2509 Entry::Occupied(mut o) => {
2510 for o in o.get() {
2511 if eq(o, expr) {
2512 match_expr_list.push((o, expr));
2513 }
2514 }
2515 o.get_mut().push(expr);
2516 },
2517 Entry::Vacant(v) => {
2518 v.insert(vec![expr]);
2519 },
2520 }
2521 }
2522
2523 match_expr_list
2524}
2525
2526pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2529 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2530 if let PatKind::Ref(pat, _) = pat.kind {
2531 peel(pat, count + 1)
2532 } else {
2533 (pat, count)
2534 }
2535 }
2536 peel(pat, 0)
2537}
2538
2539pub fn peel_hir_expr_while<'tcx>(
2541 mut expr: &'tcx Expr<'tcx>,
2542 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2543) -> &'tcx Expr<'tcx> {
2544 while let Some(e) = f(expr) {
2545 expr = e;
2546 }
2547 expr
2548}
2549
2550pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2553 let mut remaining = count;
2554 let e = peel_hir_expr_while(expr, |e| match e.kind {
2555 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2556 remaining -= 1;
2557 Some(e)
2558 },
2559 _ => None,
2560 });
2561 (e, count - remaining)
2562}
2563
2564pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2567 let mut count: usize = 0;
2568 let mut curr_expr = expr;
2569 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2570 count = count.wrapping_add(1);
2571 curr_expr = local_expr;
2572 }
2573 (curr_expr, count)
2574}
2575
2576pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2579 let mut count = 0;
2580 let e = peel_hir_expr_while(expr, |e| match e.kind {
2581 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2582 count += 1;
2583 Some(e)
2584 },
2585 _ => None,
2586 });
2587 (e, count)
2588}
2589
2590pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2593 let mut count = 0;
2594 loop {
2595 match &ty.kind {
2596 TyKind::Ref(_, ref_ty) => {
2597 ty = ref_ty.ty;
2598 count += 1;
2599 },
2600 _ => break (ty, count),
2601 }
2602 }
2603}
2604
2605pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2608 let mut count = 0;
2609 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2610 ty = *dest_ty;
2611 count += 1;
2612 }
2613 (ty, count)
2614}
2615
2616pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2619 loop {
2620 match expr.kind {
2621 ExprKind::AddrOf(_, _, e) => expr = e,
2622 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2623 _ => break,
2624 }
2625 }
2626 expr
2627}
2628
2629pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2630 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2631 if let Res::Def(_, def_id) = path.res {
2632 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2633 }
2634 }
2635 false
2636}
2637
2638static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2639
2640fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
2641 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2642 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2643 let value = map.entry(module);
2644 match value {
2645 Entry::Occupied(entry) => f(entry.get()),
2646 Entry::Vacant(entry) => {
2647 let mut names = Vec::new();
2648 for id in tcx.hir_module_free_items(module) {
2649 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2650 && let item = tcx.hir_item(id)
2651 && let ItemKind::Const(ident, ty, _generics, _body) = item.kind
2652 {
2653 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2654 if let Res::Def(DefKind::Struct, _) = path.res {
2656 let has_test_marker = tcx
2657 .hir_attrs(item.hir_id())
2658 .iter()
2659 .any(|a| a.has_name(sym::rustc_test_marker));
2660 if has_test_marker {
2661 names.push(ident.name);
2662 }
2663 }
2664 }
2665 }
2666 }
2667 names.sort_unstable();
2668 f(entry.insert(names))
2669 },
2670 }
2671}
2672
2673pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2677 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2678 let node = tcx.hir_node(id);
2679 once((id, node))
2680 .chain(tcx.hir_parent_iter(id))
2681 .any(|(_id, node)| {
2684 if let Node::Item(item) = node {
2685 if let ItemKind::Fn { ident, .. } = item.kind {
2686 return names.binary_search(&ident.name).is_ok();
2689 }
2690 }
2691 false
2692 })
2693 })
2694}
2695
2696pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2701 tcx.hir_attrs(id).iter().any(|attr| {
2702 if attr.has_name(sym::cfg)
2703 && let Some(items) = attr.meta_item_list()
2704 && let [item] = &*items
2705 && item.has_name(sym::test)
2706 {
2707 true
2708 } else {
2709 false
2710 }
2711 })
2712}
2713
2714pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2716 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2717}
2718
2719pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2721 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2722}
2723
2724pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2726 tcx.has_attr(def_id, sym::cfg)
2727 || tcx
2728 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2729 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2730 .any(|attr| attr.has_name(sym::cfg))
2731}
2732
2733pub fn walk_to_expr_usage<'tcx, T>(
2744 cx: &LateContext<'tcx>,
2745 e: &Expr<'tcx>,
2746 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2747) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2748 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2749 let mut child_id = e.hir_id;
2750
2751 while let Some((parent_id, parent)) = iter.next() {
2752 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2753 return Some(ControlFlow::Break(x));
2754 }
2755 let parent_expr = match parent {
2756 Node::Expr(e) => e,
2757 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2758 child_id = parent_id;
2759 continue;
2760 },
2761 Node::Arm(a) if a.body.hir_id == child_id => {
2762 child_id = parent_id;
2763 continue;
2764 },
2765 _ => return Some(ControlFlow::Continue((parent, child_id))),
2766 };
2767 match parent_expr.kind {
2768 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2769 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2770 child_id = id;
2771 iter = cx.tcx.hir_parent_iter(id);
2772 },
2773 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2774 _ => return Some(ControlFlow::Continue((parent, child_id))),
2775 }
2776 }
2777 debug_assert!(false, "no parent node found for `{child_id:?}`");
2778 None
2779}
2780
2781#[derive(Clone, Copy)]
2783pub enum DefinedTy<'tcx> {
2784 Hir(&'tcx hir::Ty<'tcx>),
2786 Mir {
2794 def_site_def_id: Option<DefId>,
2795 ty: Binder<'tcx, Ty<'tcx>>,
2796 },
2797}
2798
2799pub struct ExprUseCtxt<'tcx> {
2801 pub node: Node<'tcx>,
2803 pub child_id: HirId,
2805 pub adjustments: &'tcx [Adjustment<'tcx>],
2807 pub is_ty_unified: bool,
2809 pub moved_before_use: bool,
2811 pub same_ctxt: bool,
2813}
2814impl<'tcx> ExprUseCtxt<'tcx> {
2815 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2816 match self.node {
2817 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2818 Node::ExprField(field) => ExprUseNode::Field(field),
2819
2820 Node::Item(&Item {
2821 kind: ItemKind::Static(..) | ItemKind::Const(..),
2822 owner_id,
2823 ..
2824 })
2825 | Node::TraitItem(&TraitItem {
2826 kind: TraitItemKind::Const(..),
2827 owner_id,
2828 ..
2829 })
2830 | Node::ImplItem(&ImplItem {
2831 kind: ImplItemKind::Const(..),
2832 owner_id,
2833 ..
2834 }) => ExprUseNode::ConstStatic(owner_id),
2835
2836 Node::Item(&Item {
2837 kind: ItemKind::Fn { .. },
2838 owner_id,
2839 ..
2840 })
2841 | Node::TraitItem(&TraitItem {
2842 kind: TraitItemKind::Fn(..),
2843 owner_id,
2844 ..
2845 })
2846 | Node::ImplItem(&ImplItem {
2847 kind: ImplItemKind::Fn(..),
2848 owner_id,
2849 ..
2850 }) => ExprUseNode::Return(owner_id),
2851
2852 Node::Expr(use_expr) => match use_expr.kind {
2853 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2854 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2855 }),
2856
2857 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2858 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2859 Some(i) => ExprUseNode::FnArg(func, i),
2860 None => ExprUseNode::Callee,
2861 },
2862 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2863 use_expr.hir_id,
2864 name.args,
2865 args.iter()
2866 .position(|arg| arg.hir_id == self.child_id)
2867 .map_or(0, |i| i + 1),
2868 ),
2869 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2870 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2871 _ => ExprUseNode::Other,
2872 },
2873 _ => ExprUseNode::Other,
2874 }
2875 }
2876}
2877
2878pub enum ExprUseNode<'tcx> {
2880 LetStmt(&'tcx LetStmt<'tcx>),
2882 ConstStatic(OwnerId),
2884 Return(OwnerId),
2886 Field(&'tcx ExprField<'tcx>),
2888 FnArg(&'tcx Expr<'tcx>, usize),
2890 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2892 Callee,
2894 FieldAccess(Ident),
2896 AddrOf(ast::BorrowKind, Mutability),
2898 Other,
2899}
2900impl<'tcx> ExprUseNode<'tcx> {
2901 pub fn is_return(&self) -> bool {
2903 matches!(self, Self::Return(_))
2904 }
2905
2906 pub fn is_recv(&self) -> bool {
2908 matches!(self, Self::MethodArg(_, _, 0))
2909 }
2910
2911 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2913 match *self {
2914 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2915 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2916 def_site_def_id: Some(id.def_id.to_def_id()),
2917 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2918 }),
2919 Self::Return(id) => {
2920 if let Node::Expr(Expr {
2921 kind: ExprKind::Closure(c),
2922 ..
2923 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2924 {
2925 match c.fn_decl.output {
2926 FnRetTy::DefaultReturn(_) => None,
2927 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2928 }
2929 } else {
2930 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2931 Some(DefinedTy::Mir {
2932 def_site_def_id: Some(id.def_id.to_def_id()),
2933 ty,
2934 })
2935 }
2936 },
2937 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2938 Some(Expr {
2939 hir_id,
2940 kind: ExprKind::Struct(path, ..),
2941 ..
2942 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2943 .and_then(|(adt, variant)| {
2944 variant
2945 .fields
2946 .iter()
2947 .find(|f| f.name == field.ident.name)
2948 .map(|f| (adt, f))
2949 })
2950 .map(|(adt, field_def)| DefinedTy::Mir {
2951 def_site_def_id: Some(adt.did()),
2952 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2953 }),
2954 _ => None,
2955 },
2956 Self::FnArg(callee, i) => {
2957 let sig = expr_sig(cx, callee)?;
2958 let (hir_ty, ty) = sig.input_with_hir(i)?;
2959 Some(match hir_ty {
2960 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2961 None => DefinedTy::Mir {
2962 def_site_def_id: sig.predicates_id(),
2963 ty,
2964 },
2965 })
2966 },
2967 Self::MethodArg(id, _, i) => {
2968 let id = cx.typeck_results().type_dependent_def_id(id)?;
2969 let sig = cx.tcx.fn_sig(id).skip_binder();
2970 Some(DefinedTy::Mir {
2971 def_site_def_id: Some(id),
2972 ty: sig.input(i),
2973 })
2974 },
2975 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2976 }
2977 }
2978}
2979
2980pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2982 let mut adjustments = [].as_slice();
2983 let mut is_ty_unified = false;
2984 let mut moved_before_use = false;
2985 let mut same_ctxt = true;
2986 let ctxt = e.span.ctxt();
2987 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2988 if adjustments.is_empty()
2989 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2990 {
2991 adjustments = cx.typeck_results().expr_adjustments(e);
2992 }
2993 same_ctxt &= cx.tcx.hir().span(parent_id).ctxt() == ctxt;
2994 if let Node::Expr(e) = parent {
2995 match e.kind {
2996 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2997 is_ty_unified = true;
2998 moved_before_use = true;
2999 },
3000 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
3001 is_ty_unified = true;
3002 moved_before_use = true;
3003 },
3004 ExprKind::Block(..) => moved_before_use = true,
3005 _ => {},
3006 }
3007 }
3008 ControlFlow::Continue(())
3009 });
3010 match node {
3011 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
3012 node,
3013 child_id,
3014 adjustments,
3015 is_ty_unified,
3016 moved_before_use,
3017 same_ctxt,
3018 },
3019 #[allow(unreachable_patterns)]
3020 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
3021 None => ExprUseCtxt {
3022 node: Node::Crate(cx.tcx.hir_root_module()),
3023 child_id: HirId::INVALID,
3024 adjustments: &[],
3025 is_ty_unified: true,
3026 moved_before_use: true,
3027 same_ctxt: false,
3028 },
3029 }
3030}
3031
3032pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
3034 let mut pos = 0;
3035 tokenize(s).map(move |t| {
3036 let end = pos + t.len;
3037 let range = pos as usize..end as usize;
3038 let inner = InnerSpan::new(range.start, range.end);
3039 pos = end;
3040 (t.kind, s.get(range).unwrap_or_default(), inner)
3041 })
3042}
3043
3044pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
3047 let Ok(snippet) = sm.span_to_snippet(span) else {
3048 return false;
3049 };
3050 return tokenize(&snippet).any(|token| {
3051 matches!(
3052 token.kind,
3053 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
3054 )
3055 });
3056}
3057
3058pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
3062 span_extract_comments(sm, span).join("\n")
3063}
3064
3065pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
3069 let snippet = sm.span_to_snippet(span).unwrap_or_default();
3070 tokenize_with_text(&snippet)
3071 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
3072 .map(|(_, s, _)| s.to_string())
3073 .collect::<Vec<_>>()
3074}
3075
3076pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
3077 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
3078}
3079
3080pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
3106 cx: &LateContext<'_>,
3107 pat: &'a Pat<'hir>,
3108 else_body: &Expr<'_>,
3109) -> Option<&'a Pat<'hir>> {
3110 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
3111 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
3112 && !is_refutable(cx, inner_pat)
3113 && let else_body = peel_blocks(else_body)
3114 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
3115 && let ExprKind::Path(ret_path) = ret_val.kind
3116 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
3117 {
3118 Some(inner_pat)
3119 } else {
3120 None
3121 }
3122}
3123
3124macro_rules! op_utils {
3125 ($($name:ident $assign:ident)*) => {
3126 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
3128
3129 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
3131
3132 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
3134 match kind {
3135 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
3136 _ => None,
3137 }
3138 }
3139 };
3140}
3141
3142op_utils! {
3143 Add AddAssign
3144 Sub SubAssign
3145 Mul MulAssign
3146 Div DivAssign
3147 Rem RemAssign
3148 BitXor BitXorAssign
3149 BitAnd BitAndAssign
3150 BitOr BitOrAssign
3151 Shl ShlAssign
3152 Shr ShrAssign
3153}
3154
3155pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
3158 match *pat {
3159 PatKind::Wild => true,
3160 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
3161 !visitors::is_local_used(cx, body, id)
3162 },
3163 _ => false,
3164 }
3165}
3166
3167#[derive(Clone, Copy)]
3168pub enum RequiresSemi {
3169 Yes,
3170 No,
3171}
3172impl RequiresSemi {
3173 pub fn requires_semi(self) -> bool {
3174 matches!(self, Self::Yes)
3175 }
3176}
3177
3178#[expect(clippy::too_many_lines)]
3181pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
3182 struct BreakTarget {
3183 id: HirId,
3184 unused: bool,
3185 }
3186
3187 struct V<'cx, 'tcx> {
3188 cx: &'cx LateContext<'tcx>,
3189 break_targets: Vec<BreakTarget>,
3190 break_targets_for_result_ty: u32,
3191 in_final_expr: bool,
3192 requires_semi: bool,
3193 is_never: bool,
3194 }
3195
3196 impl V<'_, '_> {
3197 fn push_break_target(&mut self, id: HirId) {
3198 self.break_targets.push(BreakTarget { id, unused: true });
3199 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3200 }
3201 }
3202
3203 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3204 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3205 if self.is_never && self.break_targets.is_empty() {
3222 if self.in_final_expr && !self.requires_semi {
3223 match e.kind {
3226 ExprKind::DropTemps(e) => self.visit_expr(e),
3227 ExprKind::If(_, then, Some(else_)) => {
3228 self.visit_expr(then);
3229 self.visit_expr(else_);
3230 },
3231 ExprKind::Match(_, arms, _) => {
3232 for arm in arms {
3233 self.visit_expr(arm.body);
3234 }
3235 },
3236 ExprKind::Loop(b, ..) => {
3237 self.push_break_target(e.hir_id);
3238 self.in_final_expr = false;
3239 self.visit_block(b);
3240 self.break_targets.pop();
3241 },
3242 ExprKind::Block(b, _) => {
3243 if b.targeted_by_break {
3244 self.push_break_target(b.hir_id);
3245 self.visit_block(b);
3246 self.break_targets.pop();
3247 } else {
3248 self.visit_block(b);
3249 }
3250 },
3251 _ => {
3252 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3253 },
3254 }
3255 }
3256 return;
3257 }
3258 match e.kind {
3259 ExprKind::DropTemps(e) => self.visit_expr(e),
3260 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3261 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3262 self.in_final_expr = false;
3263 self.visit_expr(e);
3264 self.is_never = true;
3265 },
3266 ExprKind::Break(dest, e) => {
3267 if let Some(e) = e {
3268 self.in_final_expr = false;
3269 self.visit_expr(e);
3270 }
3271 if let Ok(id) = dest.target_id
3272 && let Some((i, target)) = self
3273 .break_targets
3274 .iter_mut()
3275 .enumerate()
3276 .find(|(_, target)| target.id == id)
3277 {
3278 target.unused &= self.is_never;
3279 if i < self.break_targets_for_result_ty as usize {
3280 self.requires_semi = true;
3281 }
3282 }
3283 self.is_never = true;
3284 },
3285 ExprKind::If(cond, then, else_) => {
3286 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3287 self.visit_expr(cond);
3288 self.in_final_expr = in_final_expr;
3289
3290 if self.is_never {
3291 self.visit_expr(then);
3292 if let Some(else_) = else_ {
3293 self.visit_expr(else_);
3294 }
3295 } else {
3296 self.visit_expr(then);
3297 let is_never = mem::replace(&mut self.is_never, false);
3298 if let Some(else_) = else_ {
3299 self.visit_expr(else_);
3300 self.is_never &= is_never;
3301 }
3302 }
3303 },
3304 ExprKind::Match(scrutinee, arms, _) => {
3305 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3306 self.visit_expr(scrutinee);
3307 self.in_final_expr = in_final_expr;
3308
3309 if self.is_never {
3310 for arm in arms {
3311 self.visit_arm(arm);
3312 }
3313 } else {
3314 let mut is_never = true;
3315 for arm in arms {
3316 self.is_never = false;
3317 if let Some(guard) = arm.guard {
3318 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3319 self.visit_expr(guard);
3320 self.in_final_expr = in_final_expr;
3321 self.is_never = false;
3323 }
3324 self.visit_expr(arm.body);
3325 is_never &= self.is_never;
3326 }
3327 self.is_never = is_never;
3328 }
3329 },
3330 ExprKind::Loop(b, _, _, _) => {
3331 self.push_break_target(e.hir_id);
3332 self.in_final_expr = false;
3333 self.visit_block(b);
3334 self.is_never = self.break_targets.pop().unwrap().unused;
3335 },
3336 ExprKind::Block(b, _) => {
3337 if b.targeted_by_break {
3338 self.push_break_target(b.hir_id);
3339 self.visit_block(b);
3340 self.is_never &= self.break_targets.pop().unwrap().unused;
3341 } else {
3342 self.visit_block(b);
3343 }
3344 },
3345 _ => {
3346 self.in_final_expr = false;
3347 walk_expr(self, e);
3348 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3349 },
3350 }
3351 }
3352
3353 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3354 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3355 for s in b.stmts {
3356 self.visit_stmt(s);
3357 }
3358 self.in_final_expr = in_final_expr;
3359 if let Some(e) = b.expr {
3360 self.visit_expr(e);
3361 }
3362 }
3363
3364 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3365 if let Some(e) = l.init {
3366 self.visit_expr(e);
3367 }
3368 if let Some(else_) = l.els {
3369 let is_never = self.is_never;
3370 self.visit_block(else_);
3371 self.is_never = is_never;
3372 }
3373 }
3374
3375 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3376 if let Some(guard) = arm.guard {
3377 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3378 self.visit_expr(guard);
3379 self.in_final_expr = in_final_expr;
3380 }
3381 self.visit_expr(arm.body);
3382 }
3383 }
3384
3385 if cx.typeck_results().expr_ty(e).is_never() {
3386 Some(RequiresSemi::No)
3387 } else if let ExprKind::Block(b, _) = e.kind
3388 && !b.targeted_by_break
3389 && b.expr.is_none()
3390 {
3391 None
3393 } else {
3394 let mut v = V {
3395 cx,
3396 break_targets: Vec::new(),
3397 break_targets_for_result_ty: 0,
3398 in_final_expr: true,
3399 requires_semi: false,
3400 is_never: false,
3401 };
3402 v.visit_expr(e);
3403 v.is_never
3404 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3405 RequiresSemi::Yes
3406 } else {
3407 RequiresSemi::No
3408 })
3409 }
3410}
3411
3412pub fn get_path_from_caller_to_method_type<'tcx>(
3418 tcx: TyCtxt<'tcx>,
3419 from: LocalDefId,
3420 method: DefId,
3421 args: GenericArgsRef<'tcx>,
3422) -> String {
3423 let assoc_item = tcx.associated_item(method);
3424 let def_id = assoc_item.container_id(tcx);
3425 match assoc_item.container {
3426 rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
3427 rustc_ty::AssocItemContainer::Impl => {
3428 let ty = tcx.type_of(def_id).instantiate_identity();
3429 get_path_to_ty(tcx, from, ty, args)
3430 },
3431 }
3432}
3433
3434fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3435 match ty.kind() {
3436 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3437 rustc_ty::Array(..)
3439 | rustc_ty::Dynamic(..)
3440 | rustc_ty::Never
3441 | rustc_ty::RawPtr(_, _)
3442 | rustc_ty::Ref(..)
3443 | rustc_ty::Slice(_)
3444 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3445 _ => ty.to_string(),
3446 }
3447}
3448
3449fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3451 if callee.is_local() {
3453 let callee_path = tcx.def_path(callee);
3454 let caller_path = tcx.def_path(from.to_def_id());
3455 maybe_get_relative_path(&caller_path, &callee_path, 2)
3456 } else {
3457 tcx.def_path_str(callee)
3458 }
3459}
3460
3461fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3474 use itertools::EitherOrBoth::{Both, Left, Right};
3475
3476 let unique_parts = to
3478 .data
3479 .iter()
3480 .zip_longest(from.data.iter())
3481 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3482 .map(|el| match el {
3483 Both(l, r) => Both(l.data, r.data),
3484 Left(l) => Left(l.data),
3485 Right(r) => Right(r.data),
3486 });
3487
3488 let mut go_up_by = 0;
3490 let mut path = Vec::new();
3491 for el in unique_parts {
3492 match el {
3493 Both(l, r) => {
3494 if let DefPathData::TypeNs(Some(s)) = l {
3504 path.push(s.to_string());
3505 }
3506 if let DefPathData::TypeNs(_) = r {
3507 go_up_by += 1;
3508 }
3509 },
3510 Left(DefPathData::TypeNs(Some(sym))) => path.push(sym.to_string()),
3515 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3520 _ => {},
3521 }
3522 }
3523
3524 if go_up_by > max_super {
3525 once(String::from("crate"))
3527 .chain(to.data.iter().filter_map(|el| {
3528 if let DefPathData::TypeNs(Some(sym)) = el.data {
3529 Some(sym.to_string())
3530 } else {
3531 None
3532 }
3533 }))
3534 .join("::")
3535 } else {
3536 repeat_n(String::from("super"), go_up_by).chain(path).join("::")
3537 }
3538}
3539
3540pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3543 matches!(
3544 cx.tcx.parent_hir_node(id),
3545 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3546 )
3547}
3548
3549pub fn is_block_like(expr: &Expr<'_>) -> bool {
3552 matches!(
3553 expr.kind,
3554 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3555 )
3556}
3557
3558pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3560 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3561 match expr.kind {
3562 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3563 _ if is_block_like(expr) => is_operand,
3564 _ => false,
3565 }
3566 }
3567
3568 contains_block(expr, false)
3569}
3570
3571pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3573 if let Some(parent_expr) = get_parent_expr(cx, expr)
3574 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3575 && receiver.hir_id == expr.hir_id
3576 {
3577 return true;
3578 }
3579 false
3580}
3581
3582pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3585 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3586 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3587 && temporary_ty
3588 .walk()
3589 .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static()))
3590 {
3591 ControlFlow::Break(())
3592 } else {
3593 ControlFlow::Continue(())
3594 }
3595 })
3596 .is_break()
3597}
3598
3599pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3610 let expr_ty_is_adjusted = cx
3611 .typeck_results()
3612 .expr_adjustments(expr)
3613 .iter()
3614 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3616 if expr_ty_is_adjusted {
3617 return true;
3618 }
3619
3620 match expr.kind {
3623 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3624 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3625
3626 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3627 return false;
3628 }
3629
3630 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3631 let mut args_with_ty_param = {
3632 fn_sig
3633 .inputs()
3634 .skip_binder()
3635 .iter()
3636 .skip(self_arg_count)
3637 .zip(args)
3638 .filter_map(|(arg_ty, arg)| {
3639 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3640 Some(arg)
3641 } else {
3642 None
3643 }
3644 })
3645 };
3646 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3647 },
3648 ExprKind::Struct(qpath, _, _) => {
3650 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3651 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3652 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3653 return true;
3655 };
3656 v_def
3657 .fields
3658 .iter()
3659 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3660 } else {
3661 false
3662 }
3663 },
3664 ExprKind::Block(
3666 &Block {
3667 expr: Some(ret_expr), ..
3668 },
3669 _,
3670 )
3671 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3672
3673 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3675 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3677 ExprKind::If(_, then, maybe_else) => {
3679 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3680 },
3681 ExprKind::Match(_, arms, _) => arms
3682 .iter()
3683 .map(|arm| arm.body)
3684 .any(|body| expr_requires_coercion(cx, body)),
3685 _ => false,
3686 }
3687}
3688
3689pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3692 if let Some(hir_id) = path_to_local(expr)
3693 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3694 {
3695 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3696 } else if let ExprKind::Path(p) = &expr.kind
3697 && let Some(mutability) = cx
3698 .qpath_res(p, expr.hir_id)
3699 .opt_def_id()
3700 .and_then(|id| cx.tcx.static_mutability(id))
3701 {
3702 mutability == Mutability::Mut
3703 } else if let ExprKind::Field(parent, _) = expr.kind {
3704 is_mutable(cx, parent)
3705 } else {
3706 true
3707 }
3708}
3709
3710pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3713 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3714 return hir_ty;
3715 };
3716 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3717 && let Some(segment) = path.segments.last()
3718 && segment.ident.name == sym::Option
3719 && let Res::Def(DefKind::Enum, def_id) = segment.res
3720 && def_id == option_def_id
3721 && let [GenericArg::Type(arg_ty)] = segment.args().args
3722 {
3723 hir_ty = arg_ty.as_unambig_ty();
3724 }
3725 hir_ty
3726}