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