1#![feature(box_patterns)]
2#![feature(macro_metavar_expr)]
3#![feature(never_type)]
4#![feature(rustc_private)]
5#![feature(unwrap_infallible)]
6#![recursion_limit = "512"]
7#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
8#![warn(
9 trivial_casts,
10 trivial_numeric_casts,
11 rust_2018_idioms,
12 unused_lifetimes,
13 unused_qualifications,
14 rustc::internal
15)]
16
17extern crate rustc_abi;
20extern crate rustc_ast;
21extern crate rustc_attr_parsing;
22extern crate rustc_const_eval;
23extern crate rustc_data_structures;
24#[expect(
25 unused_extern_crates,
26 reason = "The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate."
27)]
28extern crate rustc_driver;
29extern crate rustc_errors;
30extern crate rustc_hir;
31extern crate rustc_hir_analysis;
32extern crate rustc_hir_typeck;
33extern crate rustc_index;
34extern crate rustc_infer;
35extern crate rustc_lexer;
36extern crate rustc_lint;
37extern crate rustc_middle;
38extern crate rustc_mir_dataflow;
39extern crate rustc_session;
40extern crate rustc_span;
41extern crate rustc_trait_selection;
42
43pub mod ast_utils;
44#[deny(missing_docs)]
45pub mod attrs;
46mod check_proc_macro;
47pub mod comparisons;
48pub mod consts;
49pub mod diagnostics;
50pub mod eager_or_lazy;
51pub mod higher;
52mod hir_utils;
53pub mod macros;
54pub mod mir;
55pub mod msrvs;
56pub mod numeric_literal;
57pub mod paths;
58pub mod qualify_min_const_fn;
59pub mod res;
60pub mod source;
61pub mod str_utils;
62pub mod sugg;
63pub mod sym;
64pub mod ty;
65pub mod usage;
66pub mod visitors;
67
68pub use self::attrs::*;
69pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
70pub use self::hir_utils::{
71 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
72 hash_stmt, is_bool, over,
73};
74
75use core::mem;
76use core::ops::ControlFlow;
77use std::collections::hash_map::Entry;
78use std::iter::{once, repeat_n, zip};
79use std::sync::{Mutex, MutexGuard, OnceLock};
80
81use itertools::Itertools;
82use rustc_abi::Integer;
83use rustc_ast::ast::{self, LitKind, RangeLimits};
84use rustc_ast::{LitIntType, join_path_syms};
85use rustc_data_structures::fx::FxHashMap;
86use rustc_data_structures::indexmap;
87use rustc_data_structures::packed::Pu128;
88use rustc_data_structures::unhash::UnindexMap;
89use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
90use rustc_hir::attrs::CfgEntry;
91use rustc_hir::def::{DefKind, Res};
92use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
93use rustc_hir::definitions::{DefPath, DefPathData};
94use rustc_hir::hir_id::{HirIdMap, HirIdSet};
95use rustc_hir::intravisit::{Visitor, walk_expr};
96use rustc_hir::{
97 self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
98 CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs,
99 HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId,
100 OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
101 TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr,
102};
103use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
104use rustc_lint::{LateContext, Level, Lint, LintContext};
105use rustc_middle::hir::nested_filter;
106use rustc_middle::hir::place::PlaceBase;
107use rustc_middle::lint::LevelAndSource;
108use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
109use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion};
110use rustc_middle::ty::layout::IntegerExt;
111use rustc_middle::ty::{
112 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
113 TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
114};
115use rustc_span::hygiene::{ExpnKind, MacroKind};
116use rustc_span::source_map::SourceMap;
117use rustc_span::symbol::{Ident, Symbol, kw};
118use rustc_span::{InnerSpan, Span};
119use source::{SpanRangeExt, walk_span_to_context};
120use visitors::{Visitable, for_each_unconsumed_temporary};
121
122use crate::ast_utils::unordered_over;
123use crate::consts::{ConstEvalCtxt, Constant};
124use crate::higher::Range;
125use crate::msrvs::Msrv;
126use crate::res::{MaybeDef, MaybeQPath, MaybeResPath};
127use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
128use crate::visitors::for_each_expr_without_closures;
129
130pub const VEC_METHODS_SHADOWING_SLICE_METHODS: [Symbol; 3] = [sym::as_ptr, sym::is_empty, sym::len];
132
133#[macro_export]
134macro_rules! extract_msrv_attr {
135 () => {
136 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
137 let sess = rustc_lint::LintContext::sess(cx);
138 self.msrv.check_attributes(sess, attrs);
139 }
140
141 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
142 let sess = rustc_lint::LintContext::sess(cx);
143 self.msrv.check_attributes_post(sess, attrs);
144 }
145 };
146}
147
148pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
171 while let Some(init) = expr
172 .res_local_id()
173 .and_then(|id| find_binding_init(cx, id))
174 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
175 {
176 expr = init;
177 }
178 expr
179}
180
181pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
190 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
191 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
192 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
193 {
194 return local.init;
195 }
196 None
197}
198
199pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
203 for (_, node) in cx.tcx.hir_parent_iter(local) {
204 match node {
205 Node::Pat(..) | Node::PatField(..) => {},
206 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
207 _ => return true,
208 }
209 }
210
211 false
212}
213
214pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
225 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
226 cx.enclosing_body.is_some_and(|id| {
227 cx.tcx
228 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
229 .is_some()
230 })
231}
232
233pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
240 use rustc_hir::ConstContext::{Const, ConstFn, Static};
241 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
242 return false;
243 };
244 match ctx {
245 ConstFn => false,
246 Static(_) | Const { inline: _ } => true,
247 }
248}
249
250pub fn is_enum_variant_ctor(
252 cx: &LateContext<'_>,
253 enum_item: Symbol,
254 variant_name: Symbol,
255 ctor_call_id: DefId,
256) -> bool {
257 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
258 return false;
259 };
260
261 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
262 variants
263 .filter(|variant| variant.name == variant_name)
264 .filter_map(|variant| variant.ctor.as_ref())
265 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
266}
267
268pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
270 let did = match cx.tcx.def_kind(did) {
271 DefKind::Ctor(..) => cx.tcx.parent(did),
272 DefKind::Variant => match cx.tcx.opt_parent(did) {
274 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
275 _ => did,
276 },
277 _ => did,
278 };
279
280 cx.tcx.is_diagnostic_item(item, did)
281}
282
283pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
285 let did = match cx.tcx.def_kind(did) {
286 DefKind::Ctor(..) => cx.tcx.parent(did),
287 DefKind::Variant => match cx.tcx.opt_parent(did) {
289 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
290 _ => did,
291 },
292 _ => did,
293 };
294
295 cx.tcx.lang_items().get(item) == Some(did)
296}
297
298pub fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
300 expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
301}
302
303pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
305 if let ExprKind::Call(e, [arg]) = expr.kind
306 && e.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
307 {
308 Some(arg)
309 } else {
310 None
311 }
312}
313
314pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
316 matches!(
317 expr.kind,
318 ExprKind::Block(
319 Block {
320 stmts: [],
321 expr: None,
322 ..
323 },
324 _
325 ) | ExprKind::Tup([])
326 )
327}
328
329pub fn is_wild(pat: &Pat<'_>) -> bool {
331 matches!(pat.kind, PatKind::Wild)
332}
333
334pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> {
341 if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind
342 && cx
343 .qpath_res(qpath, pat.hir_id)
344 .ctor_parent(cx)
345 .is_lang_item(cx, OptionSome)
346 {
347 Some(inner)
348 } else {
349 None
350 }
351}
352
353pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
355 matches!(pat.kind,
356 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
357 if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone))
358}
359
360pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
362 is_none_pattern(cx, arm.pat)
363 && matches!(
364 peel_blocks(arm.body).kind,
365 ExprKind::Path(qpath)
366 if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)
367 )
368}
369
370pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
372 match *qpath {
373 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
374 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
375 QPath::TypeRelative(..) => false,
376 }
377}
378
379pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
381 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
382 && let ItemKind::Impl(imp) = item.kind
383 {
384 imp.of_trait.is_some()
385 } else {
386 false
387 }
388}
389
390pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
391 match *path {
392 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
393 QPath::TypeRelative(_, seg) => seg,
394 }
395}
396
397pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
398 last_path_segment(qpath)
399 .args
400 .map_or(&[][..], |a| a.args)
401 .iter()
402 .filter_map(|a| match a {
403 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
404 _ => None,
405 })
406}
407
408pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
413 match expr.kind {
414 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
415 ExprKind::Path(QPath::Resolved(
416 _,
417 Path {
418 res: Res::Local(local), ..
419 },
420 )) => Some(*local),
421 _ => None,
422 }
423}
424
425pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
441 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
442 && let ItemKind::Impl(impl_) = &item.kind
443 && let Some(of_trait) = impl_.of_trait
444 {
445 return Some(&of_trait.trait_ref);
446 }
447 None
448}
449
450fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
458 let mut result = vec![];
459 let root = loop {
460 match e.kind {
461 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
462 result.push(e);
463 e = ep;
464 },
465 _ => break e,
466 }
467 };
468 result.reverse();
469 (result, root)
470}
471
472pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
474 cx.typeck_results()
475 .expr_adjustments(e)
476 .iter()
477 .find_map(|a| match a.kind {
478 Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)),
479 Adjust::Deref(DerefAdjustKind::Builtin) => None,
480 _ => Some(None),
481 })
482 .and_then(|x| x)
483}
484
485pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
488 let (s1, r1) = projection_stack(e1);
489 let (s2, r2) = projection_stack(e2);
490 if !eq_expr_value(cx, r1, r2) {
491 return true;
492 }
493 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
494 return false;
495 }
496
497 for (x1, x2) in zip(&s1, &s2) {
498 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
499 return false;
500 }
501
502 match (&x1.kind, &x2.kind) {
503 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
504 if i1 != i2 {
505 return true;
506 }
507 },
508 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
509 if !eq_expr_value(cx, i1, i2) {
510 return false;
511 }
512 },
513 _ => return false,
514 }
515 }
516 false
517}
518
519fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
522 let std_types_symbols = &[
523 sym::Vec,
524 sym::VecDeque,
525 sym::LinkedList,
526 sym::HashMap,
527 sym::BTreeMap,
528 sym::HashSet,
529 sym::BTreeSet,
530 sym::BinaryHeap,
531 ];
532
533 if let QPath::TypeRelative(_, method) = path
534 && method.ident.name == sym::new
535 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
536 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
537 {
538 return Some(adt.did()) == cx.tcx.lang_items().string()
539 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
540 }
541 false
542}
543
544pub fn is_default_equivalent_call(
546 cx: &LateContext<'_>,
547 repl_func: &Expr<'_>,
548 whole_call_expr: Option<&Expr<'_>>,
549) -> bool {
550 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
551 && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx)
552 && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default)
553 || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath))
554 {
555 return true;
556 }
557
558 let Some(e) = whole_call_expr else { return false };
561 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
562 return false;
563 };
564 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
565 return false;
566 };
567 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
568 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
569 cx.tcx.lifetimes.re_erased.into()
570 } else if param.index == 0 && param.name == kw::SelfUpper {
571 ty.into()
572 } else {
573 param.to_error(cx.tcx)
574 }
575 });
576 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
577
578 let Ok(Some(instance)) = instance else { return false };
579 if let rustc_ty::InstanceKind::Item(def) = instance.def
580 && !cx.tcx.is_mir_available(def)
581 {
582 return false;
583 }
584 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
585 return false;
586 };
587 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
588 return false;
589 };
590
591 let body = cx.tcx.instance_mir(instance.def);
597 for block_data in body.basic_blocks.iter() {
598 if block_data.statements.len() == 1
599 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
600 && assign.0.local == RETURN_PLACE
601 && let Rvalue::Aggregate(kind, _places) = &assign.1
602 && let AggregateKind::Adt(did, variant_index, _, _, _) = **kind
603 && let def = cx.tcx.adt_def(did)
604 && let variant = &def.variant(variant_index)
605 && variant.fields.is_empty()
606 && let Some((_, did)) = variant.ctor
607 && did == repl_def_id
608 {
609 return true;
610 } else if block_data.statements.is_empty()
611 && let Some(term) = &block_data.terminator
612 {
613 match &term.kind {
614 TerminatorKind::Call {
615 func: Operand::Constant(c),
616 ..
617 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
618 && *did == repl_def_id =>
619 {
620 return true;
621 },
622 TerminatorKind::TailCall {
623 func: Operand::Constant(c),
624 ..
625 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
626 && *did == repl_def_id =>
627 {
628 return true;
629 },
630 _ => {},
631 }
632 }
633 }
634 false
635}
636
637pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
641 match &e.kind {
642 ExprKind::Lit(lit) => match lit.node {
643 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
644 LitKind::Str(s, _) => s.is_empty(),
645 _ => false,
646 },
647 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
648 ExprKind::Repeat(x, len) => {
649 if let ConstArgKind::Anon(anon_const) = len.kind
650 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
651 && let LitKind::Int(v, _) = const_lit.node
652 && v <= 32
653 && is_default_equivalent(cx, x)
654 {
655 true
656 } else {
657 false
658 }
659 },
660 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
661 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
662 ExprKind::Path(qpath) => cx
663 .qpath_res(qpath, e.hir_id)
664 .ctor_parent(cx)
665 .is_lang_item(cx, OptionNone),
666 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
667 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
668 _ => false,
669 }
670}
671
672fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
673 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
674 && seg.ident.name == sym::from
675 {
676 match arg.kind {
677 ExprKind::Lit(hir::Lit {
678 node: LitKind::Str(sym, _),
679 ..
680 }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String),
681 ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec),
682 ExprKind::Repeat(_, len) => {
683 if let ConstArgKind::Anon(anon_const) = len.kind
684 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
685 && let LitKind::Int(v, _) = const_lit.node
686 {
687 return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec);
688 }
689 },
690 _ => (),
691 }
692 }
693 false
694}
695
696pub fn can_move_expr_to_closure_no_visit<'tcx>(
728 cx: &LateContext<'tcx>,
729 expr: &'tcx Expr<'_>,
730 loop_ids: &[HirId],
731 ignore_locals: &HirIdSet,
732) -> bool {
733 match expr.kind {
734 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
735 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
736 if loop_ids.contains(&id) =>
737 {
738 true
739 },
740 ExprKind::Break(..)
741 | ExprKind::Continue(_)
742 | ExprKind::Ret(_)
743 | ExprKind::Yield(..)
744 | ExprKind::InlineAsm(_) => false,
745 ExprKind::Field(
748 &Expr {
749 hir_id,
750 kind:
751 ExprKind::Path(QPath::Resolved(
752 _,
753 Path {
754 res: Res::Local(local_id),
755 ..
756 },
757 )),
758 ..
759 },
760 _,
761 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
762 false
764 },
765 _ => true,
766 }
767}
768
769#[derive(Debug, Clone, Copy, PartialEq, Eq)]
771pub enum CaptureKind {
772 Value,
773 Use,
774 Ref(Mutability),
775}
776impl CaptureKind {
777 pub fn is_imm_ref(self) -> bool {
778 self == Self::Ref(Mutability::Not)
779 }
780}
781impl std::ops::BitOr for CaptureKind {
782 type Output = Self;
783 fn bitor(self, rhs: Self) -> Self::Output {
784 match (self, rhs) {
785 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
786 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
787 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
788 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
789 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
790 }
791 }
792}
793impl std::ops::BitOrAssign for CaptureKind {
794 fn bitor_assign(&mut self, rhs: Self) {
795 *self = *self | rhs;
796 }
797}
798
799pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
805 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
806 let mut capture = CaptureKind::Ref(Mutability::Not);
807 pat.each_binding_or_first(&mut |_, id, span, _| match cx
808 .typeck_results()
809 .extract_binding_mode(cx.sess(), id, span)
810 .0
811 {
812 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
813 capture = CaptureKind::Value;
814 },
815 ByRef::Yes(_, Mutability::Mut) if capture != CaptureKind::Value => {
816 capture = CaptureKind::Ref(Mutability::Mut);
817 },
818 _ => (),
819 });
820 capture
821 }
822
823 debug_assert!(matches!(
824 e.kind,
825 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
826 ));
827
828 let mut child_id = e.hir_id;
829 let mut capture = CaptureKind::Value;
830 let mut capture_expr_ty = e;
831
832 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
833 if let [
834 Adjustment {
835 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
836 target,
837 },
838 ref adjust @ ..,
839 ] = *cx
840 .typeck_results()
841 .adjustments()
842 .get(child_id)
843 .map_or(&[][..], |x| &**x)
844 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
845 *adjust.last().map_or(target, |a| a.target).kind()
846 {
847 return CaptureKind::Ref(mutability);
848 }
849
850 match parent {
851 Node::Expr(e) => match e.kind {
852 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
853 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
854 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
855 return CaptureKind::Ref(Mutability::Mut);
856 },
857 ExprKind::Field(..) => {
858 if capture == CaptureKind::Value {
859 capture_expr_ty = e;
860 }
861 },
862 ExprKind::Let(let_expr) => {
863 let mutability = match pat_capture_kind(cx, let_expr.pat) {
864 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
865 CaptureKind::Ref(m) => m,
866 };
867 return CaptureKind::Ref(mutability);
868 },
869 ExprKind::Match(_, arms, _) => {
870 let mut mutability = Mutability::Not;
871 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
872 match capture {
873 CaptureKind::Value | CaptureKind::Use => break,
874 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
875 CaptureKind::Ref(Mutability::Not) => (),
876 }
877 }
878 return CaptureKind::Ref(mutability);
879 },
880 _ => break,
881 },
882 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
883 CaptureKind::Value | CaptureKind::Use => break,
884 capture @ CaptureKind::Ref(_) => return capture,
885 },
886 _ => break,
887 }
888
889 child_id = parent_id;
890 }
891
892 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
893 CaptureKind::Ref(Mutability::Not)
895 } else {
896 capture
897 }
898}
899
900pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
903 struct V<'cx, 'tcx> {
904 cx: &'cx LateContext<'tcx>,
905 loops: Vec<HirId>,
907 locals: HirIdSet,
909 allow_closure: bool,
911 captures: HirIdMap<CaptureKind>,
914 }
915 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
916 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
917 if !self.allow_closure {
918 return;
919 }
920
921 match e.kind {
922 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
923 if !self.locals.contains(&l) {
924 let cap = capture_local_usage(self.cx, e);
925 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
926 }
927 },
928 ExprKind::Closure(closure) => {
929 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
930 let local_id = match capture.place.base {
931 PlaceBase::Local(id) => id,
932 PlaceBase::Upvar(var) => var.var_path.hir_id,
933 _ => continue,
934 };
935 if !self.locals.contains(&local_id) {
936 let capture = match capture.info.capture_kind {
937 UpvarCapture::ByValue => CaptureKind::Value,
938 UpvarCapture::ByUse => CaptureKind::Use,
939 UpvarCapture::ByRef(kind) => match kind {
940 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
941 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
942 CaptureKind::Ref(Mutability::Mut)
943 },
944 },
945 };
946 self.captures
947 .entry(local_id)
948 .and_modify(|e| *e |= capture)
949 .or_insert(capture);
950 }
951 }
952 },
953 ExprKind::Loop(b, ..) => {
954 self.loops.push(e.hir_id);
955 self.visit_block(b);
956 self.loops.pop();
957 },
958 _ => {
959 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
960 walk_expr(self, e);
961 },
962 }
963 }
964
965 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
966 p.each_binding_or_first(&mut |_, id, _, _| {
967 self.locals.insert(id);
968 });
969 }
970 }
971
972 let mut v = V {
973 cx,
974 loops: Vec::new(),
975 locals: HirIdSet::default(),
976 allow_closure: true,
977 captures: HirIdMap::default(),
978 };
979 v.visit_expr(expr);
980 v.allow_closure.then_some(v.captures)
981}
982
983pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
985
986pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
989 let mut method_names = Vec::with_capacity(max_depth);
990 let mut arg_lists = Vec::with_capacity(max_depth);
991 let mut spans = Vec::with_capacity(max_depth);
992
993 let mut current = expr;
994 for _ in 0..max_depth {
995 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
996 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
997 break;
998 }
999 method_names.push(path.ident.name);
1000 arg_lists.push((*receiver, &**args));
1001 spans.push(path.ident.span);
1002 current = receiver;
1003 } else {
1004 break;
1005 }
1006 }
1007
1008 (method_names, arg_lists, spans)
1009}
1010
1011pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1018 let mut current = expr;
1019 let mut matched = Vec::with_capacity(methods.len());
1020 for method_name in methods.iter().rev() {
1021 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1023 if path.ident.name == *method_name {
1024 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1025 return None;
1026 }
1027 matched.push((receiver, args)); current = receiver; } else {
1030 return None;
1031 }
1032 } else {
1033 return None;
1034 }
1035 }
1036 matched.reverse();
1038 Some(matched)
1039}
1040
1041pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1043 cx.tcx
1044 .entry_fn(())
1045 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1046}
1047
1048pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1050 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1051 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1052}
1053
1054pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1056 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1057 match cx.tcx.hir_node_by_def_id(parent_id) {
1058 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1059 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1060 _ => None,
1061 }
1062}
1063
1064pub struct ContainsName<'a, 'tcx> {
1065 pub cx: &'a LateContext<'tcx>,
1066 pub name: Symbol,
1067}
1068
1069impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1070 type Result = ControlFlow<()>;
1071 type NestedFilter = nested_filter::OnlyBodies;
1072
1073 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1074 if self.name == name {
1075 ControlFlow::Break(())
1076 } else {
1077 ControlFlow::Continue(())
1078 }
1079 }
1080
1081 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1082 self.cx.tcx
1083 }
1084}
1085
1086pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1088 let mut cn = ContainsName { cx, name };
1089 cn.visit_expr(expr).is_break()
1090}
1091
1092pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1094 for_each_expr_without_closures(expr, |e| {
1095 if matches!(e.kind, ExprKind::Ret(..)) {
1096 ControlFlow::Break(())
1097 } else {
1098 ControlFlow::Continue(())
1099 }
1100 })
1101 .is_some()
1102}
1103
1104pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1106 get_parent_expr_for_hir(cx, e.hir_id)
1107}
1108
1109pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1112 match cx.tcx.parent_hir_node(hir_id) {
1113 Node::Expr(parent) => Some(parent),
1114 _ => None,
1115 }
1116}
1117
1118pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1120 let enclosing_node = cx
1121 .tcx
1122 .hir_get_enclosing_scope(hir_id)
1123 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1124 enclosing_node.and_then(|node| match node {
1125 Node::Block(block) => Some(block),
1126 Node::Item(&Item {
1127 kind: ItemKind::Fn { body: eid, .. },
1128 ..
1129 })
1130 | Node::ImplItem(&ImplItem {
1131 kind: ImplItemKind::Fn(_, eid),
1132 ..
1133 })
1134 | Node::TraitItem(&TraitItem {
1135 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1136 ..
1137 }) => match cx.tcx.hir_body(eid).value.kind {
1138 ExprKind::Block(block, _) => Some(block),
1139 _ => None,
1140 },
1141 _ => None,
1142 })
1143}
1144
1145pub fn get_enclosing_closure<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Closure<'tcx>> {
1147 cx.tcx.hir_parent_iter(hir_id).find_map(|(_, node)| {
1148 if let Node::Expr(expr) = node
1149 && let ExprKind::Closure(closure) = expr.kind
1150 {
1151 Some(closure)
1152 } else {
1153 None
1154 }
1155 })
1156}
1157
1158pub fn is_upvar_in_closure(cx: &LateContext<'_>, closure: &Closure<'_>, local_id: HirId) -> bool {
1160 cx.typeck_results()
1161 .closure_min_captures
1162 .get(&closure.def_id)
1163 .is_some_and(|x| x.contains_key(&local_id))
1164}
1165
1166pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1168 cx: &LateContext<'tcx>,
1169 expr: &Expr<'_>,
1170) -> Option<&'tcx Expr<'tcx>> {
1171 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1172 match node {
1173 Node::Expr(e) => match e.kind {
1174 ExprKind::Closure { .. }
1175 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1176 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1177
1178 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1180 _ => (),
1181 },
1182 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1183 _ => break,
1184 }
1185 }
1186 None
1187}
1188
1189pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1191 match tcx.hir_parent_iter(id).next() {
1192 Some((
1193 _,
1194 Node::Item(Item {
1195 kind: ItemKind::Impl(imp),
1196 ..
1197 }),
1198 )) => Some(imp),
1199 _ => None,
1200 }
1201}
1202
1203pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1214 while let ExprKind::Block(
1215 Block {
1216 stmts: [],
1217 expr: Some(inner),
1218 rules: BlockCheckMode::DefaultBlock,
1219 ..
1220 },
1221 _,
1222 ) = expr.kind
1223 {
1224 expr = inner;
1225 }
1226 expr
1227}
1228
1229pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1240 while let ExprKind::Block(
1241 Block {
1242 stmts: [],
1243 expr: Some(inner),
1244 rules: BlockCheckMode::DefaultBlock,
1245 ..
1246 }
1247 | Block {
1248 stmts:
1249 [
1250 Stmt {
1251 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1252 ..
1253 },
1254 ],
1255 expr: None,
1256 rules: BlockCheckMode::DefaultBlock,
1257 ..
1258 },
1259 _,
1260 ) = expr.kind
1261 {
1262 expr = inner;
1263 }
1264 expr
1265}
1266
1267pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1269 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1270 match iter.next() {
1271 Some((
1272 _,
1273 Node::Expr(Expr {
1274 kind: ExprKind::If(_, _, Some(else_expr)),
1275 ..
1276 }),
1277 )) => else_expr.hir_id == expr.hir_id,
1278 _ => false,
1279 }
1280}
1281
1282pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1285 let mut child_id = expr.hir_id;
1286 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1287 if let Node::LetStmt(LetStmt {
1288 init: Some(init),
1289 els: Some(els),
1290 ..
1291 }) = node
1292 && (init.hir_id == child_id || els.hir_id == child_id)
1293 {
1294 return true;
1295 }
1296
1297 child_id = parent_id;
1298 }
1299
1300 false
1301}
1302
1303pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1305 let mut child_id = expr.hir_id;
1306 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1307 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1308 && els.hir_id == child_id
1309 {
1310 return true;
1311 }
1312
1313 child_id = parent_id;
1314 }
1315
1316 false
1317}
1318
1319pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1334 let ty = cx.typeck_results().expr_ty(expr);
1335 if let Some(Range { start, end, limits, .. }) = Range::hir(cx, expr) {
1336 let start_is_none_or_min = start.is_none_or(|start| {
1337 if let rustc_ty::Adt(_, subst) = ty.kind()
1338 && let bnd_ty = subst.type_at(0)
1339 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1340 {
1341 start_const.is_numeric_min(cx.tcx, bnd_ty)
1342 } else {
1343 false
1344 }
1345 });
1346 let end_is_none_or_max = end.is_none_or(|end| match limits {
1347 RangeLimits::Closed => {
1348 if let rustc_ty::Adt(_, subst) = ty.kind()
1349 && let bnd_ty = subst.type_at(0)
1350 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1351 {
1352 end_const.is_numeric_max(cx.tcx, bnd_ty)
1353 } else {
1354 false
1355 }
1356 },
1357 RangeLimits::HalfOpen => {
1358 if let Some(container_path) = container_path
1359 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1360 && name.ident.name == sym::len
1361 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1362 {
1363 container_path.res == path.res
1364 } else {
1365 false
1366 }
1367 },
1368 });
1369 return start_is_none_or_min && end_is_none_or_max;
1370 }
1371 false
1372}
1373
1374pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1377 if is_integer_literal(e, value) {
1378 return true;
1379 }
1380 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1381 if let Some(Constant::Int(v)) =
1382 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1383 {
1384 return value == v;
1385 }
1386 false
1387}
1388
1389pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1391 if let ExprKind::Lit(spanned) = expr.kind
1393 && let LitKind::Int(v, _) = spanned.node
1394 {
1395 return v == value;
1396 }
1397 false
1398}
1399
1400pub fn is_integer_literal_untyped(expr: &Expr<'_>) -> bool {
1402 if let ExprKind::Lit(spanned) = expr.kind
1403 && let LitKind::Int(_, suffix) = spanned.node
1404 {
1405 return suffix == LitIntType::Unsuffixed;
1406 }
1407
1408 false
1409}
1410
1411pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1413 if let ExprKind::Lit(spanned) = expr.kind
1414 && let LitKind::Float(v, _) = spanned.node
1415 {
1416 v.as_str().parse() == Ok(value)
1417 } else {
1418 false
1419 }
1420}
1421
1422pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1430 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1431}
1432
1433#[must_use]
1437pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1438 loop {
1439 if span.from_expansion() {
1440 let data = span.ctxt().outer_expn_data();
1441 let new_span = data.call_site;
1442
1443 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1444 && mac_name == name
1445 {
1446 return Some(new_span);
1447 }
1448
1449 span = new_span;
1450 } else {
1451 return None;
1452 }
1453 }
1454}
1455
1456#[must_use]
1467pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1468 if span.from_expansion() {
1469 let data = span.ctxt().outer_expn_data();
1470 let new_span = data.call_site;
1471
1472 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1473 && mac_name == name
1474 {
1475 return Some(new_span);
1476 }
1477 }
1478
1479 None
1480}
1481
1482pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1484 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1485 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1486}
1487
1488pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1490 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1491 cx.tcx.instantiate_bound_regions_with_erased(arg)
1492}
1493
1494pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1496 if let ExprKind::Call(fun, _) = expr.kind
1497 && let ExprKind::Path(ref qp) = fun.kind
1498 {
1499 let res = cx.qpath_res(qp, fun.hir_id);
1500 return match res {
1501 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1502 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1503 _ => false,
1504 };
1505 }
1506 false
1507}
1508
1509pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1512 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1513 !matches!(
1514 cx.qpath_res(qpath, id),
1515 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1516 )
1517 }
1518
1519 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1520 i.into_iter().any(|pat| is_refutable(cx, pat))
1521 }
1522
1523 match pat.kind {
1524 PatKind::Missing => unreachable!(),
1525 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1527 PatKind::Box(pat) | PatKind::Ref(pat, _, _) => is_refutable(cx, pat),
1528 PatKind::Expr(PatExpr {
1529 kind: PatExprKind::Path(qpath),
1530 hir_id,
1531 ..
1532 }) => is_qpath_refutable(cx, qpath, *hir_id),
1533 PatKind::Or(pats) => {
1534 are_refutable(cx, pats)
1536 },
1537 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1538 PatKind::Struct(ref qpath, fields, _) => {
1539 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1540 },
1541 PatKind::TupleStruct(ref qpath, pats, _) => {
1542 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1543 },
1544 PatKind::Slice(head, middle, tail) => {
1545 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1546 rustc_ty::Slice(..) => {
1547 !head.is_empty() || middle.is_none() || !tail.is_empty()
1549 },
1550 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1551 _ => {
1552 true
1554 },
1555 }
1556 },
1557 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1558 }
1559}
1560
1561pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1564 if let PatKind::Or(pats) = pat.kind {
1565 pats.iter().for_each(f);
1566 } else {
1567 f(pat);
1568 }
1569}
1570
1571pub fn is_self(slf: &Param<'_>) -> bool {
1572 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1573 name.name == kw::SelfLower
1574 } else {
1575 false
1576 }
1577}
1578
1579pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1580 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1581 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1582 {
1583 return true;
1584 }
1585 false
1586}
1587
1588pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1589 (0..decl.inputs.len()).map(move |i| &body.params[i])
1590}
1591
1592pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1595 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1596 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1597 && ddpos.as_opt_usize().is_none()
1598 && cx
1599 .qpath_res(path, arm.pat.hir_id)
1600 .ctor_parent(cx)
1601 .is_lang_item(cx, ResultOk)
1602 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1603 && arm.body.res_local_id() == Some(hir_id)
1604 {
1605 return true;
1606 }
1607 false
1608 }
1609
1610 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1611 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1612 cx.qpath_res(path, arm.pat.hir_id)
1613 .ctor_parent(cx)
1614 .is_lang_item(cx, ResultErr)
1615 } else {
1616 false
1617 }
1618 }
1619
1620 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1621 if let MatchSource::TryDesugar(_) = *source {
1623 return Some(expr);
1624 }
1625
1626 if arms.len() == 2
1627 && arms[0].guard.is_none()
1628 && arms[1].guard.is_none()
1629 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1630 {
1631 return Some(expr);
1632 }
1633 }
1634
1635 None
1636}
1637
1638pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1648 let mut suppress_lint = false;
1649
1650 for id in ids {
1651 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1652 if let Some(expectation) = lint_id {
1653 cx.fulfill_expectation(expectation);
1654 }
1655
1656 match level {
1657 Level::Allow | Level::Expect => suppress_lint = true,
1658 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1659 }
1660 }
1661
1662 suppress_lint
1663}
1664
1665pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1673 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1674}
1675
1676pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1677 while let PatKind::Ref(subpat, _, _) = pat.kind {
1678 pat = subpat;
1679 }
1680 pat
1681}
1682
1683pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1684 Integer::from_int_ty(&tcx, ity).size().bits()
1685}
1686
1687#[expect(clippy::cast_possible_wrap)]
1688pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1690 let amt = 128 - int_bits(tcx, ity);
1691 ((u as i128) << amt) >> amt
1692}
1693
1694#[expect(clippy::cast_sign_loss)]
1695pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1697 let amt = 128 - int_bits(tcx, ity);
1698 ((u as u128) << amt) >> amt
1699}
1700
1701pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1703 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1704 let amt = 128 - bits;
1705 (u << amt) >> amt
1706}
1707
1708pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1709 attrs.iter().any(|attr| attr.has_name(symbol))
1710}
1711
1712pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1713 find_attr!(cx.tcx, hir_id, Repr { .. })
1714}
1715
1716pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1717 let mut prev_enclosing_node = None;
1718 let mut enclosing_node = node;
1719 while Some(enclosing_node) != prev_enclosing_node {
1720 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1721 return true;
1722 }
1723 prev_enclosing_node = Some(enclosing_node);
1724 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1725 }
1726
1727 false
1728}
1729
1730pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1733 tcx.hir_parent_owner_iter(id)
1734 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1735 .any(|(id, _)| {
1736 find_attr!(
1737 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1738 AutomaticallyDerived(..)
1739 )
1740 })
1741}
1742
1743pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1745 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1748}
1749
1750pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1755 let mut conds = Vec::new();
1756 let mut blocks: Vec<&Block<'_>> = Vec::new();
1757
1758 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1759 conds.push(cond);
1760 if let ExprKind::Block(block, _) = then.kind {
1761 blocks.push(block);
1762 } else {
1763 panic!("ExprKind::If node is not an ExprKind::Block");
1764 }
1765
1766 if let Some(else_expr) = r#else {
1767 expr = else_expr;
1768 } else {
1769 break;
1770 }
1771 }
1772
1773 if !blocks.is_empty()
1775 && let ExprKind::Block(block, _) = expr.kind
1776 {
1777 blocks.push(block);
1778 }
1779
1780 (conds, blocks)
1781}
1782
1783pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1785 if let ExprKind::Closure(&Closure {
1786 body,
1787 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1788 ..
1789 }) = expr.kind
1790 && let ExprKind::Block(
1791 Block {
1792 expr:
1793 Some(Expr {
1794 kind: ExprKind::DropTemps(inner_expr),
1795 ..
1796 }),
1797 ..
1798 },
1799 _,
1800 ) = tcx.hir_body(body).value.kind
1801 {
1802 Some(inner_expr)
1803 } else {
1804 None
1805 }
1806}
1807
1808pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1810 get_async_closure_expr(tcx, body.value)
1811}
1812
1813pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1815 let did = match expr.kind {
1816 ExprKind::Call(path, _) => {
1817 if let ExprKind::Path(ref qpath) = path.kind
1818 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1819 {
1820 Some(did)
1821 } else {
1822 None
1823 }
1824 },
1825 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1826 _ => None,
1827 };
1828
1829 did.is_some_and(|did| find_attr!(cx.tcx, did, MustUse { .. }))
1830}
1831
1832fn is_body_identity_function<'hir>(cx: &LateContext<'_>, func: &Body<'hir>) -> bool {
1846 let [param] = func.params else {
1847 return false;
1848 };
1849
1850 let mut param_pat = param.pat;
1851
1852 let mut advance_param_pat_over_stmts = |stmts: &[Stmt<'hir>]| {
1859 for stmt in stmts {
1860 if let StmtKind::Let(local) = stmt.kind
1861 && let Some(init) = local.init
1862 && is_expr_identity_of_pat(cx, param_pat, init, true)
1863 {
1864 param_pat = local.pat;
1865 } else {
1866 return false;
1867 }
1868 }
1869
1870 true
1871 };
1872
1873 let mut expr = func.value;
1874 loop {
1875 match expr.kind {
1876 ExprKind::Block(
1877 &Block {
1878 stmts: [],
1879 expr: Some(e),
1880 ..
1881 },
1882 _,
1883 )
1884 | ExprKind::Ret(Some(e)) => expr = e,
1885 ExprKind::Block(
1886 &Block {
1887 stmts: [stmt],
1888 expr: None,
1889 ..
1890 },
1891 _,
1892 ) => {
1893 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1894 && let ExprKind::Ret(Some(ret_val)) = e.kind
1895 {
1896 expr = ret_val;
1897 } else {
1898 return false;
1899 }
1900 },
1901 ExprKind::Block(
1902 &Block {
1903 stmts, expr: Some(e), ..
1904 },
1905 _,
1906 ) => {
1907 if !advance_param_pat_over_stmts(stmts) {
1908 return false;
1909 }
1910
1911 expr = e;
1912 },
1913 ExprKind::Block(&Block { stmts, expr: None, .. }, _) => {
1914 if let Some((last_stmt, stmts)) = stmts.split_last()
1915 && advance_param_pat_over_stmts(stmts)
1916 && let StmtKind::Semi(e) | StmtKind::Expr(e) = last_stmt.kind
1917 && let ExprKind::Ret(Some(ret_val)) = e.kind
1918 {
1919 expr = ret_val;
1920 } else {
1921 return false;
1922 }
1923 },
1924 _ => return is_expr_identity_of_pat(cx, param_pat, expr, true),
1925 }
1926 }
1927}
1928
1929pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1939 if cx
1940 .typeck_results()
1941 .pat_binding_modes()
1942 .get(pat.hir_id)
1943 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..)))
1944 {
1945 return false;
1949 }
1950
1951 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1953
1954 match (pat.kind, expr.kind) {
1955 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1956 expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1957 },
1958 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1959 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1960 },
1961 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1962 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1963 {
1964 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1965 },
1966 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1967 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1968 },
1969 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1970 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1971 {
1972 if let ExprKind::Path(ident) = &ident.kind
1974 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1975 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
1977 {
1978 true
1979 } else {
1980 false
1981 }
1982 },
1983 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
1984 if field_pats.len() == fields.len() =>
1985 {
1986 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1988 && unordered_over(field_pats, fields, |field_pat, field| {
1990 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
1991 })
1992 },
1993 _ => false,
1994 }
1995}
1996
1997pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2002 match expr.kind {
2003 ExprKind::Closure(&Closure { body, fn_decl, .. })
2004 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
2005 {
2006 is_body_identity_function(cx, cx.tcx.hir_body(body))
2007 },
2008 ExprKind::Path(QPath::Resolved(_, path))
2009 if path.segments.iter().all(|seg| seg.infer_args)
2010 && let Some(did) = path.res.opt_def_id() =>
2011 {
2012 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
2013 },
2014 _ => false,
2015 }
2016}
2017
2018pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2027 match expr.kind {
2028 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
2029 _ => expr.basic_res().is_diag_item(cx, sym::convert_identity),
2030 }
2031}
2032
2033pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2036 let mut child_id = expr.hir_id;
2037 let mut iter = tcx.hir_parent_iter(child_id);
2038 loop {
2039 match iter.next() {
2040 None => break None,
2041 Some((id, Node::Block(_))) => child_id = id,
2042 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2043 Some((_, Node::Expr(expr))) => match expr.kind {
2044 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2045 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2046 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2047 _ => break Some((Node::Expr(expr), child_id)),
2048 },
2049 Some((_, node)) => break Some((node, child_id)),
2050 }
2051 }
2052}
2053
2054pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2056 !matches!(
2057 get_expr_use_or_unification_node(tcx, expr),
2058 None | Some((
2059 Node::Stmt(Stmt {
2060 kind: StmtKind::Expr(_)
2061 | StmtKind::Semi(_)
2062 | StmtKind::Let(LetStmt {
2063 pat: Pat {
2064 kind: PatKind::Wild,
2065 ..
2066 },
2067 ..
2068 }),
2069 ..
2070 }),
2071 _
2072 ))
2073 )
2074}
2075
2076pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2078 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2079}
2080
2081pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2085 !expr.is_place_expr(|base| {
2086 cx.typeck_results()
2087 .adjustments()
2088 .get(base.hir_id)
2089 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2090 })
2091}
2092
2093pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2094 if is_no_core_crate(cx) {
2095 None
2096 } else if is_no_std_crate(cx) {
2097 Some("core")
2098 } else {
2099 Some("std")
2100 }
2101}
2102
2103pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2104 find_attr!(cx.tcx, crate, NoStd(..))
2105}
2106
2107pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2108 find_attr!(cx.tcx, crate, NoCore(..))
2109}
2110
2111pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2121 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2122 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2123 } else {
2124 false
2125 }
2126}
2127
2128pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2138 use rustc_trait_selection::traits;
2139 let predicates = cx
2140 .tcx
2141 .predicates_of(did)
2142 .predicates
2143 .iter()
2144 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2145 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2146}
2147
2148pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2150 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2151}
2152
2153pub fn fn_def_id_with_node_args<'tcx>(
2156 cx: &LateContext<'tcx>,
2157 expr: &Expr<'_>,
2158) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2159 let typeck = cx.typeck_results();
2160 match &expr.kind {
2161 ExprKind::MethodCall(..) => Some((
2162 typeck.type_dependent_def_id(expr.hir_id)?,
2163 typeck.node_args(expr.hir_id),
2164 )),
2165 ExprKind::Call(
2166 Expr {
2167 kind: ExprKind::Path(qpath),
2168 hir_id: path_hir_id,
2169 ..
2170 },
2171 ..,
2172 ) => {
2173 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2176 typeck.qpath_res(qpath, *path_hir_id)
2177 {
2178 Some((id, typeck.node_args(*path_hir_id)))
2179 } else {
2180 None
2181 }
2182 },
2183 _ => None,
2184 }
2185}
2186
2187pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2192 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2193 let expr_kind = expr_type.kind();
2194 let is_primitive = match expr_kind {
2195 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2196 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2197 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2198 is_recursively_primitive_type(*element_type)
2199 } else {
2200 unreachable!()
2201 }
2202 },
2203 _ => false,
2204 };
2205
2206 if is_primitive {
2207 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2210 rustc_ty::Slice(..) => return Some("slice".into()),
2211 rustc_ty::Array(..) => return Some("array".into()),
2212 rustc_ty::Tuple(..) => return Some("tuple".into()),
2213 _ => {
2214 let refs_peeled = expr_type.peel_refs();
2217 return Some(refs_peeled.walk().last().unwrap().to_string());
2218 },
2219 }
2220 }
2221 None
2222}
2223
2224pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2232where
2233 Hash: FnMut(&T) -> u64,
2234 Eq: FnMut(&T, &T) -> bool,
2235{
2236 match exprs {
2237 [a, b] if eq(a, b) => return vec![vec![a, b]],
2238 _ if exprs.len() <= 2 => return vec![],
2239 _ => {},
2240 }
2241
2242 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2243
2244 for expr in exprs {
2245 match buckets.entry(hash(expr)) {
2246 indexmap::map::Entry::Occupied(mut o) => {
2247 let bucket = o.get_mut();
2248 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2249 Some(group) => group.push(expr),
2250 None => bucket.push(vec![expr]),
2251 }
2252 },
2253 indexmap::map::Entry::Vacant(v) => {
2254 v.insert(vec![vec![expr]]);
2255 },
2256 }
2257 }
2258
2259 buckets
2260 .into_values()
2261 .flatten()
2262 .filter(|group| group.len() > 1)
2263 .collect()
2264}
2265
2266pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2269 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2270 if let PatKind::Ref(pat, _, _) = pat.kind {
2271 peel(pat, count + 1)
2272 } else {
2273 (pat, count)
2274 }
2275 }
2276 peel(pat, 0)
2277}
2278
2279pub fn peel_hir_expr_while<'tcx>(
2281 mut expr: &'tcx Expr<'tcx>,
2282 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2283) -> &'tcx Expr<'tcx> {
2284 while let Some(e) = f(expr) {
2285 expr = e;
2286 }
2287 expr
2288}
2289
2290pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2293 let mut remaining = count;
2294 let e = peel_hir_expr_while(expr, |e| match e.kind {
2295 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2296 remaining -= 1;
2297 Some(e)
2298 },
2299 _ => None,
2300 });
2301 (e, count - remaining)
2302}
2303
2304pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2307 let mut count: usize = 0;
2308 let mut curr_expr = expr;
2309 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2310 count = count.wrapping_add(1);
2311 curr_expr = local_expr;
2312 }
2313 (curr_expr, count)
2314}
2315
2316pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2319 let mut count = 0;
2320 let e = peel_hir_expr_while(expr, |e| match e.kind {
2321 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2322 count += 1;
2323 Some(e)
2324 },
2325 _ => None,
2326 });
2327 (e, count)
2328}
2329
2330pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2333 let mut count = 0;
2334 loop {
2335 match &ty.kind {
2336 TyKind::Ref(_, ref_ty) => {
2337 ty = ref_ty.ty;
2338 count += 1;
2339 },
2340 _ => break (ty, count),
2341 }
2342 }
2343}
2344
2345pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
2347 match &ty.kind {
2348 TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty),
2349 _ => ty,
2350 }
2351}
2352
2353pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2356 loop {
2357 match expr.kind {
2358 ExprKind::AddrOf(_, _, e) => expr = e,
2359 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2360 _ => break,
2361 }
2362 }
2363 expr
2364}
2365
2366pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2369 let mut operators = Vec::new();
2370 peel_hir_expr_while(expr, |expr| match expr.kind {
2371 ExprKind::AddrOf(_, _, e) => {
2372 operators.push(expr);
2373 Some(e)
2374 },
2375 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2376 operators.push(expr);
2377 Some(e)
2378 },
2379 _ => None,
2380 });
2381 operators
2382}
2383
2384pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2385 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2386 && let Res::Def(_, def_id) = path.res
2387 {
2388 #[allow(deprecated)]
2389 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2390 }
2391 false
2392}
2393
2394static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2395
2396fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2399 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2400 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2401 let value = map.entry(module);
2402 match value {
2403 Entry::Occupied(entry) => f(entry.get()),
2404 Entry::Vacant(entry) => {
2405 let mut names = Vec::new();
2406 for id in tcx.hir_module_free_items(module) {
2407 if matches!(tcx.def_kind(id.owner_id), DefKind::Const { .. })
2408 && let item = tcx.hir_item(id)
2409 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2410 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2411 && let Res::Def(DefKind::Struct, _) = path.res
2413 && find_attr!(tcx, item.hir_id(), RustcTestMarker(..))
2414 {
2415 names.push(ident.name);
2416 }
2417 }
2418 names.sort_unstable();
2419 f(entry.insert(names))
2420 },
2421 }
2422}
2423
2424pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2428 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2429 let node = tcx.hir_node(id);
2430 once((id, node))
2431 .chain(tcx.hir_parent_iter(id))
2432 .any(|(_id, node)| {
2435 if let Node::Item(item) = node
2436 && let ItemKind::Fn { ident, .. } = item.kind
2437 {
2438 return names.binary_search(&ident.name).is_ok();
2441 }
2442 false
2443 })
2444 })
2445}
2446
2447pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2454 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2455 if let Node::Item(item) = tcx.hir_node(id)
2456 && let ItemKind::Fn { ident, .. } = item.kind
2457 {
2458 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2459 names.binary_search(&ident.name).is_ok()
2460 })
2461 } else {
2462 false
2463 }
2464}
2465
2466pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2471 if let Some(cfgs) = find_attr!(tcx, id, CfgTrace(cfgs) => cfgs)
2472 && cfgs
2473 .iter()
2474 .any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. }))
2475 {
2476 true
2477 } else {
2478 false
2479 }
2480}
2481
2482pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2484 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2485}
2486
2487pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2489 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2490}
2491
2492pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2494 find_attr!(tcx, def_id, CfgTrace(..))
2495 || find_attr!(
2496 tcx.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2497 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)),
2498 CfgTrace(..)
2499 )
2500}
2501
2502pub fn walk_to_expr_usage<'tcx, T>(
2513 cx: &LateContext<'tcx>,
2514 e: &Expr<'tcx>,
2515 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2516) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2517 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2518 let mut child_id = e.hir_id;
2519
2520 while let Some((parent_id, parent)) = iter.next() {
2521 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2522 return Some(ControlFlow::Break(x));
2523 }
2524 let parent_expr = match parent {
2525 Node::Expr(e) => e,
2526 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2527 child_id = parent_id;
2528 continue;
2529 },
2530 Node::Arm(a) if a.body.hir_id == child_id => {
2531 child_id = parent_id;
2532 continue;
2533 },
2534 _ => return Some(ControlFlow::Continue((parent, child_id))),
2535 };
2536 match parent_expr.kind {
2537 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2538 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2539 child_id = id;
2540 iter = cx.tcx.hir_parent_iter(id);
2541 },
2542 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2543 _ => return Some(ControlFlow::Continue((parent, child_id))),
2544 }
2545 }
2546 debug_assert!(false, "no parent node found for `{child_id:?}`");
2547 None
2548}
2549
2550#[derive(Clone, Copy)]
2552pub enum DefinedTy<'tcx> {
2553 Hir(&'tcx hir::Ty<'tcx>),
2555 Mir {
2563 def_site_def_id: Option<DefId>,
2564 ty: Binder<'tcx, Ty<'tcx>>,
2565 },
2566}
2567
2568pub struct ExprUseCtxt<'tcx> {
2570 pub node: Node<'tcx>,
2572 pub child_id: HirId,
2574 pub adjustments: &'tcx [Adjustment<'tcx>],
2576 pub is_ty_unified: bool,
2578 pub moved_before_use: bool,
2580 pub same_ctxt: bool,
2582}
2583impl<'tcx> ExprUseCtxt<'tcx> {
2584 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2585 match self.node {
2586 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2587 Node::ExprField(field) => ExprUseNode::Field(field),
2588
2589 Node::Item(&Item {
2590 kind: ItemKind::Static(..) | ItemKind::Const(..),
2591 owner_id,
2592 ..
2593 })
2594 | Node::TraitItem(&TraitItem {
2595 kind: TraitItemKind::Const(..),
2596 owner_id,
2597 ..
2598 })
2599 | Node::ImplItem(&ImplItem {
2600 kind: ImplItemKind::Const(..),
2601 owner_id,
2602 ..
2603 }) => ExprUseNode::ConstStatic(owner_id),
2604
2605 Node::Item(&Item {
2606 kind: ItemKind::Fn { .. },
2607 owner_id,
2608 ..
2609 })
2610 | Node::TraitItem(&TraitItem {
2611 kind: TraitItemKind::Fn(..),
2612 owner_id,
2613 ..
2614 })
2615 | Node::ImplItem(&ImplItem {
2616 kind: ImplItemKind::Fn(..),
2617 owner_id,
2618 ..
2619 }) => ExprUseNode::Return(owner_id),
2620
2621 Node::Expr(use_expr) => match use_expr.kind {
2622 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2623 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2624 }),
2625
2626 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2627 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2628 Some(i) => ExprUseNode::FnArg(func, i),
2629 None => ExprUseNode::Callee,
2630 },
2631 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2632 use_expr.hir_id,
2633 name.args,
2634 args.iter()
2635 .position(|arg| arg.hir_id == self.child_id)
2636 .map_or(0, |i| i + 1),
2637 ),
2638 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2639 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2640 _ => ExprUseNode::Other,
2641 },
2642 _ => ExprUseNode::Other,
2643 }
2644 }
2645}
2646
2647pub enum ExprUseNode<'tcx> {
2649 LetStmt(&'tcx LetStmt<'tcx>),
2651 ConstStatic(OwnerId),
2653 Return(OwnerId),
2655 Field(&'tcx ExprField<'tcx>),
2657 FnArg(&'tcx Expr<'tcx>, usize),
2659 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2661 Callee,
2663 FieldAccess(Ident),
2665 AddrOf(ast::BorrowKind, Mutability),
2667 Other,
2668}
2669impl<'tcx> ExprUseNode<'tcx> {
2670 pub fn is_return(&self) -> bool {
2672 matches!(self, Self::Return(_))
2673 }
2674
2675 pub fn is_recv(&self) -> bool {
2677 matches!(self, Self::MethodArg(_, _, 0))
2678 }
2679
2680 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2682 match *self {
2683 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2684 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2685 def_site_def_id: Some(id.def_id.to_def_id()),
2686 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2687 }),
2688 Self::Return(id) => {
2689 if let Node::Expr(Expr {
2690 kind: ExprKind::Closure(c),
2691 ..
2692 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2693 {
2694 match c.fn_decl.output {
2695 FnRetTy::DefaultReturn(_) => None,
2696 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2697 }
2698 } else {
2699 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2700 Some(DefinedTy::Mir {
2701 def_site_def_id: Some(id.def_id.to_def_id()),
2702 ty,
2703 })
2704 }
2705 },
2706 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2707 Some(Expr {
2708 hir_id,
2709 kind: ExprKind::Struct(path, ..),
2710 ..
2711 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2712 .and_then(|(adt, variant)| {
2713 variant
2714 .fields
2715 .iter()
2716 .find(|f| f.name == field.ident.name)
2717 .map(|f| (adt, f))
2718 })
2719 .map(|(adt, field_def)| DefinedTy::Mir {
2720 def_site_def_id: Some(adt.did()),
2721 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2722 }),
2723 _ => None,
2724 },
2725 Self::FnArg(callee, i) => {
2726 let sig = expr_sig(cx, callee)?;
2727 let (hir_ty, ty) = sig.input_with_hir(i)?;
2728 Some(match hir_ty {
2729 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2730 None => DefinedTy::Mir {
2731 def_site_def_id: sig.predicates_id(),
2732 ty,
2733 },
2734 })
2735 },
2736 Self::MethodArg(id, _, i) => {
2737 let id = cx.typeck_results().type_dependent_def_id(id)?;
2738 let sig = cx.tcx.fn_sig(id).skip_binder();
2739 Some(DefinedTy::Mir {
2740 def_site_def_id: Some(id),
2741 ty: sig.input(i),
2742 })
2743 },
2744 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2745 }
2746 }
2747}
2748
2749pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2751 let mut adjustments = [].as_slice();
2752 let mut is_ty_unified = false;
2753 let mut moved_before_use = false;
2754 let mut same_ctxt = true;
2755 let ctxt = e.span.ctxt();
2756 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2757 if adjustments.is_empty()
2758 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2759 {
2760 adjustments = cx.typeck_results().expr_adjustments(e);
2761 }
2762 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2763 if let Node::Expr(e) = parent {
2764 match e.kind {
2765 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2766 is_ty_unified = true;
2767 moved_before_use = true;
2768 },
2769 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2770 is_ty_unified = true;
2771 moved_before_use = true;
2772 },
2773 ExprKind::Block(..) => moved_before_use = true,
2774 _ => {},
2775 }
2776 }
2777 ControlFlow::Continue(())
2778 });
2779 match node {
2780 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2781 node,
2782 child_id,
2783 adjustments,
2784 is_ty_unified,
2785 moved_before_use,
2786 same_ctxt,
2787 },
2788 None => ExprUseCtxt {
2789 node: Node::Crate(cx.tcx.hir_root_module()),
2790 child_id: HirId::INVALID,
2791 adjustments: &[],
2792 is_ty_unified: true,
2793 moved_before_use: true,
2794 same_ctxt: false,
2795 },
2796 }
2797}
2798
2799pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2801 let mut pos = 0;
2802 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2803 let end = pos + t.len;
2804 let range = pos as usize..end as usize;
2805 let inner = InnerSpan::new(range.start, range.end);
2806 pos = end;
2807 (t.kind, s.get(range).unwrap_or_default(), inner)
2808 })
2809}
2810
2811pub fn span_contains_comment(cx: &impl source::HasSession, span: Span) -> bool {
2814 span.check_source_text(cx, |snippet| {
2815 tokenize(snippet, FrontmatterAllowed::No).any(|token| {
2816 matches!(
2817 token.kind,
2818 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2819 )
2820 })
2821 })
2822}
2823
2824pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2829 span.check_source_text(cx, |snippet| {
2830 tokenize_with_text(snippet).any(|(token, _, _)| match token {
2831 TokenKind::Whitespace => false,
2832 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2833 _ => true,
2834 })
2835 })
2836}
2837pub fn span_extract_comment(cx: &impl source::HasSession, span: Span) -> String {
2841 span_extract_comments(cx, span).join("\n")
2842}
2843
2844pub fn span_extract_comments(cx: &impl source::HasSession, span: Span) -> Vec<String> {
2848 span.with_source_text(cx, |snippet| {
2849 tokenize_with_text(snippet)
2850 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2851 .map(|(_, s, _)| s.to_string())
2852 .collect::<Vec<_>>()
2853 })
2854 .unwrap_or_default()
2855}
2856
2857pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2858 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2859}
2860
2861pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2886 cx: &LateContext<'_>,
2887 pat: &'a Pat<'hir>,
2888 else_body: &Expr<'_>,
2889) -> Option<&'a Pat<'hir>> {
2890 if let Some([inner_pat]) = as_some_pattern(cx, pat)
2891 && !is_refutable(cx, inner_pat)
2892 && let else_body = peel_blocks(else_body)
2893 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2894 && let ExprKind::Path(ret_path) = ret_val.kind
2895 && cx
2896 .qpath_res(&ret_path, ret_val.hir_id)
2897 .ctor_parent(cx)
2898 .is_lang_item(cx, OptionNone)
2899 {
2900 Some(inner_pat)
2901 } else {
2902 None
2903 }
2904}
2905
2906macro_rules! op_utils {
2907 ($($name:ident $assign:ident)*) => {
2908 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2910
2911 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2913
2914 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2916 match kind {
2917 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2918 _ => None,
2919 }
2920 }
2921 };
2922}
2923
2924op_utils! {
2925 Add AddAssign
2926 Sub SubAssign
2927 Mul MulAssign
2928 Div DivAssign
2929 Rem RemAssign
2930 BitXor BitXorAssign
2931 BitAnd BitAndAssign
2932 BitOr BitOrAssign
2933 Shl ShlAssign
2934 Shr ShrAssign
2935}
2936
2937pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2940 match *pat {
2941 PatKind::Wild => true,
2942 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2943 !visitors::is_local_used(cx, body, id)
2944 },
2945 _ => false,
2946 }
2947}
2948
2949#[derive(Clone, Copy)]
2950pub enum RequiresSemi {
2951 Yes,
2952 No,
2953}
2954impl RequiresSemi {
2955 pub fn requires_semi(self) -> bool {
2956 matches!(self, Self::Yes)
2957 }
2958}
2959
2960#[expect(clippy::too_many_lines)]
2963pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2964 struct BreakTarget {
2965 id: HirId,
2966 unused: bool,
2967 }
2968
2969 struct V<'cx, 'tcx> {
2970 cx: &'cx LateContext<'tcx>,
2971 break_targets: Vec<BreakTarget>,
2972 break_targets_for_result_ty: u32,
2973 in_final_expr: bool,
2974 requires_semi: bool,
2975 is_never: bool,
2976 }
2977
2978 impl V<'_, '_> {
2979 fn push_break_target(&mut self, id: HirId) {
2980 self.break_targets.push(BreakTarget { id, unused: true });
2981 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2982 }
2983 }
2984
2985 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
2986 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
2987 if self.is_never && self.break_targets.is_empty() {
3004 if self.in_final_expr && !self.requires_semi {
3005 match e.kind {
3008 ExprKind::DropTemps(e) => self.visit_expr(e),
3009 ExprKind::If(_, then, Some(else_)) => {
3010 self.visit_expr(then);
3011 self.visit_expr(else_);
3012 },
3013 ExprKind::Match(_, arms, _) => {
3014 for arm in arms {
3015 self.visit_expr(arm.body);
3016 }
3017 },
3018 ExprKind::Loop(b, ..) => {
3019 self.push_break_target(e.hir_id);
3020 self.in_final_expr = false;
3021 self.visit_block(b);
3022 self.break_targets.pop();
3023 },
3024 ExprKind::Block(b, _) => {
3025 if b.targeted_by_break {
3026 self.push_break_target(b.hir_id);
3027 self.visit_block(b);
3028 self.break_targets.pop();
3029 } else {
3030 self.visit_block(b);
3031 }
3032 },
3033 _ => {
3034 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3035 },
3036 }
3037 }
3038 return;
3039 }
3040 match e.kind {
3041 ExprKind::DropTemps(e) => self.visit_expr(e),
3042 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3043 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3044 self.in_final_expr = false;
3045 self.visit_expr(e);
3046 self.is_never = true;
3047 },
3048 ExprKind::Break(dest, e) => {
3049 if let Some(e) = e {
3050 self.in_final_expr = false;
3051 self.visit_expr(e);
3052 }
3053 if let Ok(id) = dest.target_id
3054 && let Some((i, target)) = self
3055 .break_targets
3056 .iter_mut()
3057 .enumerate()
3058 .find(|(_, target)| target.id == id)
3059 {
3060 target.unused &= self.is_never;
3061 if i < self.break_targets_for_result_ty as usize {
3062 self.requires_semi = true;
3063 }
3064 }
3065 self.is_never = true;
3066 },
3067 ExprKind::If(cond, then, else_) => {
3068 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3069 self.visit_expr(cond);
3070 self.in_final_expr = in_final_expr;
3071
3072 if self.is_never {
3073 self.visit_expr(then);
3074 if let Some(else_) = else_ {
3075 self.visit_expr(else_);
3076 }
3077 } else {
3078 self.visit_expr(then);
3079 let is_never = mem::replace(&mut self.is_never, false);
3080 if let Some(else_) = else_ {
3081 self.visit_expr(else_);
3082 self.is_never &= is_never;
3083 }
3084 }
3085 },
3086 ExprKind::Match(scrutinee, arms, _) => {
3087 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3088 self.visit_expr(scrutinee);
3089 self.in_final_expr = in_final_expr;
3090
3091 if self.is_never {
3092 for arm in arms {
3093 self.visit_arm(arm);
3094 }
3095 } else {
3096 let mut is_never = true;
3097 for arm in arms {
3098 self.is_never = false;
3099 if let Some(guard) = arm.guard {
3100 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3101 self.visit_expr(guard);
3102 self.in_final_expr = in_final_expr;
3103 self.is_never = false;
3105 }
3106 self.visit_expr(arm.body);
3107 is_never &= self.is_never;
3108 }
3109 self.is_never = is_never;
3110 }
3111 },
3112 ExprKind::Loop(b, _, _, _) => {
3113 self.push_break_target(e.hir_id);
3114 self.in_final_expr = false;
3115 self.visit_block(b);
3116 self.is_never = self.break_targets.pop().unwrap().unused;
3117 },
3118 ExprKind::Block(b, _) => {
3119 if b.targeted_by_break {
3120 self.push_break_target(b.hir_id);
3121 self.visit_block(b);
3122 self.is_never &= self.break_targets.pop().unwrap().unused;
3123 } else {
3124 self.visit_block(b);
3125 }
3126 },
3127 _ => {
3128 self.in_final_expr = false;
3129 walk_expr(self, e);
3130 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3131 },
3132 }
3133 }
3134
3135 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3136 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3137 for s in b.stmts {
3138 self.visit_stmt(s);
3139 }
3140 self.in_final_expr = in_final_expr;
3141 if let Some(e) = b.expr {
3142 self.visit_expr(e);
3143 }
3144 }
3145
3146 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3147 if let Some(e) = l.init {
3148 self.visit_expr(e);
3149 }
3150 if let Some(else_) = l.els {
3151 let is_never = self.is_never;
3152 self.visit_block(else_);
3153 self.is_never = is_never;
3154 }
3155 }
3156
3157 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3158 if let Some(guard) = arm.guard {
3159 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3160 self.visit_expr(guard);
3161 self.in_final_expr = in_final_expr;
3162 }
3163 self.visit_expr(arm.body);
3164 }
3165 }
3166
3167 if cx.typeck_results().expr_ty(e).is_never() {
3168 Some(RequiresSemi::No)
3169 } else if let ExprKind::Block(b, _) = e.kind
3170 && !b.targeted_by_break
3171 && b.expr.is_none()
3172 {
3173 None
3175 } else {
3176 let mut v = V {
3177 cx,
3178 break_targets: Vec::new(),
3179 break_targets_for_result_ty: 0,
3180 in_final_expr: true,
3181 requires_semi: false,
3182 is_never: false,
3183 };
3184 v.visit_expr(e);
3185 v.is_never
3186 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3187 RequiresSemi::Yes
3188 } else {
3189 RequiresSemi::No
3190 })
3191 }
3192}
3193
3194pub fn get_path_from_caller_to_method_type<'tcx>(
3200 tcx: TyCtxt<'tcx>,
3201 from: LocalDefId,
3202 method: DefId,
3203 args: GenericArgsRef<'tcx>,
3204) -> String {
3205 let assoc_item = tcx.associated_item(method);
3206 let def_id = assoc_item.container_id(tcx);
3207 match assoc_item.container {
3208 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3209 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3210 let ty = tcx.type_of(def_id).instantiate_identity();
3211 get_path_to_ty(tcx, from, ty, args)
3212 },
3213 }
3214}
3215
3216fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3217 match ty.kind() {
3218 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3219 rustc_ty::Array(..)
3221 | rustc_ty::Dynamic(..)
3222 | rustc_ty::Never
3223 | rustc_ty::RawPtr(_, _)
3224 | rustc_ty::Ref(..)
3225 | rustc_ty::Slice(_)
3226 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3227 _ => ty.to_string(),
3228 }
3229}
3230
3231fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3233 if callee.is_local() {
3235 let callee_path = tcx.def_path(callee);
3236 let caller_path = tcx.def_path(from.to_def_id());
3237 maybe_get_relative_path(&caller_path, &callee_path, 2)
3238 } else {
3239 tcx.def_path_str(callee)
3240 }
3241}
3242
3243fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3256 use itertools::EitherOrBoth::{Both, Left, Right};
3257
3258 let unique_parts = to
3260 .data
3261 .iter()
3262 .zip_longest(from.data.iter())
3263 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3264 .map(|el| match el {
3265 Both(l, r) => Both(l.data, r.data),
3266 Left(l) => Left(l.data),
3267 Right(r) => Right(r.data),
3268 });
3269
3270 let mut go_up_by = 0;
3272 let mut path = Vec::new();
3273 for el in unique_parts {
3274 match el {
3275 Both(l, r) => {
3276 if let DefPathData::TypeNs(sym) = l {
3286 path.push(sym);
3287 }
3288 if let DefPathData::TypeNs(_) = r {
3289 go_up_by += 1;
3290 }
3291 },
3292 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3297 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3302 _ => {},
3303 }
3304 }
3305
3306 if go_up_by > max_super {
3307 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3309 if let DefPathData::TypeNs(sym) = el.data {
3310 Some(sym)
3311 } else {
3312 None
3313 }
3314 })))
3315 } else if go_up_by == 0 && path.is_empty() {
3316 String::from("Self")
3317 } else {
3318 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3319 }
3320}
3321
3322pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3325 matches!(
3326 cx.tcx.parent_hir_node(id),
3327 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3328 )
3329}
3330
3331pub fn is_block_like(expr: &Expr<'_>) -> bool {
3334 matches!(
3335 expr.kind,
3336 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3337 )
3338}
3339
3340pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3342 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3343 match expr.kind {
3344 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3345 _ if is_block_like(expr) => is_operand,
3346 _ => false,
3347 }
3348 }
3349
3350 contains_block(expr, false)
3351}
3352
3353pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3355 if let Some(parent_expr) = get_parent_expr(cx, expr)
3356 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3357 && receiver.hir_id == expr.hir_id
3358 {
3359 return true;
3360 }
3361 false
3362}
3363
3364pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3367 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3368 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3369 && temporary_ty
3370 .walk()
3371 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3372 {
3373 ControlFlow::Break(())
3374 } else {
3375 ControlFlow::Continue(())
3376 }
3377 })
3378 .is_break()
3379}
3380
3381pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3392 let expr_ty_is_adjusted = cx
3393 .typeck_results()
3394 .expr_adjustments(expr)
3395 .iter()
3396 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3398 if expr_ty_is_adjusted {
3399 return true;
3400 }
3401
3402 match expr.kind {
3405 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3406 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3407
3408 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3409 return false;
3410 }
3411
3412 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3413 let mut args_with_ty_param = {
3414 fn_sig
3415 .inputs()
3416 .skip_binder()
3417 .iter()
3418 .skip(self_arg_count)
3419 .zip(args)
3420 .filter_map(|(arg_ty, arg)| {
3421 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3422 Some(arg)
3423 } else {
3424 None
3425 }
3426 })
3427 };
3428 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3429 },
3430 ExprKind::Struct(qpath, _, _) => {
3432 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3433 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3434 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3435 return true;
3437 };
3438 v_def
3439 .fields
3440 .iter()
3441 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3442 } else {
3443 false
3444 }
3445 },
3446 ExprKind::Block(
3448 &Block {
3449 expr: Some(ret_expr), ..
3450 },
3451 _,
3452 )
3453 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3454
3455 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3457 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3459 ExprKind::If(_, then, maybe_else) => {
3461 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3462 },
3463 ExprKind::Match(_, arms, _) => arms
3464 .iter()
3465 .map(|arm| arm.body)
3466 .any(|body| expr_requires_coercion(cx, body)),
3467 _ => false,
3468 }
3469}
3470
3471pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3474 if let Some(hir_id) = expr.res_local_id()
3475 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3476 {
3477 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3478 } else if let ExprKind::Path(p) = &expr.kind
3479 && let Some(mutability) = cx
3480 .qpath_res(p, expr.hir_id)
3481 .opt_def_id()
3482 .and_then(|id| cx.tcx.static_mutability(id))
3483 {
3484 mutability == Mutability::Mut
3485 } else if let ExprKind::Field(parent, _) = expr.kind {
3486 is_mutable(cx, parent)
3487 } else {
3488 true
3489 }
3490}
3491
3492pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3495 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3496 return hir_ty;
3497 };
3498 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3499 && let Some(segment) = path.segments.last()
3500 && segment.ident.name == sym::Option
3501 && let Res::Def(DefKind::Enum, def_id) = segment.res
3502 && def_id == option_def_id
3503 && let [GenericArg::Type(arg_ty)] = segment.args().args
3504 {
3505 hir_ty = arg_ty.as_unambig_ty();
3506 }
3507 hir_ty
3508}
3509
3510pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3513 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3514 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3515 && let ctxt = expr.span.ctxt()
3516 && for_each_expr_without_closures(into_future_arg, |e| {
3517 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3518 })
3519 .is_none()
3520 {
3521 Some(into_future_arg)
3522 } else {
3523 None
3524 }
3525}
3526
3527pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3529 if let ExprKind::Call(fn_expr, []) = &expr.kind
3530 && let ExprKind::Path(qpath) = &fn_expr.kind
3531 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3532 {
3533 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3534 } else {
3535 false
3536 }
3537}
3538
3539pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3556 let enclosing_body_owner = cx
3557 .tcx
3558 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3559 let mut prev_id = expr.hir_id;
3560 let mut skip_until_id = None;
3561 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3562 if hir_id == enclosing_body_owner {
3563 return true;
3564 }
3565 if let Some(id) = skip_until_id {
3566 prev_id = hir_id;
3567 if id == hir_id {
3568 skip_until_id = None;
3569 }
3570 continue;
3571 }
3572 match node {
3573 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3574 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3575 Node::Expr(expr) => match expr.kind {
3576 ExprKind::Ret(_) => return true,
3577 ExprKind::If(_, then, opt_else)
3578 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3579 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3580 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3581 ExprKind::Break(
3582 Destination {
3583 target_id: Ok(target_id),
3584 ..
3585 },
3586 _,
3587 ) => skip_until_id = Some(target_id),
3588 _ => break,
3589 },
3590 _ => break,
3591 }
3592 prev_id = hir_id;
3593 }
3594
3595 false
3598}
3599
3600pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3603 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3604 matches!(
3605 adj.kind,
3606 Adjust::Deref(DerefAdjustKind::Overloaded(_))
3607 | Adjust::Pointer(PointerCoercion::Unsize)
3608 | Adjust::NeverToAny
3609 )
3610 })
3611}
3612
3613pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3615 matches!(
3616 expr.kind,
3617 ExprKind::Closure(Closure {
3618 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3619 CoroutineDesugaring::Async,
3620 CoroutineSource::Block
3621 )),
3622 ..
3623 })
3624 )
3625}
3626
3627pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3629 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3630}