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