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_empty_block(expr: &Expr<'_>) -> bool {
316 matches!(
317 expr.kind,
318 ExprKind::Block(
319 Block {
320 stmts: [],
321 expr: None,
322 ..
323 },
324 _,
325 )
326 )
327}
328
329pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
331 matches!(
332 expr.kind,
333 ExprKind::Block(
334 Block {
335 stmts: [],
336 expr: None,
337 ..
338 },
339 _
340 ) | ExprKind::Tup([])
341 )
342}
343
344pub fn is_wild(pat: &Pat<'_>) -> bool {
346 matches!(pat.kind, PatKind::Wild)
347}
348
349pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> {
356 if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind
357 && cx
358 .qpath_res(qpath, pat.hir_id)
359 .ctor_parent(cx)
360 .is_lang_item(cx, OptionSome)
361 {
362 Some(inner)
363 } else {
364 None
365 }
366}
367
368pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
370 matches!(pat.kind,
371 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
372 if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone))
373}
374
375pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
377 is_none_pattern(cx, arm.pat)
378 && matches!(
379 peel_blocks(arm.body).kind,
380 ExprKind::Path(qpath)
381 if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)
382 )
383}
384
385pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
387 match *qpath {
388 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
389 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
390 QPath::TypeRelative(..) => false,
391 }
392}
393
394pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
396 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
397 && let ItemKind::Impl(imp) = item.kind
398 {
399 imp.of_trait.is_some()
400 } else {
401 false
402 }
403}
404
405pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
406 match *path {
407 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
408 QPath::TypeRelative(_, seg) => seg,
409 }
410}
411
412pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
413 last_path_segment(qpath)
414 .args
415 .map_or(&[][..], |a| a.args)
416 .iter()
417 .filter_map(|a| match a {
418 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
419 _ => None,
420 })
421}
422
423pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
428 match expr.kind {
429 ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
430 ExprKind::Path(QPath::Resolved(
431 _,
432 Path {
433 res: Res::Local(local), ..
434 },
435 )) => Some(*local),
436 _ => None,
437 }
438}
439
440pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
456 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
457 && let ItemKind::Impl(impl_) = &item.kind
458 && let Some(of_trait) = impl_.of_trait
459 {
460 return Some(&of_trait.trait_ref);
461 }
462 None
463}
464
465fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
473 let mut result = vec![];
474 let root = loop {
475 match e.kind {
476 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
477 result.push(e);
478 e = ep;
479 },
480 _ => break e,
481 }
482 };
483 result.reverse();
484 (result, root)
485}
486
487pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
489 cx.typeck_results()
490 .expr_adjustments(e)
491 .iter()
492 .find_map(|a| match a.kind {
493 Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)),
494 Adjust::Deref(DerefAdjustKind::Builtin) => None,
495 _ => Some(None),
496 })
497 .and_then(|x| x)
498}
499
500pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
503 let (s1, r1) = projection_stack(e1);
504 let (s2, r2) = projection_stack(e2);
505 if !eq_expr_value(cx, r1, r2) {
506 return true;
507 }
508 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
509 return false;
510 }
511
512 for (x1, x2) in zip(&s1, &s2) {
513 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
514 return false;
515 }
516
517 match (&x1.kind, &x2.kind) {
518 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
519 if i1 != i2 {
520 return true;
521 }
522 },
523 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
524 if !eq_expr_value(cx, i1, i2) {
525 return false;
526 }
527 },
528 _ => return false,
529 }
530 }
531 false
532}
533
534fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
537 let std_types_symbols = &[
538 sym::Vec,
539 sym::VecDeque,
540 sym::LinkedList,
541 sym::HashMap,
542 sym::BTreeMap,
543 sym::HashSet,
544 sym::BTreeSet,
545 sym::BinaryHeap,
546 ];
547
548 if let QPath::TypeRelative(_, method) = path
549 && method.ident.name == sym::new
550 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
551 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
552 {
553 return Some(adt.did()) == cx.tcx.lang_items().string()
554 || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
555 }
556 false
557}
558
559pub fn is_default_equivalent_call(
561 cx: &LateContext<'_>,
562 repl_func: &Expr<'_>,
563 whole_call_expr: Option<&Expr<'_>>,
564) -> bool {
565 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
566 && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx)
567 && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default)
568 || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath))
569 {
570 return true;
571 }
572
573 let Some(e) = whole_call_expr else { return false };
576 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
577 return false;
578 };
579 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
580 return false;
581 };
582 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
583 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
584 cx.tcx.lifetimes.re_erased.into()
585 } else if param.index == 0 && param.name == kw::SelfUpper {
586 ty.into()
587 } else {
588 param.to_error(cx.tcx)
589 }
590 });
591 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
592
593 let Ok(Some(instance)) = instance else { return false };
594 if let rustc_ty::InstanceKind::Item(def) = instance.def
595 && !cx.tcx.is_mir_available(def)
596 {
597 return false;
598 }
599 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
600 return false;
601 };
602 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
603 return false;
604 };
605
606 let body = cx.tcx.instance_mir(instance.def);
612 for block_data in body.basic_blocks.iter() {
613 if block_data.statements.len() == 1
614 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
615 && assign.0.local == RETURN_PLACE
616 && let Rvalue::Aggregate(kind, _places) = &assign.1
617 && let AggregateKind::Adt(did, variant_index, _, _, _) = **kind
618 && let def = cx.tcx.adt_def(did)
619 && let variant = &def.variant(variant_index)
620 && variant.fields.is_empty()
621 && let Some((_, did)) = variant.ctor
622 && did == repl_def_id
623 {
624 return true;
625 } else if block_data.statements.is_empty()
626 && let Some(term) = &block_data.terminator
627 {
628 match &term.kind {
629 TerminatorKind::Call {
630 func: Operand::Constant(c),
631 ..
632 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
633 && *did == repl_def_id =>
634 {
635 return true;
636 },
637 TerminatorKind::TailCall {
638 func: Operand::Constant(c),
639 ..
640 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
641 && *did == repl_def_id =>
642 {
643 return true;
644 },
645 _ => {},
646 }
647 }
648 }
649 false
650}
651
652pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
656 match &e.kind {
657 ExprKind::Lit(lit) => match lit.node {
658 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
659 LitKind::Str(s, _) => s.is_empty(),
660 _ => false,
661 },
662 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
663 ExprKind::Repeat(x, len) => {
664 if let ConstArgKind::Anon(anon_const) = len.kind
665 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
666 && let LitKind::Int(v, _) = const_lit.node
667 && v <= 32
668 && is_default_equivalent(cx, x)
669 {
670 true
671 } else {
672 false
673 }
674 },
675 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
676 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
677 ExprKind::Path(qpath) => cx
678 .qpath_res(qpath, e.hir_id)
679 .ctor_parent(cx)
680 .is_lang_item(cx, OptionNone),
681 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
682 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
683 _ => false,
684 }
685}
686
687fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
688 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
689 && seg.ident.name == sym::from
690 {
691 match arg.kind {
692 ExprKind::Lit(hir::Lit {
693 node: LitKind::Str(sym, _),
694 ..
695 }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String),
696 ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec),
697 ExprKind::Repeat(_, len) => {
698 if let ConstArgKind::Anon(anon_const) = len.kind
699 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
700 && let LitKind::Int(v, _) = const_lit.node
701 {
702 return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec);
703 }
704 },
705 _ => (),
706 }
707 }
708 false
709}
710
711pub fn can_move_expr_to_closure_no_visit<'tcx>(
743 cx: &LateContext<'tcx>,
744 expr: &'tcx Expr<'_>,
745 loop_ids: &[HirId],
746 ignore_locals: &HirIdSet,
747) -> bool {
748 match expr.kind {
749 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
750 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
751 if loop_ids.contains(&id) =>
752 {
753 true
754 },
755 ExprKind::Break(..)
756 | ExprKind::Continue(_)
757 | ExprKind::Ret(_)
758 | ExprKind::Yield(..)
759 | ExprKind::InlineAsm(_) => false,
760 ExprKind::Field(
763 &Expr {
764 hir_id,
765 kind:
766 ExprKind::Path(QPath::Resolved(
767 _,
768 Path {
769 res: Res::Local(local_id),
770 ..
771 },
772 )),
773 ..
774 },
775 _,
776 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
777 false
779 },
780 _ => true,
781 }
782}
783
784#[derive(Debug, Clone, Copy, PartialEq, Eq)]
786pub enum CaptureKind {
787 Value,
788 Use,
789 Ref(Mutability),
790}
791impl CaptureKind {
792 pub fn is_imm_ref(self) -> bool {
793 self == Self::Ref(Mutability::Not)
794 }
795}
796impl std::ops::BitOr for CaptureKind {
797 type Output = Self;
798 fn bitor(self, rhs: Self) -> Self::Output {
799 match (self, rhs) {
800 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
801 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
802 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
803 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
804 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
805 }
806 }
807}
808impl std::ops::BitOrAssign for CaptureKind {
809 fn bitor_assign(&mut self, rhs: Self) {
810 *self = *self | rhs;
811 }
812}
813
814pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
820 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
821 let mut capture = CaptureKind::Ref(Mutability::Not);
822 pat.each_binding_or_first(&mut |_, id, span, _| match cx
823 .typeck_results()
824 .extract_binding_mode(cx.sess(), id, span)
825 .0
826 {
827 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
828 capture = CaptureKind::Value;
829 },
830 ByRef::Yes(_, Mutability::Mut) if capture != CaptureKind::Value => {
831 capture = CaptureKind::Ref(Mutability::Mut);
832 },
833 _ => (),
834 });
835 capture
836 }
837
838 debug_assert!(matches!(
839 e.kind,
840 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
841 ));
842
843 let mut child_id = e.hir_id;
844 let mut capture = CaptureKind::Value;
845 let mut capture_expr_ty = e;
846
847 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
848 if let [
849 Adjustment {
850 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
851 target,
852 },
853 ref adjust @ ..,
854 ] = *cx
855 .typeck_results()
856 .adjustments()
857 .get(child_id)
858 .map_or(&[][..], |x| &**x)
859 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
860 *adjust.last().map_or(target, |a| a.target).kind()
861 {
862 return CaptureKind::Ref(mutability);
863 }
864
865 match parent {
866 Node::Expr(e) => match e.kind {
867 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
868 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
869 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
870 return CaptureKind::Ref(Mutability::Mut);
871 },
872 ExprKind::Field(..) => {
873 if capture == CaptureKind::Value {
874 capture_expr_ty = e;
875 }
876 },
877 ExprKind::Let(let_expr) => {
878 let mutability = match pat_capture_kind(cx, let_expr.pat) {
879 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
880 CaptureKind::Ref(m) => m,
881 };
882 return CaptureKind::Ref(mutability);
883 },
884 ExprKind::Match(_, arms, _) => {
885 let mut mutability = Mutability::Not;
886 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
887 match capture {
888 CaptureKind::Value | CaptureKind::Use => break,
889 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
890 CaptureKind::Ref(Mutability::Not) => (),
891 }
892 }
893 return CaptureKind::Ref(mutability);
894 },
895 _ => break,
896 },
897 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
898 CaptureKind::Value | CaptureKind::Use => break,
899 capture @ CaptureKind::Ref(_) => return capture,
900 },
901 _ => break,
902 }
903
904 child_id = parent_id;
905 }
906
907 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
908 CaptureKind::Ref(Mutability::Not)
910 } else {
911 capture
912 }
913}
914
915pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
918 struct V<'cx, 'tcx> {
919 cx: &'cx LateContext<'tcx>,
920 loops: Vec<HirId>,
922 locals: HirIdSet,
924 allow_closure: bool,
926 captures: HirIdMap<CaptureKind>,
929 }
930 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
931 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
932 if !self.allow_closure {
933 return;
934 }
935
936 match e.kind {
937 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
938 if !self.locals.contains(&l) {
939 let cap = capture_local_usage(self.cx, e);
940 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
941 }
942 },
943 ExprKind::Closure(closure) => {
944 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
945 let local_id = match capture.place.base {
946 PlaceBase::Local(id) => id,
947 PlaceBase::Upvar(var) => var.var_path.hir_id,
948 _ => continue,
949 };
950 if !self.locals.contains(&local_id) {
951 let capture = match capture.info.capture_kind {
952 UpvarCapture::ByValue => CaptureKind::Value,
953 UpvarCapture::ByUse => CaptureKind::Use,
954 UpvarCapture::ByRef(kind) => match kind {
955 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
956 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
957 CaptureKind::Ref(Mutability::Mut)
958 },
959 },
960 };
961 self.captures
962 .entry(local_id)
963 .and_modify(|e| *e |= capture)
964 .or_insert(capture);
965 }
966 }
967 },
968 ExprKind::Loop(b, ..) => {
969 self.loops.push(e.hir_id);
970 self.visit_block(b);
971 self.loops.pop();
972 },
973 _ => {
974 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
975 walk_expr(self, e);
976 },
977 }
978 }
979
980 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
981 p.each_binding_or_first(&mut |_, id, _, _| {
982 self.locals.insert(id);
983 });
984 }
985 }
986
987 let mut v = V {
988 cx,
989 loops: Vec::new(),
990 locals: HirIdSet::default(),
991 allow_closure: true,
992 captures: HirIdMap::default(),
993 };
994 v.visit_expr(expr);
995 v.allow_closure.then_some(v.captures)
996}
997
998pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1000
1001pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1004 let mut method_names = Vec::with_capacity(max_depth);
1005 let mut arg_lists = Vec::with_capacity(max_depth);
1006 let mut spans = Vec::with_capacity(max_depth);
1007
1008 let mut current = expr;
1009 for _ in 0..max_depth {
1010 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1011 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1012 break;
1013 }
1014 method_names.push(path.ident.name);
1015 arg_lists.push((*receiver, &**args));
1016 spans.push(path.ident.span);
1017 current = receiver;
1018 } else {
1019 break;
1020 }
1021 }
1022
1023 (method_names, arg_lists, spans)
1024}
1025
1026pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1033 let mut current = expr;
1034 let mut matched = Vec::with_capacity(methods.len());
1035 for method_name in methods.iter().rev() {
1036 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1038 if path.ident.name == *method_name {
1039 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1040 return None;
1041 }
1042 matched.push((receiver, args)); current = receiver; } else {
1045 return None;
1046 }
1047 } else {
1048 return None;
1049 }
1050 }
1051 matched.reverse();
1053 Some(matched)
1054}
1055
1056pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1058 cx.tcx
1059 .entry_fn(())
1060 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1061}
1062
1063pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1065 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1066 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1067}
1068
1069pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1071 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1072 match cx.tcx.hir_node_by_def_id(parent_id) {
1073 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1074 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1075 _ => None,
1076 }
1077}
1078
1079pub struct ContainsName<'a, 'tcx> {
1080 pub cx: &'a LateContext<'tcx>,
1081 pub name: Symbol,
1082}
1083
1084impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1085 type Result = ControlFlow<()>;
1086 type NestedFilter = nested_filter::OnlyBodies;
1087
1088 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1089 if self.name == name {
1090 ControlFlow::Break(())
1091 } else {
1092 ControlFlow::Continue(())
1093 }
1094 }
1095
1096 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1097 self.cx.tcx
1098 }
1099}
1100
1101pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1103 let mut cn = ContainsName { cx, name };
1104 cn.visit_expr(expr).is_break()
1105}
1106
1107pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1109 for_each_expr_without_closures(expr, |e| {
1110 if matches!(e.kind, ExprKind::Ret(..)) {
1111 ControlFlow::Break(())
1112 } else {
1113 ControlFlow::Continue(())
1114 }
1115 })
1116 .is_some()
1117}
1118
1119pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1121 get_parent_expr_for_hir(cx, e.hir_id)
1122}
1123
1124pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1127 match cx.tcx.parent_hir_node(hir_id) {
1128 Node::Expr(parent) => Some(parent),
1129 _ => None,
1130 }
1131}
1132
1133pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1135 let enclosing_node = cx
1136 .tcx
1137 .hir_get_enclosing_scope(hir_id)
1138 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1139 enclosing_node.and_then(|node| match node {
1140 Node::Block(block) => Some(block),
1141 Node::Item(&Item {
1142 kind: ItemKind::Fn { body: eid, .. },
1143 ..
1144 })
1145 | Node::ImplItem(&ImplItem {
1146 kind: ImplItemKind::Fn(_, eid),
1147 ..
1148 })
1149 | Node::TraitItem(&TraitItem {
1150 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1151 ..
1152 }) => match cx.tcx.hir_body(eid).value.kind {
1153 ExprKind::Block(block, _) => Some(block),
1154 _ => None,
1155 },
1156 _ => None,
1157 })
1158}
1159
1160pub fn get_enclosing_closure<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Closure<'tcx>> {
1162 cx.tcx.hir_parent_iter(hir_id).find_map(|(_, node)| {
1163 if let Node::Expr(expr) = node
1164 && let ExprKind::Closure(closure) = expr.kind
1165 {
1166 Some(closure)
1167 } else {
1168 None
1169 }
1170 })
1171}
1172
1173pub fn is_upvar_in_closure(cx: &LateContext<'_>, closure: &Closure<'_>, local_id: HirId) -> bool {
1175 cx.typeck_results()
1176 .closure_min_captures
1177 .get(&closure.def_id)
1178 .is_some_and(|x| x.contains_key(&local_id))
1179}
1180
1181pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1183 cx: &LateContext<'tcx>,
1184 expr: &Expr<'_>,
1185) -> Option<&'tcx Expr<'tcx>> {
1186 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1187 match node {
1188 Node::Expr(e) => match e.kind {
1189 ExprKind::Closure { .. }
1190 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1191 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1192
1193 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1195 _ => (),
1196 },
1197 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1198 _ => break,
1199 }
1200 }
1201 None
1202}
1203
1204pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1206 match tcx.hir_parent_iter(id).next() {
1207 Some((
1208 _,
1209 Node::Item(Item {
1210 kind: ItemKind::Impl(imp),
1211 ..
1212 }),
1213 )) => Some(imp),
1214 _ => None,
1215 }
1216}
1217
1218pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1229 while let ExprKind::Block(
1230 Block {
1231 stmts: [],
1232 expr: Some(inner),
1233 rules: BlockCheckMode::DefaultBlock,
1234 ..
1235 },
1236 _,
1237 ) = expr.kind
1238 {
1239 expr = inner;
1240 }
1241 expr
1242}
1243
1244pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1255 while let ExprKind::Block(
1256 Block {
1257 stmts: [],
1258 expr: Some(inner),
1259 rules: BlockCheckMode::DefaultBlock,
1260 ..
1261 }
1262 | Block {
1263 stmts:
1264 [
1265 Stmt {
1266 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1267 ..
1268 },
1269 ],
1270 expr: None,
1271 rules: BlockCheckMode::DefaultBlock,
1272 ..
1273 },
1274 _,
1275 ) = expr.kind
1276 {
1277 expr = inner;
1278 }
1279 expr
1280}
1281
1282pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1284 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1285 match iter.next() {
1286 Some((
1287 _,
1288 Node::Expr(Expr {
1289 kind: ExprKind::If(_, _, Some(else_expr)),
1290 ..
1291 }),
1292 )) => else_expr.hir_id == expr.hir_id,
1293 _ => false,
1294 }
1295}
1296
1297pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1300 let mut child_id = expr.hir_id;
1301 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1302 if let Node::LetStmt(LetStmt {
1303 init: Some(init),
1304 els: Some(els),
1305 ..
1306 }) = node
1307 && (init.hir_id == child_id || els.hir_id == child_id)
1308 {
1309 return true;
1310 }
1311
1312 child_id = parent_id;
1313 }
1314
1315 false
1316}
1317
1318pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1320 let mut child_id = expr.hir_id;
1321 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1322 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1323 && els.hir_id == child_id
1324 {
1325 return true;
1326 }
1327
1328 child_id = parent_id;
1329 }
1330
1331 false
1332}
1333
1334pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1349 let ty = cx.typeck_results().expr_ty(expr);
1350 if let Some(Range { start, end, limits, .. }) = Range::hir(cx, expr) {
1351 let start_is_none_or_min = start.is_none_or(|start| {
1352 if let rustc_ty::Adt(_, subst) = ty.kind()
1353 && let bnd_ty = subst.type_at(0)
1354 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1355 {
1356 start_const.is_numeric_min(cx.tcx, bnd_ty)
1357 } else {
1358 false
1359 }
1360 });
1361 let end_is_none_or_max = end.is_none_or(|end| match limits {
1362 RangeLimits::Closed => {
1363 if let rustc_ty::Adt(_, subst) = ty.kind()
1364 && let bnd_ty = subst.type_at(0)
1365 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1366 {
1367 end_const.is_numeric_max(cx.tcx, bnd_ty)
1368 } else {
1369 false
1370 }
1371 },
1372 RangeLimits::HalfOpen => {
1373 if let Some(container_path) = container_path
1374 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1375 && name.ident.name == sym::len
1376 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1377 {
1378 container_path.res == path.res
1379 } else {
1380 false
1381 }
1382 },
1383 });
1384 return start_is_none_or_min && end_is_none_or_max;
1385 }
1386 false
1387}
1388
1389pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1392 if is_integer_literal(e, value) {
1393 return true;
1394 }
1395 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1396 if let Some(Constant::Int(v)) =
1397 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1398 {
1399 return value == v;
1400 }
1401 false
1402}
1403
1404pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1406 if let ExprKind::Lit(spanned) = expr.kind
1408 && let LitKind::Int(v, _) = spanned.node
1409 {
1410 return v == value;
1411 }
1412 false
1413}
1414
1415pub fn is_integer_literal_untyped(expr: &Expr<'_>) -> bool {
1417 if let ExprKind::Lit(spanned) = expr.kind
1418 && let LitKind::Int(_, suffix) = spanned.node
1419 {
1420 return suffix == LitIntType::Unsuffixed;
1421 }
1422
1423 false
1424}
1425
1426pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1428 if let ExprKind::Lit(spanned) = expr.kind
1429 && let LitKind::Float(v, _) = spanned.node
1430 {
1431 v.as_str().parse() == Ok(value)
1432 } else {
1433 false
1434 }
1435}
1436
1437pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1445 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1446}
1447
1448#[must_use]
1452pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1453 loop {
1454 if span.from_expansion() {
1455 let data = span.ctxt().outer_expn_data();
1456 let new_span = data.call_site;
1457
1458 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1459 && mac_name == name
1460 {
1461 return Some(new_span);
1462 }
1463
1464 span = new_span;
1465 } else {
1466 return None;
1467 }
1468 }
1469}
1470
1471#[must_use]
1482pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1483 if span.from_expansion() {
1484 let data = span.ctxt().outer_expn_data();
1485 let new_span = data.call_site;
1486
1487 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1488 && mac_name == name
1489 {
1490 return Some(new_span);
1491 }
1492 }
1493
1494 None
1495}
1496
1497pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1499 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1500 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1501}
1502
1503pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1505 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1506 cx.tcx.instantiate_bound_regions_with_erased(arg)
1507}
1508
1509pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1511 if let ExprKind::Call(fun, _) = expr.kind
1512 && let ExprKind::Path(ref qp) = fun.kind
1513 {
1514 let res = cx.qpath_res(qp, fun.hir_id);
1515 return match res {
1516 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1517 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1518 _ => false,
1519 };
1520 }
1521 false
1522}
1523
1524pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1527 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1528 !matches!(
1529 cx.qpath_res(qpath, id),
1530 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1531 )
1532 }
1533
1534 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1535 i.into_iter().any(|pat| is_refutable(cx, pat))
1536 }
1537
1538 match pat.kind {
1539 PatKind::Missing => unreachable!(),
1540 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1542 PatKind::Box(pat) | PatKind::Ref(pat, _, _) => is_refutable(cx, pat),
1543 PatKind::Expr(PatExpr {
1544 kind: PatExprKind::Path(qpath),
1545 hir_id,
1546 ..
1547 }) => is_qpath_refutable(cx, qpath, *hir_id),
1548 PatKind::Or(pats) => {
1549 are_refutable(cx, pats)
1551 },
1552 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1553 PatKind::Struct(ref qpath, fields, _) => {
1554 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1555 },
1556 PatKind::TupleStruct(ref qpath, pats, _) => {
1557 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1558 },
1559 PatKind::Slice(head, middle, tail) => {
1560 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1561 rustc_ty::Slice(..) => {
1562 !head.is_empty() || middle.is_none() || !tail.is_empty()
1564 },
1565 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1566 _ => {
1567 true
1569 },
1570 }
1571 },
1572 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1573 }
1574}
1575
1576pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1579 if let PatKind::Or(pats) = pat.kind {
1580 pats.iter().for_each(f);
1581 } else {
1582 f(pat);
1583 }
1584}
1585
1586pub fn is_self(slf: &Param<'_>) -> bool {
1587 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1588 name.name == kw::SelfLower
1589 } else {
1590 false
1591 }
1592}
1593
1594pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1595 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1596 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1597 {
1598 return true;
1599 }
1600 false
1601}
1602
1603pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1604 (0..decl.inputs.len()).map(move |i| &body.params[i])
1605}
1606
1607pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1610 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1611 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1612 && ddpos.as_opt_usize().is_none()
1613 && cx
1614 .qpath_res(path, arm.pat.hir_id)
1615 .ctor_parent(cx)
1616 .is_lang_item(cx, ResultOk)
1617 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1618 && arm.body.res_local_id() == Some(hir_id)
1619 {
1620 return true;
1621 }
1622 false
1623 }
1624
1625 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1626 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1627 cx.qpath_res(path, arm.pat.hir_id)
1628 .ctor_parent(cx)
1629 .is_lang_item(cx, ResultErr)
1630 } else {
1631 false
1632 }
1633 }
1634
1635 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1636 if let MatchSource::TryDesugar(_) = *source {
1638 return Some(expr);
1639 }
1640
1641 if arms.len() == 2
1642 && arms[0].guard.is_none()
1643 && arms[1].guard.is_none()
1644 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1645 {
1646 return Some(expr);
1647 }
1648 }
1649
1650 None
1651}
1652
1653pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1663 let mut suppress_lint = false;
1664
1665 for id in ids {
1666 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1667 if let Some(expectation) = lint_id {
1668 cx.fulfill_expectation(expectation);
1669 }
1670
1671 match level {
1672 Level::Allow | Level::Expect => suppress_lint = true,
1673 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1674 }
1675 }
1676
1677 suppress_lint
1678}
1679
1680pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1688 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1689}
1690
1691pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1692 while let PatKind::Ref(subpat, _, _) = pat.kind {
1693 pat = subpat;
1694 }
1695 pat
1696}
1697
1698pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1699 Integer::from_int_ty(&tcx, ity).size().bits()
1700}
1701
1702#[expect(clippy::cast_possible_wrap)]
1703pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1705 let amt = 128 - int_bits(tcx, ity);
1706 ((u as i128) << amt) >> amt
1707}
1708
1709#[expect(clippy::cast_sign_loss)]
1710pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1712 let amt = 128 - int_bits(tcx, ity);
1713 ((u as u128) << amt) >> amt
1714}
1715
1716pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1718 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1719 let amt = 128 - bits;
1720 (u << amt) >> amt
1721}
1722
1723pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1724 attrs.iter().any(|attr| attr.has_name(symbol))
1725}
1726
1727pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1728 find_attr!(cx.tcx, hir_id, Repr { .. })
1729}
1730
1731pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1732 let mut prev_enclosing_node = None;
1733 let mut enclosing_node = node;
1734 while Some(enclosing_node) != prev_enclosing_node {
1735 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1736 return true;
1737 }
1738 prev_enclosing_node = Some(enclosing_node);
1739 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1740 }
1741
1742 false
1743}
1744
1745pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1748 tcx.hir_parent_owner_iter(id)
1749 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1750 .any(|(id, _)| {
1751 find_attr!(
1752 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1753 AutomaticallyDerived(..)
1754 )
1755 })
1756}
1757
1758pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1760 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1763}
1764
1765pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1770 let mut conds = Vec::new();
1771 let mut blocks: Vec<&Block<'_>> = Vec::new();
1772
1773 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1774 conds.push(cond);
1775 if let ExprKind::Block(block, _) = then.kind {
1776 blocks.push(block);
1777 } else {
1778 panic!("ExprKind::If node is not an ExprKind::Block");
1779 }
1780
1781 if let Some(else_expr) = r#else {
1782 expr = else_expr;
1783 } else {
1784 break;
1785 }
1786 }
1787
1788 if !blocks.is_empty()
1790 && let ExprKind::Block(block, _) = expr.kind
1791 {
1792 blocks.push(block);
1793 }
1794
1795 (conds, blocks)
1796}
1797
1798pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1800 if let ExprKind::Closure(&Closure {
1801 body,
1802 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1803 ..
1804 }) = expr.kind
1805 && let ExprKind::Block(
1806 Block {
1807 expr:
1808 Some(Expr {
1809 kind: ExprKind::DropTemps(inner_expr),
1810 ..
1811 }),
1812 ..
1813 },
1814 _,
1815 ) = tcx.hir_body(body).value.kind
1816 {
1817 Some(inner_expr)
1818 } else {
1819 None
1820 }
1821}
1822
1823pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1825 get_async_closure_expr(tcx, body.value)
1826}
1827
1828pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1830 let did = match expr.kind {
1831 ExprKind::Call(path, _) => {
1832 if let ExprKind::Path(ref qpath) = path.kind
1833 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1834 {
1835 Some(did)
1836 } else {
1837 None
1838 }
1839 },
1840 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1841 _ => None,
1842 };
1843
1844 did.is_some_and(|did| find_attr!(cx.tcx, did, MustUse { .. }))
1845}
1846
1847fn is_body_identity_function<'hir>(cx: &LateContext<'_>, func: &Body<'hir>) -> bool {
1861 let [param] = func.params else {
1862 return false;
1863 };
1864
1865 let mut param_pat = param.pat;
1866
1867 let mut advance_param_pat_over_stmts = |stmts: &[Stmt<'hir>]| {
1874 for stmt in stmts {
1875 if let StmtKind::Let(local) = stmt.kind
1876 && let Some(init) = local.init
1877 && is_expr_identity_of_pat(cx, param_pat, init, true)
1878 {
1879 param_pat = local.pat;
1880 } else {
1881 return false;
1882 }
1883 }
1884
1885 true
1886 };
1887
1888 let mut expr = func.value;
1889 loop {
1890 match expr.kind {
1891 ExprKind::Block(
1892 &Block {
1893 stmts: [],
1894 expr: Some(e),
1895 ..
1896 },
1897 _,
1898 )
1899 | ExprKind::Ret(Some(e)) => expr = e,
1900 ExprKind::Block(
1901 &Block {
1902 stmts: [stmt],
1903 expr: None,
1904 ..
1905 },
1906 _,
1907 ) => {
1908 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1909 && let ExprKind::Ret(Some(ret_val)) = e.kind
1910 {
1911 expr = ret_val;
1912 } else {
1913 return false;
1914 }
1915 },
1916 ExprKind::Block(
1917 &Block {
1918 stmts, expr: Some(e), ..
1919 },
1920 _,
1921 ) => {
1922 if !advance_param_pat_over_stmts(stmts) {
1923 return false;
1924 }
1925
1926 expr = e;
1927 },
1928 ExprKind::Block(&Block { stmts, expr: None, .. }, _) => {
1929 if let Some((last_stmt, stmts)) = stmts.split_last()
1930 && advance_param_pat_over_stmts(stmts)
1931 && let StmtKind::Semi(e) | StmtKind::Expr(e) = last_stmt.kind
1932 && let ExprKind::Ret(Some(ret_val)) = e.kind
1933 {
1934 expr = ret_val;
1935 } else {
1936 return false;
1937 }
1938 },
1939 _ => return is_expr_identity_of_pat(cx, param_pat, expr, true),
1940 }
1941 }
1942}
1943
1944pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1954 if cx
1955 .typeck_results()
1956 .pat_binding_modes()
1957 .get(pat.hir_id)
1958 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(..)))
1959 {
1960 return false;
1964 }
1965
1966 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1968
1969 match (pat.kind, expr.kind) {
1970 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1971 expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1972 },
1973 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1974 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1975 },
1976 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1977 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1978 {
1979 over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1980 },
1981 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1982 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1983 },
1984 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1985 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1986 {
1987 if let ExprKind::Path(ident) = &ident.kind
1989 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1990 && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
1992 {
1993 true
1994 } else {
1995 false
1996 }
1997 },
1998 (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
1999 if field_pats.len() == fields.len() =>
2000 {
2001 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
2003 && unordered_over(field_pats, fields, |field_pat, field| {
2005 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
2006 })
2007 },
2008 _ => false,
2009 }
2010}
2011
2012pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2017 match expr.kind {
2018 ExprKind::Closure(&Closure { body, fn_decl, .. })
2019 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
2020 {
2021 is_body_identity_function(cx, cx.tcx.hir_body(body))
2022 },
2023 ExprKind::Path(QPath::Resolved(_, path))
2024 if path.segments.iter().all(|seg| seg.infer_args)
2025 && let Some(did) = path.res.opt_def_id() =>
2026 {
2027 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
2028 },
2029 _ => false,
2030 }
2031}
2032
2033pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2042 match expr.kind {
2043 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
2044 _ => expr.basic_res().is_diag_item(cx, sym::convert_identity),
2045 }
2046}
2047
2048pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2051 let mut child_id = expr.hir_id;
2052 let mut iter = tcx.hir_parent_iter(child_id);
2053 loop {
2054 match iter.next() {
2055 None => break None,
2056 Some((id, Node::Block(_))) => child_id = id,
2057 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2058 Some((_, Node::Expr(expr))) => match expr.kind {
2059 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2060 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2061 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2062 _ => break Some((Node::Expr(expr), child_id)),
2063 },
2064 Some((_, node)) => break Some((node, child_id)),
2065 }
2066 }
2067}
2068
2069pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2071 !matches!(
2072 get_expr_use_or_unification_node(tcx, expr),
2073 None | Some((
2074 Node::Stmt(Stmt {
2075 kind: StmtKind::Expr(_)
2076 | StmtKind::Semi(_)
2077 | StmtKind::Let(LetStmt {
2078 pat: Pat {
2079 kind: PatKind::Wild,
2080 ..
2081 },
2082 ..
2083 }),
2084 ..
2085 }),
2086 _
2087 ))
2088 )
2089}
2090
2091pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2093 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2094}
2095
2096pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2100 !expr.is_place_expr(|base| {
2101 cx.typeck_results()
2102 .adjustments()
2103 .get(base.hir_id)
2104 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2105 })
2106}
2107
2108pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2109 if is_no_core_crate(cx) {
2110 None
2111 } else if is_no_std_crate(cx) {
2112 Some("core")
2113 } else {
2114 Some("std")
2115 }
2116}
2117
2118pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2119 find_attr!(cx.tcx, crate, NoStd(..))
2120}
2121
2122pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2123 find_attr!(cx.tcx, crate, NoCore(..))
2124}
2125
2126pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2136 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2137 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2138 } else {
2139 false
2140 }
2141}
2142
2143pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2153 use rustc_trait_selection::traits;
2154 let predicates = cx
2155 .tcx
2156 .predicates_of(did)
2157 .predicates
2158 .iter()
2159 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2160 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2161}
2162
2163pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2165 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2166}
2167
2168pub fn fn_def_id_with_node_args<'tcx>(
2171 cx: &LateContext<'tcx>,
2172 expr: &Expr<'_>,
2173) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2174 let typeck = cx.typeck_results();
2175 match &expr.kind {
2176 ExprKind::MethodCall(..) => Some((
2177 typeck.type_dependent_def_id(expr.hir_id)?,
2178 typeck.node_args(expr.hir_id),
2179 )),
2180 ExprKind::Call(
2181 Expr {
2182 kind: ExprKind::Path(qpath),
2183 hir_id: path_hir_id,
2184 ..
2185 },
2186 ..,
2187 ) => {
2188 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2191 typeck.qpath_res(qpath, *path_hir_id)
2192 {
2193 Some((id, typeck.node_args(*path_hir_id)))
2194 } else {
2195 None
2196 }
2197 },
2198 _ => None,
2199 }
2200}
2201
2202pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2207 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2208 let expr_kind = expr_type.kind();
2209 let is_primitive = match expr_kind {
2210 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2211 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2212 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2213 is_recursively_primitive_type(*element_type)
2214 } else {
2215 unreachable!()
2216 }
2217 },
2218 _ => false,
2219 };
2220
2221 if is_primitive {
2222 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2225 rustc_ty::Slice(..) => return Some("slice".into()),
2226 rustc_ty::Array(..) => return Some("array".into()),
2227 rustc_ty::Tuple(..) => return Some("tuple".into()),
2228 _ => {
2229 let refs_peeled = expr_type.peel_refs();
2232 return Some(refs_peeled.walk().last().unwrap().to_string());
2233 },
2234 }
2235 }
2236 None
2237}
2238
2239pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2247where
2248 Hash: FnMut(&T) -> u64,
2249 Eq: FnMut(&T, &T) -> bool,
2250{
2251 match exprs {
2252 [a, b] if eq(a, b) => return vec![vec![a, b]],
2253 _ if exprs.len() <= 2 => return vec![],
2254 _ => {},
2255 }
2256
2257 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2258
2259 for expr in exprs {
2260 match buckets.entry(hash(expr)) {
2261 indexmap::map::Entry::Occupied(mut o) => {
2262 let bucket = o.get_mut();
2263 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2264 Some(group) => group.push(expr),
2265 None => bucket.push(vec![expr]),
2266 }
2267 },
2268 indexmap::map::Entry::Vacant(v) => {
2269 v.insert(vec![vec![expr]]);
2270 },
2271 }
2272 }
2273
2274 buckets
2275 .into_values()
2276 .flatten()
2277 .filter(|group| group.len() > 1)
2278 .collect()
2279}
2280
2281pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2284 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2285 if let PatKind::Ref(pat, _, _) = pat.kind {
2286 peel(pat, count + 1)
2287 } else {
2288 (pat, count)
2289 }
2290 }
2291 peel(pat, 0)
2292}
2293
2294pub fn peel_hir_expr_while<'tcx>(
2296 mut expr: &'tcx Expr<'tcx>,
2297 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2298) -> &'tcx Expr<'tcx> {
2299 while let Some(e) = f(expr) {
2300 expr = e;
2301 }
2302 expr
2303}
2304
2305pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2308 let mut remaining = count;
2309 let e = peel_hir_expr_while(expr, |e| match e.kind {
2310 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2311 remaining -= 1;
2312 Some(e)
2313 },
2314 _ => None,
2315 });
2316 (e, count - remaining)
2317}
2318
2319pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2322 let mut count: usize = 0;
2323 let mut curr_expr = expr;
2324 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2325 count = count.wrapping_add(1);
2326 curr_expr = local_expr;
2327 }
2328 (curr_expr, count)
2329}
2330
2331pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2334 let mut count = 0;
2335 let e = peel_hir_expr_while(expr, |e| match e.kind {
2336 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2337 count += 1;
2338 Some(e)
2339 },
2340 _ => None,
2341 });
2342 (e, count)
2343}
2344
2345pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2348 let mut count = 0;
2349 loop {
2350 match &ty.kind {
2351 TyKind::Ref(_, ref_ty) => {
2352 ty = ref_ty.ty;
2353 count += 1;
2354 },
2355 _ => break (ty, count),
2356 }
2357 }
2358}
2359
2360pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
2362 match &ty.kind {
2363 TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty),
2364 _ => ty,
2365 }
2366}
2367
2368pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2371 loop {
2372 match expr.kind {
2373 ExprKind::AddrOf(_, _, e) => expr = e,
2374 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2375 _ => break,
2376 }
2377 }
2378 expr
2379}
2380
2381pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
2384 let mut operators = Vec::new();
2385 peel_hir_expr_while(expr, |expr| match expr.kind {
2386 ExprKind::AddrOf(_, _, e) => {
2387 operators.push(expr);
2388 Some(e)
2389 },
2390 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
2391 operators.push(expr);
2392 Some(e)
2393 },
2394 _ => None,
2395 });
2396 operators
2397}
2398
2399pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2400 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2401 && let Res::Def(_, def_id) = path.res
2402 {
2403 #[allow(deprecated)]
2404 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2405 }
2406 false
2407}
2408
2409static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2410
2411fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2414 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2415 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2416 let value = map.entry(module);
2417 match value {
2418 Entry::Occupied(entry) => f(entry.get()),
2419 Entry::Vacant(entry) => {
2420 let mut names = Vec::new();
2421 for id in tcx.hir_module_free_items(module) {
2422 if matches!(tcx.def_kind(id.owner_id), DefKind::Const { .. })
2423 && let item = tcx.hir_item(id)
2424 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2425 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2426 && let Res::Def(DefKind::Struct, _) = path.res
2428 && find_attr!(tcx, item.hir_id(), RustcTestMarker(..))
2429 {
2430 names.push(ident.name);
2431 }
2432 }
2433 names.sort_unstable();
2434 f(entry.insert(names))
2435 },
2436 }
2437}
2438
2439pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2443 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2444 let node = tcx.hir_node(id);
2445 once((id, node))
2446 .chain(tcx.hir_parent_iter(id))
2447 .any(|(_id, node)| {
2450 if let Node::Item(item) = node
2451 && let ItemKind::Fn { ident, .. } = item.kind
2452 {
2453 return names.binary_search(&ident.name).is_ok();
2456 }
2457 false
2458 })
2459 })
2460}
2461
2462pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2469 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2470 if let Node::Item(item) = tcx.hir_node(id)
2471 && let ItemKind::Fn { ident, .. } = item.kind
2472 {
2473 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2474 names.binary_search(&ident.name).is_ok()
2475 })
2476 } else {
2477 false
2478 }
2479}
2480
2481pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2486 if let Some(cfgs) = find_attr!(tcx, id, CfgTrace(cfgs) => cfgs)
2487 && cfgs
2488 .iter()
2489 .any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. }))
2490 {
2491 true
2492 } else {
2493 false
2494 }
2495}
2496
2497pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2499 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2500}
2501
2502pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2504 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2505}
2506
2507pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2509 find_attr!(tcx, def_id, CfgTrace(..))
2510 || find_attr!(
2511 tcx.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2512 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)),
2513 CfgTrace(..)
2514 )
2515}
2516
2517pub fn walk_to_expr_usage<'tcx, T>(
2528 cx: &LateContext<'tcx>,
2529 e: &Expr<'tcx>,
2530 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2531) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2532 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2533 let mut child_id = e.hir_id;
2534
2535 while let Some((parent_id, parent)) = iter.next() {
2536 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2537 return Some(ControlFlow::Break(x));
2538 }
2539 let parent_expr = match parent {
2540 Node::Expr(e) => e,
2541 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2542 child_id = parent_id;
2543 continue;
2544 },
2545 Node::Arm(a) if a.body.hir_id == child_id => {
2546 child_id = parent_id;
2547 continue;
2548 },
2549 _ => return Some(ControlFlow::Continue((parent, child_id))),
2550 };
2551 match parent_expr.kind {
2552 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2553 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2554 child_id = id;
2555 iter = cx.tcx.hir_parent_iter(id);
2556 },
2557 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2558 _ => return Some(ControlFlow::Continue((parent, child_id))),
2559 }
2560 }
2561 debug_assert!(false, "no parent node found for `{child_id:?}`");
2562 None
2563}
2564
2565#[derive(Clone, Copy)]
2567pub enum DefinedTy<'tcx> {
2568 Hir(&'tcx hir::Ty<'tcx>),
2570 Mir {
2578 def_site_def_id: Option<DefId>,
2579 ty: Binder<'tcx, Ty<'tcx>>,
2580 },
2581}
2582
2583pub struct ExprUseCtxt<'tcx> {
2585 pub node: Node<'tcx>,
2587 pub child_id: HirId,
2589 pub adjustments: &'tcx [Adjustment<'tcx>],
2591 pub is_ty_unified: bool,
2593 pub moved_before_use: bool,
2595 pub same_ctxt: bool,
2597}
2598impl<'tcx> ExprUseCtxt<'tcx> {
2599 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2600 match self.node {
2601 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2602 Node::ExprField(field) => ExprUseNode::Field(field),
2603
2604 Node::Item(&Item {
2605 kind: ItemKind::Static(..) | ItemKind::Const(..),
2606 owner_id,
2607 ..
2608 })
2609 | Node::TraitItem(&TraitItem {
2610 kind: TraitItemKind::Const(..),
2611 owner_id,
2612 ..
2613 })
2614 | Node::ImplItem(&ImplItem {
2615 kind: ImplItemKind::Const(..),
2616 owner_id,
2617 ..
2618 }) => ExprUseNode::ConstStatic(owner_id),
2619
2620 Node::Item(&Item {
2621 kind: ItemKind::Fn { .. },
2622 owner_id,
2623 ..
2624 })
2625 | Node::TraitItem(&TraitItem {
2626 kind: TraitItemKind::Fn(..),
2627 owner_id,
2628 ..
2629 })
2630 | Node::ImplItem(&ImplItem {
2631 kind: ImplItemKind::Fn(..),
2632 owner_id,
2633 ..
2634 }) => ExprUseNode::Return(owner_id),
2635
2636 Node::Expr(use_expr) => match use_expr.kind {
2637 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2638 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2639 }),
2640
2641 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2642 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2643 Some(i) => ExprUseNode::FnArg(func, i),
2644 None => ExprUseNode::Callee,
2645 },
2646 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2647 use_expr.hir_id,
2648 name.args,
2649 args.iter()
2650 .position(|arg| arg.hir_id == self.child_id)
2651 .map_or(0, |i| i + 1),
2652 ),
2653 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2654 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2655 _ => ExprUseNode::Other,
2656 },
2657 _ => ExprUseNode::Other,
2658 }
2659 }
2660}
2661
2662pub enum ExprUseNode<'tcx> {
2664 LetStmt(&'tcx LetStmt<'tcx>),
2666 ConstStatic(OwnerId),
2668 Return(OwnerId),
2670 Field(&'tcx ExprField<'tcx>),
2672 FnArg(&'tcx Expr<'tcx>, usize),
2674 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2676 Callee,
2678 FieldAccess(Ident),
2680 AddrOf(ast::BorrowKind, Mutability),
2682 Other,
2683}
2684impl<'tcx> ExprUseNode<'tcx> {
2685 pub fn is_return(&self) -> bool {
2687 matches!(self, Self::Return(_))
2688 }
2689
2690 pub fn is_recv(&self) -> bool {
2692 matches!(self, Self::MethodArg(_, _, 0))
2693 }
2694
2695 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2697 match *self {
2698 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2699 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2700 def_site_def_id: Some(id.def_id.to_def_id()),
2701 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2702 }),
2703 Self::Return(id) => {
2704 if let Node::Expr(Expr {
2705 kind: ExprKind::Closure(c),
2706 ..
2707 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2708 {
2709 match c.fn_decl.output {
2710 FnRetTy::DefaultReturn(_) => None,
2711 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2712 }
2713 } else {
2714 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2715 Some(DefinedTy::Mir {
2716 def_site_def_id: Some(id.def_id.to_def_id()),
2717 ty,
2718 })
2719 }
2720 },
2721 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2722 Some(Expr {
2723 hir_id,
2724 kind: ExprKind::Struct(path, ..),
2725 ..
2726 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2727 .and_then(|(adt, variant)| {
2728 variant
2729 .fields
2730 .iter()
2731 .find(|f| f.name == field.ident.name)
2732 .map(|f| (adt, f))
2733 })
2734 .map(|(adt, field_def)| DefinedTy::Mir {
2735 def_site_def_id: Some(adt.did()),
2736 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2737 }),
2738 _ => None,
2739 },
2740 Self::FnArg(callee, i) => {
2741 let sig = expr_sig(cx, callee)?;
2742 let (hir_ty, ty) = sig.input_with_hir(i)?;
2743 Some(match hir_ty {
2744 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2745 None => DefinedTy::Mir {
2746 def_site_def_id: sig.predicates_id(),
2747 ty,
2748 },
2749 })
2750 },
2751 Self::MethodArg(id, _, i) => {
2752 let id = cx.typeck_results().type_dependent_def_id(id)?;
2753 let sig = cx.tcx.fn_sig(id).skip_binder();
2754 Some(DefinedTy::Mir {
2755 def_site_def_id: Some(id),
2756 ty: sig.input(i),
2757 })
2758 },
2759 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2760 }
2761 }
2762}
2763
2764pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2766 let mut adjustments = [].as_slice();
2767 let mut is_ty_unified = false;
2768 let mut moved_before_use = false;
2769 let mut same_ctxt = true;
2770 let ctxt = e.span.ctxt();
2771 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2772 if adjustments.is_empty()
2773 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2774 {
2775 adjustments = cx.typeck_results().expr_adjustments(e);
2776 }
2777 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2778 if let Node::Expr(e) = parent {
2779 match e.kind {
2780 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2781 is_ty_unified = true;
2782 moved_before_use = true;
2783 },
2784 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2785 is_ty_unified = true;
2786 moved_before_use = true;
2787 },
2788 ExprKind::Block(..) => moved_before_use = true,
2789 _ => {},
2790 }
2791 }
2792 ControlFlow::Continue(())
2793 });
2794 match node {
2795 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2796 node,
2797 child_id,
2798 adjustments,
2799 is_ty_unified,
2800 moved_before_use,
2801 same_ctxt,
2802 },
2803 None => ExprUseCtxt {
2804 node: Node::Crate(cx.tcx.hir_root_module()),
2805 child_id: HirId::INVALID,
2806 adjustments: &[],
2807 is_ty_unified: true,
2808 moved_before_use: true,
2809 same_ctxt: false,
2810 },
2811 }
2812}
2813
2814pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2816 let mut pos = 0;
2817 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2818 let end = pos + t.len;
2819 let range = pos as usize..end as usize;
2820 let inner = InnerSpan::new(range.start, range.end);
2821 pos = end;
2822 (t.kind, s.get(range).unwrap_or_default(), inner)
2823 })
2824}
2825
2826pub fn span_contains_comment(cx: &impl source::HasSession, span: Span) -> bool {
2829 span.check_source_text(cx, |snippet| {
2830 tokenize(snippet, FrontmatterAllowed::No).any(|token| {
2831 matches!(
2832 token.kind,
2833 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2834 )
2835 })
2836 })
2837}
2838
2839pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2844 span.check_source_text(cx, |snippet| {
2845 tokenize_with_text(snippet).any(|(token, _, _)| match token {
2846 TokenKind::Whitespace => false,
2847 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2848 _ => true,
2849 })
2850 })
2851}
2852pub fn span_extract_comment(cx: &impl source::HasSession, span: Span) -> String {
2856 span_extract_comments(cx, span).join("\n")
2857}
2858
2859pub fn span_extract_comments(cx: &impl source::HasSession, span: Span) -> Vec<String> {
2863 span.with_source_text(cx, |snippet| {
2864 tokenize_with_text(snippet)
2865 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2866 .map(|(_, s, _)| s.to_string())
2867 .collect::<Vec<_>>()
2868 })
2869 .unwrap_or_default()
2870}
2871
2872pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2873 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2874}
2875
2876pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2901 cx: &LateContext<'_>,
2902 pat: &'a Pat<'hir>,
2903 else_body: &Expr<'_>,
2904) -> Option<&'a Pat<'hir>> {
2905 if let Some([inner_pat]) = as_some_pattern(cx, pat)
2906 && !is_refutable(cx, inner_pat)
2907 && let else_body = peel_blocks(else_body)
2908 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2909 && let ExprKind::Path(ret_path) = ret_val.kind
2910 && cx
2911 .qpath_res(&ret_path, ret_val.hir_id)
2912 .ctor_parent(cx)
2913 .is_lang_item(cx, OptionNone)
2914 {
2915 Some(inner_pat)
2916 } else {
2917 None
2918 }
2919}
2920
2921macro_rules! op_utils {
2922 ($($name:ident $assign:ident)*) => {
2923 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2925
2926 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2928
2929 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2931 match kind {
2932 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2933 _ => None,
2934 }
2935 }
2936 };
2937}
2938
2939op_utils! {
2940 Add AddAssign
2941 Sub SubAssign
2942 Mul MulAssign
2943 Div DivAssign
2944 Rem RemAssign
2945 BitXor BitXorAssign
2946 BitAnd BitAndAssign
2947 BitOr BitOrAssign
2948 Shl ShlAssign
2949 Shr ShrAssign
2950}
2951
2952pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2955 match *pat {
2956 PatKind::Wild => true,
2957 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2958 !visitors::is_local_used(cx, body, id)
2959 },
2960 _ => false,
2961 }
2962}
2963
2964#[derive(Clone, Copy)]
2965pub enum RequiresSemi {
2966 Yes,
2967 No,
2968}
2969impl RequiresSemi {
2970 pub fn requires_semi(self) -> bool {
2971 matches!(self, Self::Yes)
2972 }
2973}
2974
2975#[expect(clippy::too_many_lines)]
2978pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2979 struct BreakTarget {
2980 id: HirId,
2981 unused: bool,
2982 }
2983
2984 struct V<'cx, 'tcx> {
2985 cx: &'cx LateContext<'tcx>,
2986 break_targets: Vec<BreakTarget>,
2987 break_targets_for_result_ty: u32,
2988 in_final_expr: bool,
2989 requires_semi: bool,
2990 is_never: bool,
2991 }
2992
2993 impl V<'_, '_> {
2994 fn push_break_target(&mut self, id: HirId) {
2995 self.break_targets.push(BreakTarget { id, unused: true });
2996 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2997 }
2998 }
2999
3000 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3001 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3002 if self.is_never && self.break_targets.is_empty() {
3019 if self.in_final_expr && !self.requires_semi {
3020 match e.kind {
3023 ExprKind::DropTemps(e) => self.visit_expr(e),
3024 ExprKind::If(_, then, Some(else_)) => {
3025 self.visit_expr(then);
3026 self.visit_expr(else_);
3027 },
3028 ExprKind::Match(_, arms, _) => {
3029 for arm in arms {
3030 self.visit_expr(arm.body);
3031 }
3032 },
3033 ExprKind::Loop(b, ..) => {
3034 self.push_break_target(e.hir_id);
3035 self.in_final_expr = false;
3036 self.visit_block(b);
3037 self.break_targets.pop();
3038 },
3039 ExprKind::Block(b, _) => {
3040 if b.targeted_by_break {
3041 self.push_break_target(b.hir_id);
3042 self.visit_block(b);
3043 self.break_targets.pop();
3044 } else {
3045 self.visit_block(b);
3046 }
3047 },
3048 _ => {
3049 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3050 },
3051 }
3052 }
3053 return;
3054 }
3055 match e.kind {
3056 ExprKind::DropTemps(e) => self.visit_expr(e),
3057 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3058 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3059 self.in_final_expr = false;
3060 self.visit_expr(e);
3061 self.is_never = true;
3062 },
3063 ExprKind::Break(dest, e) => {
3064 if let Some(e) = e {
3065 self.in_final_expr = false;
3066 self.visit_expr(e);
3067 }
3068 if let Ok(id) = dest.target_id
3069 && let Some((i, target)) = self
3070 .break_targets
3071 .iter_mut()
3072 .enumerate()
3073 .find(|(_, target)| target.id == id)
3074 {
3075 target.unused &= self.is_never;
3076 if i < self.break_targets_for_result_ty as usize {
3077 self.requires_semi = true;
3078 }
3079 }
3080 self.is_never = true;
3081 },
3082 ExprKind::If(cond, then, else_) => {
3083 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3084 self.visit_expr(cond);
3085 self.in_final_expr = in_final_expr;
3086
3087 if self.is_never {
3088 self.visit_expr(then);
3089 if let Some(else_) = else_ {
3090 self.visit_expr(else_);
3091 }
3092 } else {
3093 self.visit_expr(then);
3094 let is_never = mem::replace(&mut self.is_never, false);
3095 if let Some(else_) = else_ {
3096 self.visit_expr(else_);
3097 self.is_never &= is_never;
3098 }
3099 }
3100 },
3101 ExprKind::Match(scrutinee, arms, _) => {
3102 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3103 self.visit_expr(scrutinee);
3104 self.in_final_expr = in_final_expr;
3105
3106 if self.is_never {
3107 for arm in arms {
3108 self.visit_arm(arm);
3109 }
3110 } else {
3111 let mut is_never = true;
3112 for arm in arms {
3113 self.is_never = false;
3114 if let Some(guard) = arm.guard {
3115 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3116 self.visit_expr(guard);
3117 self.in_final_expr = in_final_expr;
3118 self.is_never = false;
3120 }
3121 self.visit_expr(arm.body);
3122 is_never &= self.is_never;
3123 }
3124 self.is_never = is_never;
3125 }
3126 },
3127 ExprKind::Loop(b, _, _, _) => {
3128 self.push_break_target(e.hir_id);
3129 self.in_final_expr = false;
3130 self.visit_block(b);
3131 self.is_never = self.break_targets.pop().unwrap().unused;
3132 },
3133 ExprKind::Block(b, _) => {
3134 if b.targeted_by_break {
3135 self.push_break_target(b.hir_id);
3136 self.visit_block(b);
3137 self.is_never &= self.break_targets.pop().unwrap().unused;
3138 } else {
3139 self.visit_block(b);
3140 }
3141 },
3142 _ => {
3143 self.in_final_expr = false;
3144 walk_expr(self, e);
3145 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3146 },
3147 }
3148 }
3149
3150 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3151 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3152 for s in b.stmts {
3153 self.visit_stmt(s);
3154 }
3155 self.in_final_expr = in_final_expr;
3156 if let Some(e) = b.expr {
3157 self.visit_expr(e);
3158 }
3159 }
3160
3161 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3162 if let Some(e) = l.init {
3163 self.visit_expr(e);
3164 }
3165 if let Some(else_) = l.els {
3166 let is_never = self.is_never;
3167 self.visit_block(else_);
3168 self.is_never = is_never;
3169 }
3170 }
3171
3172 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3173 if let Some(guard) = arm.guard {
3174 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3175 self.visit_expr(guard);
3176 self.in_final_expr = in_final_expr;
3177 }
3178 self.visit_expr(arm.body);
3179 }
3180 }
3181
3182 if cx.typeck_results().expr_ty(e).is_never() {
3183 Some(RequiresSemi::No)
3184 } else if let ExprKind::Block(b, _) = e.kind
3185 && !b.targeted_by_break
3186 && b.expr.is_none()
3187 {
3188 None
3190 } else {
3191 let mut v = V {
3192 cx,
3193 break_targets: Vec::new(),
3194 break_targets_for_result_ty: 0,
3195 in_final_expr: true,
3196 requires_semi: false,
3197 is_never: false,
3198 };
3199 v.visit_expr(e);
3200 v.is_never
3201 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3202 RequiresSemi::Yes
3203 } else {
3204 RequiresSemi::No
3205 })
3206 }
3207}
3208
3209pub fn get_path_from_caller_to_method_type<'tcx>(
3215 tcx: TyCtxt<'tcx>,
3216 from: LocalDefId,
3217 method: DefId,
3218 args: GenericArgsRef<'tcx>,
3219) -> String {
3220 let assoc_item = tcx.associated_item(method);
3221 let def_id = assoc_item.container_id(tcx);
3222 match assoc_item.container {
3223 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3224 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3225 let ty = tcx.type_of(def_id).instantiate_identity();
3226 get_path_to_ty(tcx, from, ty, args)
3227 },
3228 }
3229}
3230
3231fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3232 match ty.kind() {
3233 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3234 rustc_ty::Array(..)
3236 | rustc_ty::Dynamic(..)
3237 | rustc_ty::Never
3238 | rustc_ty::RawPtr(_, _)
3239 | rustc_ty::Ref(..)
3240 | rustc_ty::Slice(_)
3241 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3242 _ => ty.to_string(),
3243 }
3244}
3245
3246fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3248 if callee.is_local() {
3250 let callee_path = tcx.def_path(callee);
3251 let caller_path = tcx.def_path(from.to_def_id());
3252 maybe_get_relative_path(&caller_path, &callee_path, 2)
3253 } else {
3254 tcx.def_path_str(callee)
3255 }
3256}
3257
3258fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3271 use itertools::EitherOrBoth::{Both, Left, Right};
3272
3273 let unique_parts = to
3275 .data
3276 .iter()
3277 .zip_longest(from.data.iter())
3278 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3279 .map(|el| match el {
3280 Both(l, r) => Both(l.data, r.data),
3281 Left(l) => Left(l.data),
3282 Right(r) => Right(r.data),
3283 });
3284
3285 let mut go_up_by = 0;
3287 let mut path = Vec::new();
3288 for el in unique_parts {
3289 match el {
3290 Both(l, r) => {
3291 if let DefPathData::TypeNs(sym) = l {
3301 path.push(sym);
3302 }
3303 if let DefPathData::TypeNs(_) = r {
3304 go_up_by += 1;
3305 }
3306 },
3307 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3312 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3317 _ => {},
3318 }
3319 }
3320
3321 if go_up_by > max_super {
3322 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3324 if let DefPathData::TypeNs(sym) = el.data {
3325 Some(sym)
3326 } else {
3327 None
3328 }
3329 })))
3330 } else if go_up_by == 0 && path.is_empty() {
3331 String::from("Self")
3332 } else {
3333 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3334 }
3335}
3336
3337pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3340 matches!(
3341 cx.tcx.parent_hir_node(id),
3342 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3343 )
3344}
3345
3346pub fn is_block_like(expr: &Expr<'_>) -> bool {
3349 matches!(
3350 expr.kind,
3351 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3352 )
3353}
3354
3355pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3357 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3358 match expr.kind {
3359 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3360 _ if is_block_like(expr) => is_operand,
3361 _ => false,
3362 }
3363 }
3364
3365 contains_block(expr, false)
3366}
3367
3368pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3370 if let Some(parent_expr) = get_parent_expr(cx, expr)
3371 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3372 && receiver.hir_id == expr.hir_id
3373 {
3374 return true;
3375 }
3376 false
3377}
3378
3379pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3382 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3383 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3384 && temporary_ty
3385 .walk()
3386 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3387 {
3388 ControlFlow::Break(())
3389 } else {
3390 ControlFlow::Continue(())
3391 }
3392 })
3393 .is_break()
3394}
3395
3396pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3407 let expr_ty_is_adjusted = cx
3408 .typeck_results()
3409 .expr_adjustments(expr)
3410 .iter()
3411 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3413 if expr_ty_is_adjusted {
3414 return true;
3415 }
3416
3417 match expr.kind {
3420 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3421 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3422
3423 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3424 return false;
3425 }
3426
3427 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3428 let mut args_with_ty_param = {
3429 fn_sig
3430 .inputs()
3431 .skip_binder()
3432 .iter()
3433 .skip(self_arg_count)
3434 .zip(args)
3435 .filter_map(|(arg_ty, arg)| {
3436 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3437 Some(arg)
3438 } else {
3439 None
3440 }
3441 })
3442 };
3443 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3444 },
3445 ExprKind::Struct(qpath, _, _) => {
3447 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3448 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3449 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3450 return true;
3452 };
3453 v_def
3454 .fields
3455 .iter()
3456 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3457 } else {
3458 false
3459 }
3460 },
3461 ExprKind::Block(
3463 &Block {
3464 expr: Some(ret_expr), ..
3465 },
3466 _,
3467 )
3468 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3469
3470 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3472 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3474 ExprKind::If(_, then, maybe_else) => {
3476 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3477 },
3478 ExprKind::Match(_, arms, _) => arms
3479 .iter()
3480 .map(|arm| arm.body)
3481 .any(|body| expr_requires_coercion(cx, body)),
3482 _ => false,
3483 }
3484}
3485
3486pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3489 if let Some(hir_id) = expr.res_local_id()
3490 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3491 {
3492 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3493 } else if let ExprKind::Path(p) = &expr.kind
3494 && let Some(mutability) = cx
3495 .qpath_res(p, expr.hir_id)
3496 .opt_def_id()
3497 .and_then(|id| cx.tcx.static_mutability(id))
3498 {
3499 mutability == Mutability::Mut
3500 } else if let ExprKind::Field(parent, _) = expr.kind {
3501 is_mutable(cx, parent)
3502 } else {
3503 true
3504 }
3505}
3506
3507pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3510 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3511 return hir_ty;
3512 };
3513 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3514 && let Some(segment) = path.segments.last()
3515 && segment.ident.name == sym::Option
3516 && let Res::Def(DefKind::Enum, def_id) = segment.res
3517 && def_id == option_def_id
3518 && let [GenericArg::Type(arg_ty)] = segment.args().args
3519 {
3520 hir_ty = arg_ty.as_unambig_ty();
3521 }
3522 hir_ty
3523}
3524
3525pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3528 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3529 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3530 && let ctxt = expr.span.ctxt()
3531 && for_each_expr_without_closures(into_future_arg, |e| {
3532 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3533 })
3534 .is_none()
3535 {
3536 Some(into_future_arg)
3537 } else {
3538 None
3539 }
3540}
3541
3542pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3544 if let ExprKind::Call(fn_expr, []) = &expr.kind
3545 && let ExprKind::Path(qpath) = &fn_expr.kind
3546 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3547 {
3548 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3549 } else {
3550 false
3551 }
3552}
3553
3554pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3571 let enclosing_body_owner = cx
3572 .tcx
3573 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3574 let mut prev_id = expr.hir_id;
3575 let mut skip_until_id = None;
3576 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3577 if hir_id == enclosing_body_owner {
3578 return true;
3579 }
3580 if let Some(id) = skip_until_id {
3581 prev_id = hir_id;
3582 if id == hir_id {
3583 skip_until_id = None;
3584 }
3585 continue;
3586 }
3587 match node {
3588 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3589 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3590 Node::Expr(expr) => match expr.kind {
3591 ExprKind::Ret(_) => return true,
3592 ExprKind::If(_, then, opt_else)
3593 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3594 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3595 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3596 ExprKind::Break(
3597 Destination {
3598 target_id: Ok(target_id),
3599 ..
3600 },
3601 _,
3602 ) => skip_until_id = Some(target_id),
3603 _ => break,
3604 },
3605 _ => break,
3606 }
3607 prev_id = hir_id;
3608 }
3609
3610 false
3613}
3614
3615pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3618 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3619 matches!(
3620 adj.kind,
3621 Adjust::Deref(DerefAdjustKind::Overloaded(_))
3622 | Adjust::Pointer(PointerCoercion::Unsize)
3623 | Adjust::NeverToAny
3624 )
3625 })
3626}
3627
3628pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3630 matches!(
3631 expr.kind,
3632 ExprKind::Closure(Closure {
3633 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3634 CoroutineDesugaring::Async,
3635 CoroutineSource::Block
3636 )),
3637 ..
3638 })
3639 )
3640}
3641
3642pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3644 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3645}