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_ast_pretty;
35extern crate rustc_attr_parsing;
36extern crate rustc_const_eval;
37extern crate rustc_data_structures;
38#[allow(unused_extern_crates)]
40extern crate rustc_driver;
41extern crate rustc_errors;
42extern crate rustc_hir;
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_ast::ast::{self, LitKind, RangeLimits};
96use rustc_data_structures::fx::FxHashMap;
97use rustc_data_structures::packed::Pu128;
98use rustc_data_structures::unhash::UnhashMap;
99use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
100use rustc_hir::def::{DefKind, Res};
101use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
102use rustc_hir::definitions::{DefPath, DefPathData};
103use rustc_hir::hir_id::{HirIdMap, HirIdSet};
104use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
105use rustc_hir::{
106 self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext,
107 Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind,
108 ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat,
109 PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
110 TraitItemRef, TraitRef, TyKind, UnOp, def,
111};
112use rustc_lexer::{TokenKind, tokenize};
113use rustc_lint::{LateContext, Level, Lint, LintContext};
114use rustc_middle::hir::place::PlaceBase;
115use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
116use rustc_middle::ty::fast_reject::SimplifiedType;
117use rustc_middle::ty::layout::IntegerExt;
118use rustc_middle::ty::{
119 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty,
120 TyCtxt, TypeVisitableExt, UintTy, UpvarCapture,
121};
122use rustc_span::hygiene::{ExpnKind, MacroKind};
123use rustc_span::source_map::SourceMap;
124use rustc_span::symbol::{Ident, Symbol, kw};
125use rustc_span::{InnerSpan, Span, sym};
126use rustc_abi::Integer;
127use visitors::{Visitable, for_each_unconsumed_temporary};
128
129use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
130use crate::higher::Range;
131use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
132use crate::visitors::for_each_expr_without_closures;
133use rustc_middle::hir::nested_filter;
134
135#[macro_export]
136macro_rules! extract_msrv_attr {
137 (LateContext) => {
138 fn check_attributes(&mut self, cx: &rustc_lint::LateContext<'_>, attrs: &[rustc_hir::Attribute]) {
139 let sess = rustc_lint::LintContext::sess(cx);
140 self.msrv.check_attributes(sess, attrs);
141 }
142
143 fn check_attributes_post(&mut self, cx: &rustc_lint::LateContext<'_>, attrs: &[rustc_hir::Attribute]) {
144 let sess = rustc_lint::LintContext::sess(cx);
145 self.msrv.check_attributes_post(sess, attrs);
146 }
147 };
148 (EarlyContext) => {
149 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) {
150 let sess = rustc_lint::LintContext::sess(cx);
151 self.msrv.check_attributes(sess, attrs);
152 }
153
154 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) {
155 let sess = rustc_lint::LintContext::sess(cx);
156 self.msrv.check_attributes_post(sess, attrs);
157 }
158 };
159}
160
161pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
184 while let Some(init) = path_to_local(expr)
185 .and_then(|id| find_binding_init(cx, id))
186 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
187 {
188 expr = init;
189 }
190 expr
191}
192
193pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
202 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
203 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
204 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
205 {
206 return local.init;
207 }
208 None
209}
210
211pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
215 for (_, node) in cx.tcx.hir().parent_iter(local) {
216 match node {
217 Node::Pat(..) | Node::PatField(..) => {},
218 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
219 _ => return true,
220 }
221 }
222
223 false
224}
225
226pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
237 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
238 cx.enclosing_body.is_some_and(|id| {
239 cx.tcx
240 .hir()
241 .body_const_context(cx.tcx.hir().body_owner_def_id(id))
242 .is_some()
243 })
244}
245
246pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
253 use ConstContext::{Const, ConstFn, Static};
254 let hir = tcx.hir();
255 let Some(ctx) = hir.body_const_context(hir.enclosing_body_owner(hir_id)) else {
256 return false;
257 };
258 match ctx {
259 ConstFn => false,
260 Static(_) | Const { inline: _ } => true,
261 }
262}
263
264pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
267 if let Res::Def(DefKind::Ctor(..), id) = res
268 && let Some(lang_id) = cx.tcx.lang_items().get(lang_item)
269 && let Some(id) = cx.tcx.opt_parent(id)
270 {
271 id == lang_id
272 } else {
273 false
274 }
275}
276
277pub fn is_enum_variant_ctor(
279 cx: &LateContext<'_>,
280 enum_item: Symbol,
281 variant_name: Symbol,
282 ctor_call_id: DefId,
283) -> bool {
284 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
285 return false;
286 };
287
288 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
289 variants
290 .filter(|variant| variant.name == variant_name)
291 .filter_map(|variant| variant.ctor.as_ref())
292 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
293}
294
295pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
297 let did = match cx.tcx.def_kind(did) {
298 DefKind::Ctor(..) => cx.tcx.parent(did),
299 DefKind::Variant => match cx.tcx.opt_parent(did) {
301 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
302 _ => did,
303 },
304 _ => did,
305 };
306
307 cx.tcx.is_diagnostic_item(item, did)
308}
309
310pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
312 let did = match cx.tcx.def_kind(did) {
313 DefKind::Ctor(..) => cx.tcx.parent(did),
314 DefKind::Variant => match cx.tcx.opt_parent(did) {
316 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
317 _ => did,
318 },
319 _ => did,
320 };
321
322 cx.tcx.lang_items().get(item) == Some(did)
323}
324
325pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
326 matches!(
327 expr.kind,
328 ExprKind::Block(
329 Block {
330 stmts: [],
331 expr: None,
332 ..
333 },
334 _
335 ) | ExprKind::Tup([])
336 )
337}
338
339pub fn is_wild(pat: &Pat<'_>) -> bool {
341 matches!(pat.kind, PatKind::Wild)
342}
343
344pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
346 matches!(
347 arm.pat.kind,
348 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
349 if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone)
350 )
351}
352
353pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
355 match *qpath {
356 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
357 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
358 _ => false,
359 }
360}
361
362pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
365 let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
366 let trt_id = cx.tcx.trait_of_item(def_id);
367 trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path))
368}
369
370pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
372 if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
373 cx.tcx.trait_of_item(method_id).is_none()
374 } else {
375 false
376 }
377}
378
379pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
381 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
382 if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() {
383 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
384 }
385 }
386 false
387}
388
389pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
391 if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
392 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
393 }
394 false
395}
396
397pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
399 cx.typeck_results()
400 .type_dependent_def_id(expr.hir_id)
401 .is_some_and(|did| is_diag_trait_item(cx, did, diag_item))
402}
403
404pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
406 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
407 && let ItemKind::Impl(imp) = item.kind
408 {
409 imp.of_trait.is_some()
410 } else {
411 false
412 }
413}
414
415pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
425 if let ExprKind::Path(ref qpath) = expr.kind {
426 cx.qpath_res(qpath, expr.hir_id)
427 .opt_def_id()
428 .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item))
429 } else {
430 false
431 }
432}
433
434pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
435 match *path {
436 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
437 QPath::TypeRelative(_, seg) => seg,
438 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
439 }
440}
441
442pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
443 last_path_segment(qpath)
444 .args
445 .map_or(&[][..], |a| a.args)
446 .iter()
447 .filter_map(|a| match a {
448 hir::GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
449 _ => None,
450 })
451}
452
453pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
467 match *path {
468 QPath::Resolved(_, path) => match_path(path, segments),
469 QPath::TypeRelative(ty, segment) => match ty.kind {
470 TyKind::Path(ref inner_path) => {
471 if let [prefix @ .., end] = segments {
472 if match_qpath(inner_path, prefix) {
473 return segment.ident.name.as_str() == *end;
474 }
475 }
476 false
477 },
478 _ => false,
479 },
480 QPath::LangItem(..) => false,
481 }
482}
483
484pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
488 path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments))
489}
490
491pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
494 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id))
495}
496
497pub fn is_path_diagnostic_item<'tcx>(
500 cx: &LateContext<'_>,
501 maybe_path: &impl MaybePath<'tcx>,
502 diag_item: Symbol,
503) -> bool {
504 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
505}
506
507pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
528 path.segments
529 .iter()
530 .rev()
531 .zip(segments.iter().rev())
532 .all(|(a, b)| a.ident.name.as_str() == *b)
533}
534
535pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
537 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
538 if let Res::Local(id) = path.res {
539 return Some(id);
540 }
541 }
542 None
543}
544
545pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
548 path_to_local(expr) == Some(id)
549}
550
551pub trait MaybePath<'hir> {
552 fn hir_id(&self) -> HirId;
553 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
554}
555
556macro_rules! maybe_path {
557 ($ty:ident, $kind:ident) => {
558 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
559 fn hir_id(&self) -> HirId {
560 self.hir_id
561 }
562 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
563 match &self.kind {
564 hir::$kind::Path(qpath) => Some(qpath),
565 _ => None,
566 }
567 }
568 }
569 };
570}
571maybe_path!(Expr, ExprKind);
572impl<'hir> MaybePath<'hir> for Pat<'hir> {
573 fn hir_id(&self) -> HirId {
574 self.hir_id
575 }
576 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
577 match &self.kind {
578 PatKind::Expr(PatExpr {
579 kind: PatExprKind::Path(qpath),
580 ..
581 }) => Some(qpath),
582 _ => None,
583 }
584 }
585}
586maybe_path!(Ty, TyKind);
587
588pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
590 match maybe_path.qpath_opt() {
591 None => Res::Err,
592 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
593 }
594}
595
596pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
598 path_res(cx, maybe_path).opt_def_id()
599}
600
601fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
602 let ty = match name {
603 "bool" => SimplifiedType::Bool,
604 "char" => SimplifiedType::Char,
605 "str" => SimplifiedType::Str,
606 "array" => SimplifiedType::Array,
607 "slice" => SimplifiedType::Slice,
608 "const_ptr" => SimplifiedType::Ptr(Mutability::Not),
612 "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut),
613 "isize" => SimplifiedType::Int(IntTy::Isize),
614 "i8" => SimplifiedType::Int(IntTy::I8),
615 "i16" => SimplifiedType::Int(IntTy::I16),
616 "i32" => SimplifiedType::Int(IntTy::I32),
617 "i64" => SimplifiedType::Int(IntTy::I64),
618 "i128" => SimplifiedType::Int(IntTy::I128),
619 "usize" => SimplifiedType::Uint(UintTy::Usize),
620 "u8" => SimplifiedType::Uint(UintTy::U8),
621 "u16" => SimplifiedType::Uint(UintTy::U16),
622 "u32" => SimplifiedType::Uint(UintTy::U32),
623 "u64" => SimplifiedType::Uint(UintTy::U64),
624 "u128" => SimplifiedType::Uint(UintTy::U128),
625 "f32" => SimplifiedType::Float(FloatTy::F32),
626 "f64" => SimplifiedType::Float(FloatTy::F64),
627 _ => {
628 return [].iter().copied();
629 },
630 };
631
632 tcx.incoherent_impls(ty).iter().copied()
633}
634
635fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
636 match tcx.def_kind(def_id) {
637 DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
638 .module_children(def_id)
639 .iter()
640 .filter(|item| item.ident.name == name)
641 .map(|child| child.res.expect_non_local())
642 .collect(),
643 DefKind::Impl { .. } => tcx
644 .associated_item_def_ids(def_id)
645 .iter()
646 .copied()
647 .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
648 .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
649 .collect(),
650 _ => Vec::new(),
651 }
652}
653
654fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
655 let hir = tcx.hir();
656
657 let root_mod;
658 let item_kind = match tcx.hir_node_by_def_id(local_id) {
659 Node::Crate(r#mod) => {
660 root_mod = ItemKind::Mod(r#mod);
661 &root_mod
662 },
663 Node::Item(item) => &item.kind,
664 _ => return Vec::new(),
665 };
666
667 let res = |ident: Ident, owner_id: OwnerId| {
668 if ident.name == name {
669 let def_id = owner_id.to_def_id();
670 Some(Res::Def(tcx.def_kind(def_id), def_id))
671 } else {
672 None
673 }
674 };
675
676 match item_kind {
677 ItemKind::Mod(r#mod) => r#mod
678 .item_ids
679 .iter()
680 .filter_map(|&item_id| res(hir.item(item_id).ident, item_id.owner_id))
681 .collect(),
682 ItemKind::Impl(r#impl) => r#impl
683 .items
684 .iter()
685 .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
686 .collect(),
687 ItemKind::Trait(.., trait_item_refs) => trait_item_refs
688 .iter()
689 .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
690 .collect(),
691 _ => Vec::new(),
692 }
693}
694
695fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
696 if let Some(local_id) = def_id.as_local() {
697 local_item_children_by_name(tcx, local_id, name)
698 } else {
699 non_local_item_children_by_name(tcx, def_id, name)
700 }
701}
702
703pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec<Res> {
705 tcx.crates(())
706 .iter()
707 .copied()
708 .filter(move |&num| tcx.crate_name(num) == name)
709 .map(CrateNum::as_def_id)
710 .map(|id| Res::Def(tcx.def_kind(id), id))
711 .collect()
712}
713
714pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec<Res> {
724 let (base, path) = match path {
725 [primitive] => {
726 return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
727 },
728 [base, path @ ..] => (base, path),
729 _ => return Vec::new(),
730 };
731
732 let base_sym = Symbol::intern(base);
733
734 let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
735 Some(LOCAL_CRATE.as_def_id())
736 } else {
737 None
738 };
739
740 let crates = find_primitive_impls(tcx, base)
741 .chain(local_crate)
742 .map(|id| Res::Def(tcx.def_kind(id), id))
743 .chain(find_crates(tcx, base_sym))
744 .collect();
745
746 def_path_res_with_base(tcx, crates, path)
747}
748
749pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec<Res>, mut path: &[&str]) -> Vec<Res> {
754 while let [segment, rest @ ..] = path {
755 path = rest;
756 let segment = Symbol::intern(segment);
757
758 base = base
759 .into_iter()
760 .filter_map(|res| res.opt_def_id())
761 .flat_map(|def_id| {
762 let inherent_impl_children = tcx
765 .inherent_impls(def_id)
766 .iter()
767 .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
768
769 let direct_children = item_children_by_name(tcx, def_id, segment);
770
771 inherent_impl_children.chain(direct_children)
772 })
773 .collect();
774 }
775
776 base
777}
778
779pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> + use<> {
781 def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id())
782}
783
784pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option<DefId> {
789 def_path_res(tcx, path).into_iter().find_map(|res| match res {
790 Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
791 _ => None,
792 })
793}
794
795pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
811 let hir_id = cx.tcx.local_def_id_to_hir_id(def_id);
813 let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
814 if parent_impl != hir::CRATE_OWNER_ID
815 && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_impl.def_id)
816 && let ItemKind::Impl(impl_) = &item.kind
817 {
818 return impl_.of_trait.as_ref();
819 }
820 None
821}
822
823fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
831 let mut result = vec![];
832 let root = loop {
833 match e.kind {
834 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
835 result.push(e);
836 e = ep;
837 },
838 _ => break e,
839 }
840 };
841 result.reverse();
842 (result, root)
843}
844
845pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
847 cx.typeck_results()
848 .expr_adjustments(e)
849 .iter()
850 .find_map(|a| match a.kind {
851 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
852 Adjust::Deref(None) => None,
853 _ => Some(None),
854 })
855 .and_then(|x| x)
856}
857
858pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
861 let (s1, r1) = projection_stack(e1);
862 let (s2, r2) = projection_stack(e2);
863 if !eq_expr_value(cx, r1, r2) {
864 return true;
865 }
866 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
867 return false;
868 }
869
870 for (x1, x2) in s1.iter().zip(s2.iter()) {
871 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
872 return false;
873 }
874
875 match (&x1.kind, &x2.kind) {
876 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
877 if i1 != i2 {
878 return true;
879 }
880 },
881 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
882 if !eq_expr_value(cx, i1, i2) {
883 return false;
884 }
885 },
886 _ => return false,
887 }
888 }
889 false
890}
891
892fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
895 let std_types_symbols = &[
896 sym::Vec,
897 sym::VecDeque,
898 sym::LinkedList,
899 sym::HashMap,
900 sym::BTreeMap,
901 sym::HashSet,
902 sym::BTreeSet,
903 sym::BinaryHeap,
904 ];
905
906 if let QPath::TypeRelative(_, method) = path {
907 if method.ident.name == sym::new {
908 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
909 if let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() {
910 return std_types_symbols.iter().any(|&symbol| {
911 cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
912 });
913 }
914 }
915 }
916 }
917 false
918}
919
920pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
922 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
923 && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
924 && (is_diag_trait_item(cx, repl_def_id, sym::Default)
925 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath))
926 {
927 true
928 } else {
929 false
930 }
931}
932
933pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
938 match &e.kind {
939 ExprKind::Lit(lit) => match lit.node {
940 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
941 LitKind::Str(s, _) => s.is_empty(),
942 _ => false,
943 },
944 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
945 ExprKind::Repeat(x, len) => {
946 if let ConstArgKind::Anon(anon_const) = len.kind
947 && let ExprKind::Lit(const_lit) = cx.tcx.hir().body(anon_const.body).value.kind
948 && let LitKind::Int(v, _) = const_lit.node
949 && v <= 32
950 && is_default_equivalent(cx, x)
951 {
952 true
953 } else {
954 false
955 }
956 },
957 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func),
958 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
959 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
960 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
961 _ => false,
962 }
963}
964
965fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
966 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
967 && seg.ident.name == sym::from
968 {
969 match arg.kind {
970 ExprKind::Lit(hir::Lit {
971 node: LitKind::Str(sym, _),
972 ..
973 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
974 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
975 ExprKind::Repeat(_, len) => {
976 if let ConstArgKind::Anon(anon_const) = len.kind
977 && let ExprKind::Lit(const_lit) = cx.tcx.hir().body(anon_const.body).value.kind
978 && let LitKind::Int(v, _) = const_lit.node
979 {
980 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
981 }
982 },
983 _ => (),
984 }
985 }
986 false
987}
988
989pub fn can_move_expr_to_closure_no_visit<'tcx>(
1021 cx: &LateContext<'tcx>,
1022 expr: &'tcx Expr<'_>,
1023 loop_ids: &[HirId],
1024 ignore_locals: &HirIdSet,
1025) -> bool {
1026 match expr.kind {
1027 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
1028 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
1029 if loop_ids.contains(&id) =>
1030 {
1031 true
1032 },
1033 ExprKind::Break(..)
1034 | ExprKind::Continue(_)
1035 | ExprKind::Ret(_)
1036 | ExprKind::Yield(..)
1037 | ExprKind::InlineAsm(_) => false,
1038 ExprKind::Field(
1041 &Expr {
1042 hir_id,
1043 kind:
1044 ExprKind::Path(QPath::Resolved(
1045 _,
1046 Path {
1047 res: Res::Local(local_id),
1048 ..
1049 },
1050 )),
1051 ..
1052 },
1053 _,
1054 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
1055 false
1057 },
1058 _ => true,
1059 }
1060}
1061
1062#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1064pub enum CaptureKind {
1065 Value,
1066 Ref(Mutability),
1067}
1068impl CaptureKind {
1069 pub fn is_imm_ref(self) -> bool {
1070 self == Self::Ref(Mutability::Not)
1071 }
1072}
1073impl std::ops::BitOr for CaptureKind {
1074 type Output = Self;
1075 fn bitor(self, rhs: Self) -> Self::Output {
1076 match (self, rhs) {
1077 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
1078 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
1079 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
1080 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
1081 }
1082 }
1083}
1084impl std::ops::BitOrAssign for CaptureKind {
1085 fn bitor_assign(&mut self, rhs: Self) {
1086 *self = *self | rhs;
1087 }
1088}
1089
1090pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
1096 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
1097 let mut capture = CaptureKind::Ref(Mutability::Not);
1098 pat.each_binding_or_first(&mut |_, id, span, _| match cx
1099 .typeck_results()
1100 .extract_binding_mode(cx.sess(), id, span)
1101 .unwrap()
1102 .0
1103 {
1104 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
1105 capture = CaptureKind::Value;
1106 },
1107 ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
1108 capture = CaptureKind::Ref(Mutability::Mut);
1109 },
1110 _ => (),
1111 });
1112 capture
1113 }
1114
1115 debug_assert!(matches!(
1116 e.kind,
1117 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
1118 ));
1119
1120 let mut child_id = e.hir_id;
1121 let mut capture = CaptureKind::Value;
1122 let mut capture_expr_ty = e;
1123
1124 for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
1125 if let [
1126 Adjustment {
1127 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
1128 target,
1129 },
1130 ref adjust @ ..,
1131 ] = *cx
1132 .typeck_results()
1133 .adjustments()
1134 .get(child_id)
1135 .map_or(&[][..], |x| &**x)
1136 {
1137 if let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
1138 *adjust.last().map_or(target, |a| a.target).kind()
1139 {
1140 return CaptureKind::Ref(mutability);
1141 }
1142 }
1143
1144 match parent {
1145 Node::Expr(e) => match e.kind {
1146 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
1147 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
1148 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
1149 return CaptureKind::Ref(Mutability::Mut);
1150 },
1151 ExprKind::Field(..) => {
1152 if capture == CaptureKind::Value {
1153 capture_expr_ty = e;
1154 }
1155 },
1156 ExprKind::Let(let_expr) => {
1157 let mutability = match pat_capture_kind(cx, let_expr.pat) {
1158 CaptureKind::Value => Mutability::Not,
1159 CaptureKind::Ref(m) => m,
1160 };
1161 return CaptureKind::Ref(mutability);
1162 },
1163 ExprKind::Match(_, arms, _) => {
1164 let mut mutability = Mutability::Not;
1165 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
1166 match capture {
1167 CaptureKind::Value => break,
1168 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
1169 CaptureKind::Ref(Mutability::Not) => (),
1170 }
1171 }
1172 return CaptureKind::Ref(mutability);
1173 },
1174 _ => break,
1175 },
1176 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
1177 CaptureKind::Value => break,
1178 capture @ CaptureKind::Ref(_) => return capture,
1179 },
1180 _ => break,
1181 }
1182
1183 child_id = parent_id;
1184 }
1185
1186 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
1187 CaptureKind::Ref(Mutability::Not)
1189 } else {
1190 capture
1191 }
1192}
1193
1194pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
1197 struct V<'cx, 'tcx> {
1198 cx: &'cx LateContext<'tcx>,
1199 loops: Vec<HirId>,
1201 locals: HirIdSet,
1203 allow_closure: bool,
1205 captures: HirIdMap<CaptureKind>,
1208 }
1209 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1210 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1211 if !self.allow_closure {
1212 return;
1213 }
1214
1215 match e.kind {
1216 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1217 if !self.locals.contains(&l) {
1218 let cap = capture_local_usage(self.cx, e);
1219 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1220 }
1221 },
1222 ExprKind::Closure(closure) => {
1223 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
1224 let local_id = match capture.place.base {
1225 PlaceBase::Local(id) => id,
1226 PlaceBase::Upvar(var) => var.var_path.hir_id,
1227 _ => continue,
1228 };
1229 if !self.locals.contains(&local_id) {
1230 let capture = match capture.info.capture_kind {
1231 UpvarCapture::ByValue => CaptureKind::Value,
1232 UpvarCapture::ByRef(kind) => match kind {
1233 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
1234 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
1235 CaptureKind::Ref(Mutability::Mut)
1236 },
1237 },
1238 };
1239 self.captures
1240 .entry(local_id)
1241 .and_modify(|e| *e |= capture)
1242 .or_insert(capture);
1243 }
1244 }
1245 },
1246 ExprKind::Loop(b, ..) => {
1247 self.loops.push(e.hir_id);
1248 self.visit_block(b);
1249 self.loops.pop();
1250 },
1251 _ => {
1252 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1253 walk_expr(self, e);
1254 },
1255 }
1256 }
1257
1258 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1259 p.each_binding_or_first(&mut |_, id, _, _| {
1260 self.locals.insert(id);
1261 });
1262 }
1263 }
1264
1265 let mut v = V {
1266 cx,
1267 loops: Vec::new(),
1268 locals: HirIdSet::default(),
1269 allow_closure: true,
1270 captures: HirIdMap::default(),
1271 };
1272 v.visit_expr(expr);
1273 v.allow_closure.then_some(v.captures)
1274}
1275
1276pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1278
1279pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1282 let mut method_names = Vec::with_capacity(max_depth);
1283 let mut arg_lists = Vec::with_capacity(max_depth);
1284 let mut spans = Vec::with_capacity(max_depth);
1285
1286 let mut current = expr;
1287 for _ in 0..max_depth {
1288 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1289 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1290 break;
1291 }
1292 method_names.push(path.ident.name);
1293 arg_lists.push((*receiver, &**args));
1294 spans.push(path.ident.span);
1295 current = receiver;
1296 } else {
1297 break;
1298 }
1299 }
1300
1301 (method_names, arg_lists, spans)
1302}
1303
1304pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1311 let mut current = expr;
1312 let mut matched = Vec::with_capacity(methods.len());
1313 for method_name in methods.iter().rev() {
1314 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1316 if path.ident.name.as_str() == *method_name {
1317 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1318 return None;
1319 }
1320 matched.push((receiver, args)); current = receiver; } else {
1323 return None;
1324 }
1325 } else {
1326 return None;
1327 }
1328 }
1329 matched.reverse();
1331 Some(matched)
1332}
1333
1334pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1336 cx.tcx
1337 .entry_fn(())
1338 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1339}
1340
1341pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1343 let parent = cx.tcx.hir().get_parent_item(e.hir_id);
1344 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1345}
1346
1347pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1349 let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id).def_id;
1350 match cx.tcx.hir_node_by_def_id(parent_id) {
1351 Node::Item(Item { ident, .. })
1352 | Node::TraitItem(TraitItem { ident, .. })
1353 | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1354 _ => None,
1355 }
1356}
1357
1358pub struct ContainsName<'a, 'tcx> {
1359 pub cx: &'a LateContext<'tcx>,
1360 pub name: Symbol,
1361}
1362
1363impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1364 type Result = ControlFlow<()>;
1365 type NestedFilter = nested_filter::OnlyBodies;
1366
1367 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1368 if self.name == name {
1369 ControlFlow::Break(())
1370 } else {
1371 ControlFlow::Continue(())
1372 }
1373 }
1374
1375 fn nested_visit_map(&mut self) -> Self::Map {
1376 self.cx.tcx.hir()
1377 }
1378}
1379
1380pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1382 let mut cn = ContainsName { cx, name };
1383 cn.visit_expr(expr).is_break()
1384}
1385
1386pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1388 for_each_expr_without_closures(expr, |e| {
1389 if matches!(e.kind, ExprKind::Ret(..)) {
1390 ControlFlow::Break(())
1391 } else {
1392 ControlFlow::Continue(())
1393 }
1394 })
1395 .is_some()
1396}
1397
1398pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1400 get_parent_expr_for_hir(cx, e.hir_id)
1401}
1402
1403pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1406 match cx.tcx.parent_hir_node(hir_id) {
1407 Node::Expr(parent) => Some(parent),
1408 _ => None,
1409 }
1410}
1411
1412pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1414 let map = &cx.tcx.hir();
1415 let enclosing_node = map
1416 .get_enclosing_scope(hir_id)
1417 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1418 enclosing_node.and_then(|node| match node {
1419 Node::Block(block) => Some(block),
1420 Node::Item(&Item {
1421 kind: ItemKind::Fn { body: eid, .. },
1422 ..
1423 })
1424 | Node::ImplItem(&ImplItem {
1425 kind: ImplItemKind::Fn(_, eid),
1426 ..
1427 }) => match cx.tcx.hir().body(eid).value.kind {
1428 ExprKind::Block(block, _) => Some(block),
1429 _ => None,
1430 },
1431 _ => None,
1432 })
1433}
1434
1435pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1437 cx: &LateContext<'tcx>,
1438 expr: &Expr<'_>,
1439) -> Option<&'tcx Expr<'tcx>> {
1440 for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
1441 match node {
1442 Node::Expr(e) => match e.kind {
1443 ExprKind::Closure { .. }
1444 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1445 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1446
1447 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1449 _ => (),
1450 },
1451 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1452 _ => break,
1453 }
1454 }
1455 None
1456}
1457
1458pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1460 match tcx.hir().parent_iter(id).next() {
1461 Some((
1462 _,
1463 Node::Item(Item {
1464 kind: ItemKind::Impl(imp),
1465 ..
1466 }),
1467 )) => Some(imp),
1468 _ => None,
1469 }
1470}
1471
1472pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1483 while let ExprKind::Block(
1484 Block {
1485 stmts: [],
1486 expr: Some(inner),
1487 rules: BlockCheckMode::DefaultBlock,
1488 ..
1489 },
1490 _,
1491 ) = expr.kind
1492 {
1493 expr = inner;
1494 }
1495 expr
1496}
1497
1498pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1509 while let ExprKind::Block(
1510 Block {
1511 stmts: [],
1512 expr: Some(inner),
1513 rules: BlockCheckMode::DefaultBlock,
1514 ..
1515 }
1516 | Block {
1517 stmts:
1518 [
1519 Stmt {
1520 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1521 ..
1522 },
1523 ],
1524 expr: None,
1525 rules: BlockCheckMode::DefaultBlock,
1526 ..
1527 },
1528 _,
1529 ) = expr.kind
1530 {
1531 expr = inner;
1532 }
1533 expr
1534}
1535
1536pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1538 let mut iter = tcx.hir().parent_iter(expr.hir_id);
1539 match iter.next() {
1540 Some((
1541 _,
1542 Node::Expr(Expr {
1543 kind: ExprKind::If(_, _, Some(else_expr)),
1544 ..
1545 }),
1546 )) => else_expr.hir_id == expr.hir_id,
1547 _ => false,
1548 }
1549}
1550
1551pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1554 let mut child_id = expr.hir_id;
1555 for (parent_id, node) in tcx.hir().parent_iter(child_id) {
1556 if let Node::LetStmt(LetStmt {
1557 init: Some(init),
1558 els: Some(els),
1559 ..
1560 }) = node
1561 && (init.hir_id == child_id || els.hir_id == child_id)
1562 {
1563 return true;
1564 }
1565
1566 child_id = parent_id;
1567 }
1568
1569 false
1570}
1571
1572pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1574 let mut child_id = expr.hir_id;
1575 for (parent_id, node) in tcx.hir().parent_iter(child_id) {
1576 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1577 && els.hir_id == child_id
1578 {
1579 return true;
1580 }
1581
1582 child_id = parent_id;
1583 }
1584
1585 false
1586}
1587
1588pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1603 let ty = cx.typeck_results().expr_ty(expr);
1604 if let Some(Range { start, end, limits }) = Range::hir(expr) {
1605 let start_is_none_or_min = start.is_none_or(|start| {
1606 if let rustc_ty::Adt(_, subst) = ty.kind()
1607 && let bnd_ty = subst.type_at(0)
1608 && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx)
1609 && let Some(min_const) = mir_to_const(cx.tcx, min_const)
1610 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1611 {
1612 start_const == min_const
1613 } else {
1614 false
1615 }
1616 });
1617 let end_is_none_or_max = end.is_none_or(|end| match limits {
1618 RangeLimits::Closed => {
1619 if let rustc_ty::Adt(_, subst) = ty.kind()
1620 && let bnd_ty = subst.type_at(0)
1621 && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx)
1622 && let Some(max_const) = mir_to_const(cx.tcx, max_const)
1623 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1624 {
1625 end_const == max_const
1626 } else {
1627 false
1628 }
1629 },
1630 RangeLimits::HalfOpen => {
1631 if let Some(container_path) = container_path
1632 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1633 && name.ident.name == sym::len
1634 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1635 {
1636 container_path.res == path.res
1637 } else {
1638 false
1639 }
1640 },
1641 });
1642 return start_is_none_or_min && end_is_none_or_max;
1643 }
1644 false
1645}
1646
1647pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1650 if is_integer_literal(e, value) {
1651 return true;
1652 }
1653 let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
1654 if let Some(Constant::Int(v)) =
1655 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1656 {
1657 return value == v;
1658 }
1659 false
1660}
1661
1662pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1664 if let ExprKind::Lit(spanned) = expr.kind {
1666 if let LitKind::Int(v, _) = spanned.node {
1667 return v == value;
1668 }
1669 }
1670 false
1671}
1672
1673pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1681 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1682}
1683
1684#[must_use]
1688pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
1689 loop {
1690 if span.from_expansion() {
1691 let data = span.ctxt().outer_expn_data();
1692 let new_span = data.call_site;
1693
1694 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
1695 if mac_name.as_str() == name {
1696 return Some(new_span);
1697 }
1698 }
1699
1700 span = new_span;
1701 } else {
1702 return None;
1703 }
1704 }
1705}
1706
1707#[must_use]
1718pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
1719 if span.from_expansion() {
1720 let data = span.ctxt().outer_expn_data();
1721 let new_span = data.call_site;
1722
1723 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
1724 if mac_name.as_str() == name {
1725 return Some(new_span);
1726 }
1727 }
1728 }
1729
1730 None
1731}
1732
1733pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1735 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1736 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1737}
1738
1739pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1741 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1742 cx.tcx.instantiate_bound_regions_with_erased(arg)
1743}
1744
1745pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1747 if let ExprKind::Call(fun, _) = expr.kind {
1748 if let ExprKind::Path(ref qp) = fun.kind {
1749 let res = cx.qpath_res(qp, fun.hir_id);
1750 return match res {
1751 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1752 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1753 _ => false,
1754 };
1755 }
1756 }
1757 false
1758}
1759
1760pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1763 fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1764 matches!(
1765 cx.qpath_res(qpath, id),
1766 Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
1767 )
1768 }
1769
1770 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1771 i.into_iter().any(|pat| is_refutable(cx, pat))
1772 }
1773
1774 match pat.kind {
1775 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1777 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1778 PatKind::Expr(PatExpr {
1779 kind: PatExprKind::Path(qpath),
1780 hir_id,
1781 ..
1782 }) => is_enum_variant(cx, qpath, *hir_id),
1783 PatKind::Or(pats) => {
1784 are_refutable(cx, pats)
1786 },
1787 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1788 PatKind::Struct(ref qpath, fields, _) => {
1789 is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1790 },
1791 PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
1792 PatKind::Slice(head, middle, tail) => {
1793 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1794 rustc_ty::Slice(..) => {
1795 !head.is_empty() || middle.is_none() || !tail.is_empty()
1797 },
1798 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1799 _ => {
1800 true
1802 },
1803 }
1804 },
1805 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1806 }
1807}
1808
1809pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1812 if let PatKind::Or(pats) = pat.kind {
1813 pats.iter().for_each(f);
1814 } else {
1815 f(pat);
1816 }
1817}
1818
1819pub fn is_self(slf: &Param<'_>) -> bool {
1820 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1821 name.name == kw::SelfLower
1822 } else {
1823 false
1824 }
1825}
1826
1827pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1828 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
1829 if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res {
1830 return true;
1831 }
1832 }
1833 false
1834}
1835
1836pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1837 (0..decl.inputs.len()).map(move |i| &body.params[i])
1838}
1839
1840pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1843 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1844 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1845 && ddpos.as_opt_usize().is_none()
1846 && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk)
1847 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1848 && path_to_local_id(arm.body, hir_id)
1849 {
1850 return true;
1851 }
1852 false
1853 }
1854
1855 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1856 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1857 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1858 } else {
1859 false
1860 }
1861 }
1862
1863 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1864 if let MatchSource::TryDesugar(_) = *source {
1866 return Some(expr);
1867 }
1868
1869 if arms.len() == 2
1870 && arms[0].guard.is_none()
1871 && arms[1].guard.is_none()
1872 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1873 {
1874 return Some(expr);
1875 }
1876 }
1877
1878 None
1879}
1880
1881pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1891 let mut suppress_lint = false;
1892
1893 for id in ids {
1894 let (level, _) = cx.tcx.lint_level_at_node(lint, id);
1895 if let Some(expectation) = level.get_expectation_id() {
1896 cx.fulfill_expectation(expectation);
1897 }
1898
1899 match level {
1900 Level::Allow | Level::Expect(_) => suppress_lint = true,
1901 Level::Warn | Level::ForceWarn(_) | Level::Deny | Level::Forbid => {},
1902 }
1903 }
1904
1905 suppress_lint
1906}
1907
1908pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1916 cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
1917}
1918
1919pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1920 while let PatKind::Ref(subpat, _) = pat.kind {
1921 pat = subpat;
1922 }
1923 pat
1924}
1925
1926pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1927 Integer::from_int_ty(&tcx, ity).size().bits()
1928}
1929
1930#[expect(clippy::cast_possible_wrap)]
1931pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1933 let amt = 128 - int_bits(tcx, ity);
1934 ((u as i128) << amt) >> amt
1935}
1936
1937#[expect(clippy::cast_sign_loss)]
1938pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1940 let amt = 128 - int_bits(tcx, ity);
1941 ((u as u128) << amt) >> amt
1942}
1943
1944pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1946 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1947 let amt = 128 - bits;
1948 (u << amt) >> amt
1949}
1950
1951pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1952 attrs.iter().any(|attr| attr.has_name(symbol))
1953}
1954
1955pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1956 has_attr(cx.tcx.hir().attrs(hir_id), sym::repr)
1957}
1958
1959pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1960 let map = &tcx.hir();
1961 let mut prev_enclosing_node = None;
1962 let mut enclosing_node = node;
1963 while Some(enclosing_node) != prev_enclosing_node {
1964 if has_attr(map.attrs(enclosing_node), symbol) {
1965 return true;
1966 }
1967 prev_enclosing_node = Some(enclosing_node);
1968 enclosing_node = map.get_parent_item(enclosing_node).into();
1969 }
1970
1971 false
1972}
1973
1974pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1977 tcx.hir()
1978 .parent_owner_iter(id)
1979 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1980 .any(|(id, _)| {
1981 has_attr(
1982 tcx.hir().attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1983 sym::automatically_derived,
1984 )
1985 })
1986}
1987
1988pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
1993 let search_path = cx.get_def_path(did);
1994 paths
1995 .iter()
1996 .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
1997}
1998
1999pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool {
2001 let path = cx.get_def_path(did);
2003 syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
2004}
2005
2006pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
2008 let path = cx.get_def_path(did);
2009 path.first().is_some_and(|s| s.as_str() == "libc") && path.last().is_some_and(|s| s.as_str() == name)
2012}
2013
2014pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
2019 let mut conds = Vec::new();
2020 let mut blocks: Vec<&Block<'_>> = Vec::new();
2021
2022 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
2023 conds.push(cond);
2024 if let ExprKind::Block(block, _) = then.kind {
2025 blocks.push(block);
2026 } else {
2027 panic!("ExprKind::If node is not an ExprKind::Block");
2028 }
2029
2030 if let Some(else_expr) = r#else {
2031 expr = else_expr;
2032 } else {
2033 break;
2034 }
2035 }
2036
2037 if !blocks.is_empty() {
2039 if let ExprKind::Block(block, _) = expr.kind {
2040 blocks.push(block);
2041 }
2042 }
2043
2044 (conds, blocks)
2045}
2046
2047pub fn is_async_fn(kind: FnKind<'_>) -> bool {
2049 match kind {
2050 FnKind::ItemFn(_, _, header) => header.asyncness.is_async(),
2051 FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
2052 FnKind::Closure => false,
2053 }
2054}
2055
2056pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
2058 if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind {
2059 if let ExprKind::Block(
2060 Block {
2061 stmts: [],
2062 expr:
2063 Some(Expr {
2064 kind: ExprKind::DropTemps(expr),
2065 ..
2066 }),
2067 ..
2068 },
2069 _,
2070 ) = tcx.hir().body(body).value.kind
2071 {
2072 return Some(expr);
2073 }
2074 }
2075 None
2076}
2077
2078pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2080 let did = match expr.kind {
2081 ExprKind::Call(path, _) => {
2082 if let ExprKind::Path(ref qpath) = path.kind
2083 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
2084 {
2085 Some(did)
2086 } else {
2087 None
2088 }
2089 },
2090 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
2091 _ => None,
2092 };
2093
2094 did.is_some_and(|did| cx.tcx.has_attr(did, sym::must_use))
2095}
2096
2097fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
2106 fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
2107 if cx
2108 .typeck_results()
2109 .pat_binding_modes()
2110 .get(pat.hir_id)
2111 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
2112 {
2113 return false;
2117 }
2118
2119 match (pat.kind, expr.kind) {
2120 (PatKind::Binding(_, id, _, _), _) => {
2121 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
2122 },
2123 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
2124 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
2125 {
2126 pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr))
2127 },
2128 _ => false,
2129 }
2130 }
2131
2132 let [param] = func.params else {
2133 return false;
2134 };
2135
2136 let mut expr = func.value;
2137 loop {
2138 match expr.kind {
2139 ExprKind::Block(
2140 &Block {
2141 stmts: [],
2142 expr: Some(e),
2143 ..
2144 },
2145 _,
2146 )
2147 | ExprKind::Ret(Some(e)) => expr = e,
2148 ExprKind::Block(
2149 &Block {
2150 stmts: [stmt],
2151 expr: None,
2152 ..
2153 },
2154 _,
2155 ) => {
2156 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
2157 && let ExprKind::Ret(Some(ret_val)) = e.kind
2158 {
2159 expr = ret_val;
2160 } else {
2161 return false;
2162 }
2163 },
2164 _ => return check_pat(cx, param.pat, expr),
2165 }
2166 }
2167}
2168
2169pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2174 match expr.kind {
2175 ExprKind::Closure(&Closure { body, fn_decl, .. })
2176 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
2177 {
2178 is_body_identity_function(cx, cx.tcx.hir().body(body))
2179 },
2180 ExprKind::Path(QPath::Resolved(_, path))
2181 if path.segments.iter().all(|seg| seg.infer_args)
2182 && let Some(did) = path.res.opt_def_id() =>
2183 {
2184 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
2185 },
2186 _ => false,
2187 }
2188}
2189
2190pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2199 match expr.kind {
2200 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
2201 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
2202 }
2203}
2204
2205pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2208 let mut child_id = expr.hir_id;
2209 let mut iter = tcx.hir().parent_iter(child_id);
2210 loop {
2211 match iter.next() {
2212 None => break None,
2213 Some((id, Node::Block(_))) => child_id = id,
2214 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2215 Some((_, Node::Expr(expr))) => match expr.kind {
2216 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2217 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2218 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2219 _ => break Some((Node::Expr(expr), child_id)),
2220 },
2221 Some((_, node)) => break Some((node, child_id)),
2222 }
2223 }
2224}
2225
2226pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2228 !matches!(
2229 get_expr_use_or_unification_node(tcx, expr),
2230 None | Some((
2231 Node::Stmt(Stmt {
2232 kind: StmtKind::Expr(_)
2233 | StmtKind::Semi(_)
2234 | StmtKind::Let(LetStmt {
2235 pat: Pat {
2236 kind: PatKind::Wild,
2237 ..
2238 },
2239 ..
2240 }),
2241 ..
2242 }),
2243 _
2244 ))
2245 )
2246}
2247
2248pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2250 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2251}
2252
2253pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2254 if !is_no_std_crate(cx) {
2255 Some("std")
2256 } else if !is_no_core_crate(cx) {
2257 Some("core")
2258 } else {
2259 None
2260 }
2261}
2262
2263pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2264 cx.tcx
2265 .hir()
2266 .attrs(hir::CRATE_HIR_ID)
2267 .iter()
2268 .any(|attr| attr.name_or_empty() == sym::no_std)
2269}
2270
2271pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2272 cx.tcx
2273 .hir()
2274 .attrs(hir::CRATE_HIR_ID)
2275 .iter()
2276 .any(|attr| attr.name_or_empty() == sym::no_core)
2277}
2278
2279pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2289 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2290 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2291 } else {
2292 false
2293 }
2294}
2295
2296pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2306 use rustc_trait_selection::traits;
2307 let predicates = cx
2308 .tcx
2309 .predicates_of(did)
2310 .predicates
2311 .iter()
2312 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2313 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2314}
2315
2316pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2318 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2319}
2320
2321pub fn fn_def_id_with_node_args<'tcx>(
2324 cx: &LateContext<'tcx>,
2325 expr: &Expr<'_>,
2326) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2327 let typeck = cx.typeck_results();
2328 match &expr.kind {
2329 ExprKind::MethodCall(..) => Some((
2330 typeck.type_dependent_def_id(expr.hir_id)?,
2331 typeck.node_args(expr.hir_id),
2332 )),
2333 ExprKind::Call(
2334 Expr {
2335 kind: ExprKind::Path(qpath),
2336 hir_id: path_hir_id,
2337 ..
2338 },
2339 ..,
2340 ) => {
2341 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2344 typeck.qpath_res(qpath, *path_hir_id)
2345 {
2346 Some((id, typeck.node_args(*path_hir_id)))
2347 } else {
2348 None
2349 }
2350 },
2351 _ => None,
2352 }
2353}
2354
2355pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2360 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2361 let expr_kind = expr_type.kind();
2362 let is_primitive = match expr_kind {
2363 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2364 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2365 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2366 is_recursively_primitive_type(*element_type)
2367 } else {
2368 unreachable!()
2369 }
2370 },
2371 _ => false,
2372 };
2373
2374 if is_primitive {
2375 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2378 rustc_ty::Slice(..) => return Some("slice".into()),
2379 rustc_ty::Array(..) => return Some("array".into()),
2380 rustc_ty::Tuple(..) => return Some("tuple".into()),
2381 _ => {
2382 let refs_peeled = expr_type.peel_refs();
2385 return Some(refs_peeled.walk().last().unwrap().to_string());
2386 },
2387 }
2388 }
2389 None
2390}
2391
2392pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<(&T, &T)>
2399where
2400 Hash: FnMut(&T) -> u64,
2401 Eq: FnMut(&T, &T) -> bool,
2402{
2403 match exprs {
2404 [a, b] if eq(a, b) => return vec![(a, b)],
2405 _ if exprs.len() <= 2 => return vec![],
2406 _ => {},
2407 }
2408
2409 let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
2410
2411 let mut map: UnhashMap<u64, Vec<&_>> =
2412 UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
2413
2414 for expr in exprs {
2415 match map.entry(hash(expr)) {
2416 Entry::Occupied(mut o) => {
2417 for o in o.get() {
2418 if eq(o, expr) {
2419 match_expr_list.push((o, expr));
2420 }
2421 }
2422 o.get_mut().push(expr);
2423 },
2424 Entry::Vacant(v) => {
2425 v.insert(vec![expr]);
2426 },
2427 }
2428 }
2429
2430 match_expr_list
2431}
2432
2433pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2436 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2437 if let PatKind::Ref(pat, _) = pat.kind {
2438 peel(pat, count + 1)
2439 } else {
2440 (pat, count)
2441 }
2442 }
2443 peel(pat, 0)
2444}
2445
2446pub fn peel_hir_expr_while<'tcx>(
2448 mut expr: &'tcx Expr<'tcx>,
2449 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2450) -> &'tcx Expr<'tcx> {
2451 while let Some(e) = f(expr) {
2452 expr = e;
2453 }
2454 expr
2455}
2456
2457pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2460 let mut remaining = count;
2461 let e = peel_hir_expr_while(expr, |e| match e.kind {
2462 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2463 remaining -= 1;
2464 Some(e)
2465 },
2466 _ => None,
2467 });
2468 (e, count - remaining)
2469}
2470
2471pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2474 let mut count: usize = 0;
2475 let mut curr_expr = expr;
2476 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2477 count = count.wrapping_add(1);
2478 curr_expr = local_expr;
2479 }
2480 (curr_expr, count)
2481}
2482
2483pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2486 let mut count = 0;
2487 let e = peel_hir_expr_while(expr, |e| match e.kind {
2488 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2489 count += 1;
2490 Some(e)
2491 },
2492 _ => None,
2493 });
2494 (e, count)
2495}
2496
2497pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2500 let mut count = 0;
2501 loop {
2502 match &ty.kind {
2503 TyKind::Ref(_, ref_ty) => {
2504 ty = ref_ty.ty;
2505 count += 1;
2506 },
2507 _ => break (ty, count),
2508 }
2509 }
2510}
2511
2512pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2515 let mut count = 0;
2516 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2517 ty = *dest_ty;
2518 count += 1;
2519 }
2520 (ty, count)
2521}
2522
2523pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2526 loop {
2527 match expr.kind {
2528 ExprKind::AddrOf(_, _, e) => expr = e,
2529 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2530 _ => break,
2531 }
2532 }
2533 expr
2534}
2535
2536pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2537 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2538 if let Res::Def(_, def_id) = path.res {
2539 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2540 }
2541 }
2542 false
2543}
2544
2545static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2546
2547fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
2548 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2549 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2550 let value = map.entry(module);
2551 match value {
2552 Entry::Occupied(entry) => f(entry.get()),
2553 Entry::Vacant(entry) => {
2554 let mut names = Vec::new();
2555 for id in tcx.hir().module_items(module) {
2556 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2557 && let item = tcx.hir().item(id)
2558 && let ItemKind::Const(ty, _generics, _body) = item.kind
2559 {
2560 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2561 if let Res::Def(DefKind::Struct, _) = path.res {
2563 let has_test_marker = tcx
2564 .hir()
2565 .attrs(item.hir_id())
2566 .iter()
2567 .any(|a| a.has_name(sym::rustc_test_marker));
2568 if has_test_marker {
2569 names.push(item.ident.name);
2570 }
2571 }
2572 }
2573 }
2574 }
2575 names.sort_unstable();
2576 f(entry.insert(names))
2577 },
2578 }
2579}
2580
2581pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2585 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2586 let node = tcx.hir_node(id);
2587 once((id, node))
2588 .chain(tcx.hir().parent_iter(id))
2589 .any(|(_id, node)| {
2592 if let Node::Item(item) = node {
2593 if let ItemKind::Fn { .. } = item.kind {
2594 return names.binary_search(&item.ident.name).is_ok();
2597 }
2598 }
2599 false
2600 })
2601 })
2602}
2603
2604pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2609 tcx.hir().attrs(id).iter().any(|attr| {
2610 if attr.has_name(sym::cfg)
2611 && let Some(items) = attr.meta_item_list()
2612 && let [item] = &*items
2613 && item.has_name(sym::test)
2614 {
2615 true
2616 } else {
2617 false
2618 }
2619 })
2620}
2621
2622pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2624 tcx.hir()
2625 .parent_id_iter(id)
2626 .any(|parent_id| is_cfg_test(tcx, parent_id))
2627}
2628
2629pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2631 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2632}
2633
2634pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2636 let hir = tcx.hir();
2637
2638 tcx.has_attr(def_id, sym::cfg)
2639 || hir
2640 .parent_iter(tcx.local_def_id_to_hir_id(def_id))
2641 .flat_map(|(parent_id, _)| hir.attrs(parent_id))
2642 .any(|attr| attr.has_name(sym::cfg))
2643}
2644
2645pub fn walk_to_expr_usage<'tcx, T>(
2656 cx: &LateContext<'tcx>,
2657 e: &Expr<'tcx>,
2658 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2659) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2660 let map = cx.tcx.hir();
2661 let mut iter = map.parent_iter(e.hir_id);
2662 let mut child_id = e.hir_id;
2663
2664 while let Some((parent_id, parent)) = iter.next() {
2665 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2666 return Some(ControlFlow::Break(x));
2667 }
2668 let parent_expr = match parent {
2669 Node::Expr(e) => e,
2670 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2671 child_id = parent_id;
2672 continue;
2673 },
2674 Node::Arm(a) if a.body.hir_id == child_id => {
2675 child_id = parent_id;
2676 continue;
2677 },
2678 _ => return Some(ControlFlow::Continue((parent, child_id))),
2679 };
2680 match parent_expr.kind {
2681 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2682 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2683 child_id = id;
2684 iter = map.parent_iter(id);
2685 },
2686 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2687 _ => return Some(ControlFlow::Continue((parent, child_id))),
2688 }
2689 }
2690 debug_assert!(false, "no parent node found for `{child_id:?}`");
2691 None
2692}
2693
2694#[derive(Clone, Copy)]
2696pub enum DefinedTy<'tcx> {
2697 Hir(&'tcx hir::Ty<'tcx>),
2699 Mir {
2707 def_site_def_id: Option<DefId>,
2708 ty: Binder<'tcx, Ty<'tcx>>,
2709 },
2710}
2711
2712pub struct ExprUseCtxt<'tcx> {
2714 pub node: Node<'tcx>,
2716 pub child_id: HirId,
2718 pub adjustments: &'tcx [Adjustment<'tcx>],
2720 pub is_ty_unified: bool,
2722 pub moved_before_use: bool,
2724 pub same_ctxt: bool,
2726}
2727impl<'tcx> ExprUseCtxt<'tcx> {
2728 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2729 match self.node {
2730 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2731 Node::ExprField(field) => ExprUseNode::Field(field),
2732
2733 Node::Item(&Item {
2734 kind: ItemKind::Static(..) | ItemKind::Const(..),
2735 owner_id,
2736 ..
2737 })
2738 | Node::TraitItem(&TraitItem {
2739 kind: TraitItemKind::Const(..),
2740 owner_id,
2741 ..
2742 })
2743 | Node::ImplItem(&ImplItem {
2744 kind: ImplItemKind::Const(..),
2745 owner_id,
2746 ..
2747 }) => ExprUseNode::ConstStatic(owner_id),
2748
2749 Node::Item(&Item {
2750 kind: ItemKind::Fn { .. },
2751 owner_id,
2752 ..
2753 })
2754 | Node::TraitItem(&TraitItem {
2755 kind: TraitItemKind::Fn(..),
2756 owner_id,
2757 ..
2758 })
2759 | Node::ImplItem(&ImplItem {
2760 kind: ImplItemKind::Fn(..),
2761 owner_id,
2762 ..
2763 }) => ExprUseNode::Return(owner_id),
2764
2765 Node::Expr(use_expr) => match use_expr.kind {
2766 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2767 def_id: cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
2768 }),
2769
2770 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2771 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2772 Some(i) => ExprUseNode::FnArg(func, i),
2773 None => ExprUseNode::Callee,
2774 },
2775 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2776 use_expr.hir_id,
2777 name.args,
2778 args.iter()
2779 .position(|arg| arg.hir_id == self.child_id)
2780 .map_or(0, |i| i + 1),
2781 ),
2782 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2783 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2784 _ => ExprUseNode::Other,
2785 },
2786 _ => ExprUseNode::Other,
2787 }
2788 }
2789}
2790
2791pub enum ExprUseNode<'tcx> {
2793 LetStmt(&'tcx LetStmt<'tcx>),
2795 ConstStatic(OwnerId),
2797 Return(OwnerId),
2799 Field(&'tcx ExprField<'tcx>),
2801 FnArg(&'tcx Expr<'tcx>, usize),
2803 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2805 Callee,
2807 FieldAccess(Ident),
2809 AddrOf(ast::BorrowKind, Mutability),
2811 Other,
2812}
2813impl<'tcx> ExprUseNode<'tcx> {
2814 pub fn is_return(&self) -> bool {
2816 matches!(self, Self::Return(_))
2817 }
2818
2819 pub fn is_recv(&self) -> bool {
2821 matches!(self, Self::MethodArg(_, _, 0))
2822 }
2823
2824 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2826 match *self {
2827 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2828 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2829 def_site_def_id: Some(id.def_id.to_def_id()),
2830 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2831 }),
2832 Self::Return(id) => {
2833 if let Node::Expr(Expr {
2834 kind: ExprKind::Closure(c),
2835 ..
2836 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2837 {
2838 match c.fn_decl.output {
2839 FnRetTy::DefaultReturn(_) => None,
2840 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2841 }
2842 } else {
2843 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2844 Some(DefinedTy::Mir {
2845 def_site_def_id: Some(id.def_id.to_def_id()),
2846 ty,
2847 })
2848 }
2849 },
2850 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2851 Some(Expr {
2852 hir_id,
2853 kind: ExprKind::Struct(path, ..),
2854 ..
2855 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2856 .and_then(|(adt, variant)| {
2857 variant
2858 .fields
2859 .iter()
2860 .find(|f| f.name == field.ident.name)
2861 .map(|f| (adt, f))
2862 })
2863 .map(|(adt, field_def)| DefinedTy::Mir {
2864 def_site_def_id: Some(adt.did()),
2865 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2866 }),
2867 _ => None,
2868 },
2869 Self::FnArg(callee, i) => {
2870 let sig = expr_sig(cx, callee)?;
2871 let (hir_ty, ty) = sig.input_with_hir(i)?;
2872 Some(match hir_ty {
2873 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2874 None => DefinedTy::Mir {
2875 def_site_def_id: sig.predicates_id(),
2876 ty,
2877 },
2878 })
2879 },
2880 Self::MethodArg(id, _, i) => {
2881 let id = cx.typeck_results().type_dependent_def_id(id)?;
2882 let sig = cx.tcx.fn_sig(id).skip_binder();
2883 Some(DefinedTy::Mir {
2884 def_site_def_id: Some(id),
2885 ty: sig.input(i),
2886 })
2887 },
2888 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2889 }
2890 }
2891}
2892
2893pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2895 let mut adjustments = [].as_slice();
2896 let mut is_ty_unified = false;
2897 let mut moved_before_use = false;
2898 let mut same_ctxt = true;
2899 let ctxt = e.span.ctxt();
2900 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2901 if adjustments.is_empty()
2902 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2903 {
2904 adjustments = cx.typeck_results().expr_adjustments(e);
2905 }
2906 same_ctxt &= cx.tcx.hir().span(parent_id).ctxt() == ctxt;
2907 if let Node::Expr(e) = parent {
2908 match e.kind {
2909 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2910 is_ty_unified = true;
2911 moved_before_use = true;
2912 },
2913 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2914 is_ty_unified = true;
2915 moved_before_use = true;
2916 },
2917 ExprKind::Block(..) => moved_before_use = true,
2918 _ => {},
2919 }
2920 }
2921 ControlFlow::Continue(())
2922 });
2923 match node {
2924 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2925 node,
2926 child_id,
2927 adjustments,
2928 is_ty_unified,
2929 moved_before_use,
2930 same_ctxt,
2931 },
2932 #[allow(unreachable_patterns)]
2933 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
2934 None => ExprUseCtxt {
2935 node: Node::Crate(cx.tcx.hir().root_module()),
2936 child_id: HirId::INVALID,
2937 adjustments: &[],
2938 is_ty_unified: true,
2939 moved_before_use: true,
2940 same_ctxt: false,
2941 },
2942 }
2943}
2944
2945pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2947 let mut pos = 0;
2948 tokenize(s).map(move |t| {
2949 let end = pos + t.len;
2950 let range = pos as usize..end as usize;
2951 let inner = InnerSpan::new(range.start, range.end);
2952 pos = end;
2953 (t.kind, s.get(range).unwrap_or_default(), inner)
2954 })
2955}
2956
2957pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2960 let Ok(snippet) = sm.span_to_snippet(span) else {
2961 return false;
2962 };
2963 return tokenize(&snippet).any(|token| {
2964 matches!(
2965 token.kind,
2966 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2967 )
2968 });
2969}
2970
2971pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2975 span_extract_comments(sm, span).join("\n")
2976}
2977
2978pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2982 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2983 tokenize_with_text(&snippet)
2984 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2985 .map(|(_, s, _)| s.to_string())
2986 .collect::<Vec<_>>()
2987}
2988
2989pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2990 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2991}
2992
2993pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
3019 cx: &LateContext<'_>,
3020 pat: &'a Pat<'hir>,
3021 else_body: &Expr<'_>,
3022) -> Option<&'a Pat<'hir>> {
3023 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
3024 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
3025 && !is_refutable(cx, inner_pat)
3026 && let else_body = peel_blocks(else_body)
3027 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
3028 && let ExprKind::Path(ret_path) = ret_val.kind
3029 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
3030 {
3031 Some(inner_pat)
3032 } else {
3033 None
3034 }
3035}
3036
3037macro_rules! op_utils {
3038 ($($name:ident $assign:ident)*) => {
3039 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
3041
3042 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
3044
3045 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
3047 match kind {
3048 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
3049 _ => None,
3050 }
3051 }
3052 };
3053}
3054
3055op_utils! {
3056 Add AddAssign
3057 Sub SubAssign
3058 Mul MulAssign
3059 Div DivAssign
3060 Rem RemAssign
3061 BitXor BitXorAssign
3062 BitAnd BitAndAssign
3063 BitOr BitOrAssign
3064 Shl ShlAssign
3065 Shr ShrAssign
3066}
3067
3068pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
3071 match *pat {
3072 PatKind::Wild => true,
3073 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
3074 !visitors::is_local_used(cx, body, id)
3075 },
3076 _ => false,
3077 }
3078}
3079
3080#[derive(Clone, Copy)]
3081pub enum RequiresSemi {
3082 Yes,
3083 No,
3084}
3085impl RequiresSemi {
3086 pub fn requires_semi(self) -> bool {
3087 matches!(self, Self::Yes)
3088 }
3089}
3090
3091#[expect(clippy::too_many_lines)]
3094pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
3095 struct BreakTarget {
3096 id: HirId,
3097 unused: bool,
3098 }
3099
3100 struct V<'cx, 'tcx> {
3101 cx: &'cx LateContext<'tcx>,
3102 break_targets: Vec<BreakTarget>,
3103 break_targets_for_result_ty: u32,
3104 in_final_expr: bool,
3105 requires_semi: bool,
3106 is_never: bool,
3107 }
3108
3109 impl V<'_, '_> {
3110 fn push_break_target(&mut self, id: HirId) {
3111 self.break_targets.push(BreakTarget { id, unused: true });
3112 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
3113 }
3114 }
3115
3116 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3117 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3118 if self.is_never && self.break_targets.is_empty() {
3135 if self.in_final_expr && !self.requires_semi {
3136 match e.kind {
3139 ExprKind::DropTemps(e) => self.visit_expr(e),
3140 ExprKind::If(_, then, Some(else_)) => {
3141 self.visit_expr(then);
3142 self.visit_expr(else_);
3143 },
3144 ExprKind::Match(_, arms, _) => {
3145 for arm in arms {
3146 self.visit_expr(arm.body);
3147 }
3148 },
3149 ExprKind::Loop(b, ..) => {
3150 self.push_break_target(e.hir_id);
3151 self.in_final_expr = false;
3152 self.visit_block(b);
3153 self.break_targets.pop();
3154 },
3155 ExprKind::Block(b, _) => {
3156 if b.targeted_by_break {
3157 self.push_break_target(b.hir_id);
3158 self.visit_block(b);
3159 self.break_targets.pop();
3160 } else {
3161 self.visit_block(b);
3162 }
3163 },
3164 _ => {
3165 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3166 },
3167 }
3168 }
3169 return;
3170 }
3171 match e.kind {
3172 ExprKind::DropTemps(e) => self.visit_expr(e),
3173 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3174 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3175 self.in_final_expr = false;
3176 self.visit_expr(e);
3177 self.is_never = true;
3178 },
3179 ExprKind::Break(dest, e) => {
3180 if let Some(e) = e {
3181 self.in_final_expr = false;
3182 self.visit_expr(e);
3183 }
3184 if let Ok(id) = dest.target_id
3185 && let Some((i, target)) = self
3186 .break_targets
3187 .iter_mut()
3188 .enumerate()
3189 .find(|(_, target)| target.id == id)
3190 {
3191 target.unused &= self.is_never;
3192 if i < self.break_targets_for_result_ty as usize {
3193 self.requires_semi = true;
3194 }
3195 }
3196 self.is_never = true;
3197 },
3198 ExprKind::If(cond, then, else_) => {
3199 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3200 self.visit_expr(cond);
3201 self.in_final_expr = in_final_expr;
3202
3203 if self.is_never {
3204 self.visit_expr(then);
3205 if let Some(else_) = else_ {
3206 self.visit_expr(else_);
3207 }
3208 } else {
3209 self.visit_expr(then);
3210 let is_never = mem::replace(&mut self.is_never, false);
3211 if let Some(else_) = else_ {
3212 self.visit_expr(else_);
3213 self.is_never &= is_never;
3214 }
3215 }
3216 },
3217 ExprKind::Match(scrutinee, arms, _) => {
3218 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3219 self.visit_expr(scrutinee);
3220 self.in_final_expr = in_final_expr;
3221
3222 if self.is_never {
3223 for arm in arms {
3224 self.visit_arm(arm);
3225 }
3226 } else {
3227 let mut is_never = true;
3228 for arm in arms {
3229 self.is_never = false;
3230 if let Some(guard) = arm.guard {
3231 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3232 self.visit_expr(guard);
3233 self.in_final_expr = in_final_expr;
3234 self.is_never = false;
3236 }
3237 self.visit_expr(arm.body);
3238 is_never &= self.is_never;
3239 }
3240 self.is_never = is_never;
3241 }
3242 },
3243 ExprKind::Loop(b, _, _, _) => {
3244 self.push_break_target(e.hir_id);
3245 self.in_final_expr = false;
3246 self.visit_block(b);
3247 self.is_never = self.break_targets.pop().unwrap().unused;
3248 },
3249 ExprKind::Block(b, _) => {
3250 if b.targeted_by_break {
3251 self.push_break_target(b.hir_id);
3252 self.visit_block(b);
3253 self.is_never &= self.break_targets.pop().unwrap().unused;
3254 } else {
3255 self.visit_block(b);
3256 }
3257 },
3258 _ => {
3259 self.in_final_expr = false;
3260 walk_expr(self, e);
3261 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3262 },
3263 }
3264 }
3265
3266 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3267 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3268 for s in b.stmts {
3269 self.visit_stmt(s);
3270 }
3271 self.in_final_expr = in_final_expr;
3272 if let Some(e) = b.expr {
3273 self.visit_expr(e);
3274 }
3275 }
3276
3277 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3278 if let Some(e) = l.init {
3279 self.visit_expr(e);
3280 }
3281 if let Some(else_) = l.els {
3282 let is_never = self.is_never;
3283 self.visit_block(else_);
3284 self.is_never = is_never;
3285 }
3286 }
3287
3288 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3289 if let Some(guard) = arm.guard {
3290 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3291 self.visit_expr(guard);
3292 self.in_final_expr = in_final_expr;
3293 }
3294 self.visit_expr(arm.body);
3295 }
3296 }
3297
3298 if cx.typeck_results().expr_ty(e).is_never() {
3299 Some(RequiresSemi::No)
3300 } else if let ExprKind::Block(b, _) = e.kind
3301 && !b.targeted_by_break
3302 && b.expr.is_none()
3303 {
3304 None
3306 } else {
3307 let mut v = V {
3308 cx,
3309 break_targets: Vec::new(),
3310 break_targets_for_result_ty: 0,
3311 in_final_expr: true,
3312 requires_semi: false,
3313 is_never: false,
3314 };
3315 v.visit_expr(e);
3316 v.is_never
3317 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3318 RequiresSemi::Yes
3319 } else {
3320 RequiresSemi::No
3321 })
3322 }
3323}
3324
3325pub fn get_path_from_caller_to_method_type<'tcx>(
3331 tcx: TyCtxt<'tcx>,
3332 from: LocalDefId,
3333 method: DefId,
3334 args: GenericArgsRef<'tcx>,
3335) -> String {
3336 let assoc_item = tcx.associated_item(method);
3337 let def_id = assoc_item.container_id(tcx);
3338 match assoc_item.container {
3339 rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
3340 rustc_ty::AssocItemContainer::Impl => {
3341 let ty = tcx.type_of(def_id).instantiate_identity();
3342 get_path_to_ty(tcx, from, ty, args)
3343 },
3344 }
3345}
3346
3347fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3348 match ty.kind() {
3349 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3350 rustc_ty::Array(..)
3352 | rustc_ty::Dynamic(..)
3353 | rustc_ty::Never
3354 | rustc_ty::RawPtr(_, _)
3355 | rustc_ty::Ref(..)
3356 | rustc_ty::Slice(_)
3357 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3358 _ => ty.to_string(),
3359 }
3360}
3361
3362fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3364 if callee.is_local() {
3366 let callee_path = tcx.def_path(callee);
3367 let caller_path = tcx.def_path(from.to_def_id());
3368 maybe_get_relative_path(&caller_path, &callee_path, 2)
3369 } else {
3370 tcx.def_path_str(callee)
3371 }
3372}
3373
3374fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3387 use itertools::EitherOrBoth::{Both, Left, Right};
3388
3389 let unique_parts = to
3391 .data
3392 .iter()
3393 .zip_longest(from.data.iter())
3394 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3395 .map(|el| match el {
3396 Both(l, r) => Both(l.data, r.data),
3397 Left(l) => Left(l.data),
3398 Right(r) => Right(r.data),
3399 });
3400
3401 let mut go_up_by = 0;
3403 let mut path = Vec::new();
3404 for el in unique_parts {
3405 match el {
3406 Both(l, r) => {
3407 if let DefPathData::TypeNs(s) = l {
3417 path.push(s.to_string());
3418 }
3419 if let DefPathData::TypeNs(_) = r {
3420 go_up_by += 1;
3421 }
3422 },
3423 Left(DefPathData::TypeNs(sym)) => path.push(sym.to_string()),
3428 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3433 _ => {},
3434 }
3435 }
3436
3437 if go_up_by > max_super {
3438 once(String::from("crate"))
3440 .chain(to.data.iter().filter_map(|el| {
3441 if let DefPathData::TypeNs(sym) = el.data {
3442 Some(sym.to_string())
3443 } else {
3444 None
3445 }
3446 }))
3447 .join("::")
3448 } else {
3449 repeat_n(String::from("super"), go_up_by).chain(path).join("::")
3450 }
3451}
3452
3453pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3456 matches!(
3457 cx.tcx.parent_hir_node(id),
3458 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3459 )
3460}
3461
3462pub fn is_block_like(expr: &Expr<'_>) -> bool {
3465 matches!(
3466 expr.kind,
3467 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3468 )
3469}
3470
3471pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3473 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3474 match expr.kind {
3475 ExprKind::Binary(_, lhs, _) => contains_block(lhs, true),
3476 _ if is_block_like(expr) => is_operand,
3477 _ => false,
3478 }
3479 }
3480
3481 contains_block(expr, false)
3482}
3483
3484pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3486 if let Some(parent_expr) = get_parent_expr(cx, expr)
3487 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3488 && receiver.hir_id == expr.hir_id
3489 {
3490 return true;
3491 }
3492 false
3493}
3494
3495pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3498 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3499 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3500 && temporary_ty
3501 .walk()
3502 .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static()))
3503 {
3504 ControlFlow::Break(())
3505 } else {
3506 ControlFlow::Continue(())
3507 }
3508 })
3509 .is_break()
3510}