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 return find_attr!(cx.tcx, def_id, CfgTrace(..) | CfgAttrTrace);
2404 }
2405 false
2406}
2407
2408static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2409
2410fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2413 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2414 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2415 let value = map.entry(module);
2416 match value {
2417 Entry::Occupied(entry) => f(entry.get()),
2418 Entry::Vacant(entry) => {
2419 let mut names = Vec::new();
2420 for id in tcx.hir_module_free_items(module) {
2421 if matches!(tcx.def_kind(id.owner_id), DefKind::Const { .. })
2422 && let item = tcx.hir_item(id)
2423 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2424 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2425 && let Res::Def(DefKind::Struct, _) = path.res
2427 && find_attr!(tcx, item.hir_id(), RustcTestMarker(..))
2428 {
2429 names.push(ident.name);
2430 }
2431 }
2432 names.sort_unstable();
2433 f(entry.insert(names))
2434 },
2435 }
2436}
2437
2438pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2442 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2443 let node = tcx.hir_node(id);
2444 once((id, node))
2445 .chain(tcx.hir_parent_iter(id))
2446 .any(|(_id, node)| {
2449 if let Node::Item(item) = node
2450 && let ItemKind::Fn { ident, .. } = item.kind
2451 {
2452 return names.binary_search(&ident.name).is_ok();
2455 }
2456 false
2457 })
2458 })
2459}
2460
2461pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2468 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2469 if let Node::Item(item) = tcx.hir_node(id)
2470 && let ItemKind::Fn { ident, .. } = item.kind
2471 {
2472 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2473 names.binary_search(&ident.name).is_ok()
2474 })
2475 } else {
2476 false
2477 }
2478}
2479
2480pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2485 if let Some(cfgs) = find_attr!(tcx, id, CfgTrace(cfgs) => cfgs)
2486 && cfgs
2487 .iter()
2488 .any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. }))
2489 {
2490 true
2491 } else {
2492 false
2493 }
2494}
2495
2496pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2498 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2499}
2500
2501pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2503 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2504}
2505
2506pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2508 find_attr!(tcx, def_id, CfgTrace(..))
2509 || find_attr!(
2510 tcx.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2511 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)),
2512 CfgTrace(..)
2513 )
2514}
2515
2516pub fn walk_to_expr_usage<'tcx, T>(
2527 cx: &LateContext<'tcx>,
2528 e: &Expr<'tcx>,
2529 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2530) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2531 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2532 let mut child_id = e.hir_id;
2533
2534 while let Some((parent_id, parent)) = iter.next() {
2535 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2536 return Some(ControlFlow::Break(x));
2537 }
2538 let parent_expr = match parent {
2539 Node::Expr(e) => e,
2540 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2541 child_id = parent_id;
2542 continue;
2543 },
2544 Node::Arm(a) if a.body.hir_id == child_id => {
2545 child_id = parent_id;
2546 continue;
2547 },
2548 _ => return Some(ControlFlow::Continue((parent, child_id))),
2549 };
2550 match parent_expr.kind {
2551 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2552 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2553 child_id = id;
2554 iter = cx.tcx.hir_parent_iter(id);
2555 },
2556 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2557 _ => return Some(ControlFlow::Continue((parent, child_id))),
2558 }
2559 }
2560 debug_assert!(false, "no parent node found for `{child_id:?}`");
2561 None
2562}
2563
2564#[derive(Clone, Copy)]
2566pub enum DefinedTy<'tcx> {
2567 Hir(&'tcx hir::Ty<'tcx>),
2569 Mir {
2577 def_site_def_id: Option<DefId>,
2578 ty: Binder<'tcx, Ty<'tcx>>,
2579 },
2580}
2581
2582pub struct ExprUseCtxt<'tcx> {
2584 pub node: Node<'tcx>,
2586 pub child_id: HirId,
2588 pub adjustments: &'tcx [Adjustment<'tcx>],
2590 pub is_ty_unified: bool,
2592 pub moved_before_use: bool,
2594 pub same_ctxt: bool,
2596}
2597impl<'tcx> ExprUseCtxt<'tcx> {
2598 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2599 match self.node {
2600 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2601 Node::ExprField(field) => ExprUseNode::Field(field),
2602
2603 Node::Item(&Item {
2604 kind: ItemKind::Static(..) | ItemKind::Const(..),
2605 owner_id,
2606 ..
2607 })
2608 | Node::TraitItem(&TraitItem {
2609 kind: TraitItemKind::Const(..),
2610 owner_id,
2611 ..
2612 })
2613 | Node::ImplItem(&ImplItem {
2614 kind: ImplItemKind::Const(..),
2615 owner_id,
2616 ..
2617 }) => ExprUseNode::ConstStatic(owner_id),
2618
2619 Node::Item(&Item {
2620 kind: ItemKind::Fn { .. },
2621 owner_id,
2622 ..
2623 })
2624 | Node::TraitItem(&TraitItem {
2625 kind: TraitItemKind::Fn(..),
2626 owner_id,
2627 ..
2628 })
2629 | Node::ImplItem(&ImplItem {
2630 kind: ImplItemKind::Fn(..),
2631 owner_id,
2632 ..
2633 }) => ExprUseNode::Return(owner_id),
2634
2635 Node::Expr(use_expr) => match use_expr.kind {
2636 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2637 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2638 }),
2639
2640 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2641 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2642 Some(i) => ExprUseNode::FnArg(func, i),
2643 None => ExprUseNode::Callee,
2644 },
2645 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2646 use_expr.hir_id,
2647 name.args,
2648 args.iter()
2649 .position(|arg| arg.hir_id == self.child_id)
2650 .map_or(0, |i| i + 1),
2651 ),
2652 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2653 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2654 _ => ExprUseNode::Other,
2655 },
2656 _ => ExprUseNode::Other,
2657 }
2658 }
2659}
2660
2661pub enum ExprUseNode<'tcx> {
2663 LetStmt(&'tcx LetStmt<'tcx>),
2665 ConstStatic(OwnerId),
2667 Return(OwnerId),
2669 Field(&'tcx ExprField<'tcx>),
2671 FnArg(&'tcx Expr<'tcx>, usize),
2673 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2675 Callee,
2677 FieldAccess(Ident),
2679 AddrOf(ast::BorrowKind, Mutability),
2681 Other,
2682}
2683impl<'tcx> ExprUseNode<'tcx> {
2684 pub fn is_return(&self) -> bool {
2686 matches!(self, Self::Return(_))
2687 }
2688
2689 pub fn is_recv(&self) -> bool {
2691 matches!(self, Self::MethodArg(_, _, 0))
2692 }
2693
2694 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2696 match *self {
2697 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2698 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2699 def_site_def_id: Some(id.def_id.to_def_id()),
2700 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2701 }),
2702 Self::Return(id) => {
2703 if let Node::Expr(Expr {
2704 kind: ExprKind::Closure(c),
2705 ..
2706 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2707 {
2708 match c.fn_decl.output {
2709 FnRetTy::DefaultReturn(_) => None,
2710 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2711 }
2712 } else {
2713 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2714 Some(DefinedTy::Mir {
2715 def_site_def_id: Some(id.def_id.to_def_id()),
2716 ty,
2717 })
2718 }
2719 },
2720 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2721 Some(Expr {
2722 hir_id,
2723 kind: ExprKind::Struct(path, ..),
2724 ..
2725 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2726 .and_then(|(adt, variant)| {
2727 variant
2728 .fields
2729 .iter()
2730 .find(|f| f.name == field.ident.name)
2731 .map(|f| (adt, f))
2732 })
2733 .map(|(adt, field_def)| DefinedTy::Mir {
2734 def_site_def_id: Some(adt.did()),
2735 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2736 }),
2737 _ => None,
2738 },
2739 Self::FnArg(callee, i) => {
2740 let sig = expr_sig(cx, callee)?;
2741 let (hir_ty, ty) = sig.input_with_hir(i)?;
2742 Some(match hir_ty {
2743 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2744 None => DefinedTy::Mir {
2745 def_site_def_id: sig.predicates_id(),
2746 ty,
2747 },
2748 })
2749 },
2750 Self::MethodArg(id, _, i) => {
2751 let id = cx.typeck_results().type_dependent_def_id(id)?;
2752 let sig = cx.tcx.fn_sig(id).skip_binder();
2753 Some(DefinedTy::Mir {
2754 def_site_def_id: Some(id),
2755 ty: sig.input(i),
2756 })
2757 },
2758 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2759 }
2760 }
2761}
2762
2763pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2765 let mut adjustments = [].as_slice();
2766 let mut is_ty_unified = false;
2767 let mut moved_before_use = false;
2768 let mut same_ctxt = true;
2769 let ctxt = e.span.ctxt();
2770 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2771 if adjustments.is_empty()
2772 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2773 {
2774 adjustments = cx.typeck_results().expr_adjustments(e);
2775 }
2776 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2777 if let Node::Expr(e) = parent {
2778 match e.kind {
2779 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2780 is_ty_unified = true;
2781 moved_before_use = true;
2782 },
2783 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2784 is_ty_unified = true;
2785 moved_before_use = true;
2786 },
2787 ExprKind::Block(..) => moved_before_use = true,
2788 _ => {},
2789 }
2790 }
2791 ControlFlow::Continue(())
2792 });
2793 match node {
2794 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2795 node,
2796 child_id,
2797 adjustments,
2798 is_ty_unified,
2799 moved_before_use,
2800 same_ctxt,
2801 },
2802 None => ExprUseCtxt {
2803 node: Node::Crate(cx.tcx.hir_root_module()),
2804 child_id: HirId::INVALID,
2805 adjustments: &[],
2806 is_ty_unified: true,
2807 moved_before_use: true,
2808 same_ctxt: false,
2809 },
2810 }
2811}
2812
2813pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2815 let mut pos = 0;
2816 tokenize(s, FrontmatterAllowed::No).map(move |t| {
2817 let end = pos + t.len;
2818 let range = pos as usize..end as usize;
2819 let inner = InnerSpan::new(range.start, range.end);
2820 pos = end;
2821 (t.kind, s.get(range).unwrap_or_default(), inner)
2822 })
2823}
2824
2825pub fn span_contains_comment(cx: &impl source::HasSession, span: Span) -> bool {
2828 span.check_source_text(cx, |snippet| {
2829 tokenize(snippet, FrontmatterAllowed::No).any(|token| {
2830 matches!(
2831 token.kind,
2832 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2833 )
2834 })
2835 })
2836}
2837
2838pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2843 span.check_source_text(cx, |snippet| {
2844 tokenize_with_text(snippet).any(|(token, _, _)| match token {
2845 TokenKind::Whitespace => false,
2846 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2847 _ => true,
2848 })
2849 })
2850}
2851pub fn span_extract_comment(cx: &impl source::HasSession, span: Span) -> String {
2855 span_extract_comments(cx, span).join("\n")
2856}
2857
2858pub fn span_extract_comments(cx: &impl source::HasSession, span: Span) -> Vec<String> {
2862 span.with_source_text(cx, |snippet| {
2863 tokenize_with_text(snippet)
2864 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2865 .map(|(_, s, _)| s.to_string())
2866 .collect::<Vec<_>>()
2867 })
2868 .unwrap_or_default()
2869}
2870
2871pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2872 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2873}
2874
2875pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2900 cx: &LateContext<'_>,
2901 pat: &'a Pat<'hir>,
2902 else_body: &Expr<'_>,
2903) -> Option<&'a Pat<'hir>> {
2904 if let Some([inner_pat]) = as_some_pattern(cx, pat)
2905 && !is_refutable(cx, inner_pat)
2906 && let else_body = peel_blocks(else_body)
2907 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2908 && let ExprKind::Path(ret_path) = ret_val.kind
2909 && cx
2910 .qpath_res(&ret_path, ret_val.hir_id)
2911 .ctor_parent(cx)
2912 .is_lang_item(cx, OptionNone)
2913 {
2914 Some(inner_pat)
2915 } else {
2916 None
2917 }
2918}
2919
2920macro_rules! op_utils {
2921 ($($name:ident $assign:ident)*) => {
2922 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2924
2925 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2927
2928 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2930 match kind {
2931 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2932 _ => None,
2933 }
2934 }
2935 };
2936}
2937
2938op_utils! {
2939 Add AddAssign
2940 Sub SubAssign
2941 Mul MulAssign
2942 Div DivAssign
2943 Rem RemAssign
2944 BitXor BitXorAssign
2945 BitAnd BitAndAssign
2946 BitOr BitOrAssign
2947 Shl ShlAssign
2948 Shr ShrAssign
2949}
2950
2951pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2954 match *pat {
2955 PatKind::Wild => true,
2956 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2957 !visitors::is_local_used(cx, body, id)
2958 },
2959 _ => false,
2960 }
2961}
2962
2963#[derive(Clone, Copy)]
2964pub enum RequiresSemi {
2965 Yes,
2966 No,
2967}
2968impl RequiresSemi {
2969 pub fn requires_semi(self) -> bool {
2970 matches!(self, Self::Yes)
2971 }
2972}
2973
2974#[expect(clippy::too_many_lines)]
2977pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2978 struct BreakTarget {
2979 id: HirId,
2980 unused: bool,
2981 }
2982
2983 struct V<'cx, 'tcx> {
2984 cx: &'cx LateContext<'tcx>,
2985 break_targets: Vec<BreakTarget>,
2986 break_targets_for_result_ty: u32,
2987 in_final_expr: bool,
2988 requires_semi: bool,
2989 is_never: bool,
2990 }
2991
2992 impl V<'_, '_> {
2993 fn push_break_target(&mut self, id: HirId) {
2994 self.break_targets.push(BreakTarget { id, unused: true });
2995 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2996 }
2997 }
2998
2999 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
3000 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
3001 if self.is_never && self.break_targets.is_empty() {
3018 if self.in_final_expr && !self.requires_semi {
3019 match e.kind {
3022 ExprKind::DropTemps(e) => self.visit_expr(e),
3023 ExprKind::If(_, then, Some(else_)) => {
3024 self.visit_expr(then);
3025 self.visit_expr(else_);
3026 },
3027 ExprKind::Match(_, arms, _) => {
3028 for arm in arms {
3029 self.visit_expr(arm.body);
3030 }
3031 },
3032 ExprKind::Loop(b, ..) => {
3033 self.push_break_target(e.hir_id);
3034 self.in_final_expr = false;
3035 self.visit_block(b);
3036 self.break_targets.pop();
3037 },
3038 ExprKind::Block(b, _) => {
3039 if b.targeted_by_break {
3040 self.push_break_target(b.hir_id);
3041 self.visit_block(b);
3042 self.break_targets.pop();
3043 } else {
3044 self.visit_block(b);
3045 }
3046 },
3047 _ => {
3048 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3049 },
3050 }
3051 }
3052 return;
3053 }
3054 match e.kind {
3055 ExprKind::DropTemps(e) => self.visit_expr(e),
3056 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3057 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3058 self.in_final_expr = false;
3059 self.visit_expr(e);
3060 self.is_never = true;
3061 },
3062 ExprKind::Break(dest, e) => {
3063 if let Some(e) = e {
3064 self.in_final_expr = false;
3065 self.visit_expr(e);
3066 }
3067 if let Ok(id) = dest.target_id
3068 && let Some((i, target)) = self
3069 .break_targets
3070 .iter_mut()
3071 .enumerate()
3072 .find(|(_, target)| target.id == id)
3073 {
3074 target.unused &= self.is_never;
3075 if i < self.break_targets_for_result_ty as usize {
3076 self.requires_semi = true;
3077 }
3078 }
3079 self.is_never = true;
3080 },
3081 ExprKind::If(cond, then, else_) => {
3082 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3083 self.visit_expr(cond);
3084 self.in_final_expr = in_final_expr;
3085
3086 if self.is_never {
3087 self.visit_expr(then);
3088 if let Some(else_) = else_ {
3089 self.visit_expr(else_);
3090 }
3091 } else {
3092 self.visit_expr(then);
3093 let is_never = mem::replace(&mut self.is_never, false);
3094 if let Some(else_) = else_ {
3095 self.visit_expr(else_);
3096 self.is_never &= is_never;
3097 }
3098 }
3099 },
3100 ExprKind::Match(scrutinee, arms, _) => {
3101 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3102 self.visit_expr(scrutinee);
3103 self.in_final_expr = in_final_expr;
3104
3105 if self.is_never {
3106 for arm in arms {
3107 self.visit_arm(arm);
3108 }
3109 } else {
3110 let mut is_never = true;
3111 for arm in arms {
3112 self.is_never = false;
3113 if let Some(guard) = arm.guard {
3114 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3115 self.visit_expr(guard);
3116 self.in_final_expr = in_final_expr;
3117 self.is_never = false;
3119 }
3120 self.visit_expr(arm.body);
3121 is_never &= self.is_never;
3122 }
3123 self.is_never = is_never;
3124 }
3125 },
3126 ExprKind::Loop(b, _, _, _) => {
3127 self.push_break_target(e.hir_id);
3128 self.in_final_expr = false;
3129 self.visit_block(b);
3130 self.is_never = self.break_targets.pop().unwrap().unused;
3131 },
3132 ExprKind::Block(b, _) => {
3133 if b.targeted_by_break {
3134 self.push_break_target(b.hir_id);
3135 self.visit_block(b);
3136 self.is_never &= self.break_targets.pop().unwrap().unused;
3137 } else {
3138 self.visit_block(b);
3139 }
3140 },
3141 _ => {
3142 self.in_final_expr = false;
3143 walk_expr(self, e);
3144 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3145 },
3146 }
3147 }
3148
3149 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3150 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3151 for s in b.stmts {
3152 self.visit_stmt(s);
3153 }
3154 self.in_final_expr = in_final_expr;
3155 if let Some(e) = b.expr {
3156 self.visit_expr(e);
3157 }
3158 }
3159
3160 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3161 if let Some(e) = l.init {
3162 self.visit_expr(e);
3163 }
3164 if let Some(else_) = l.els {
3165 let is_never = self.is_never;
3166 self.visit_block(else_);
3167 self.is_never = is_never;
3168 }
3169 }
3170
3171 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3172 if let Some(guard) = arm.guard {
3173 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3174 self.visit_expr(guard);
3175 self.in_final_expr = in_final_expr;
3176 }
3177 self.visit_expr(arm.body);
3178 }
3179 }
3180
3181 if cx.typeck_results().expr_ty(e).is_never() {
3182 Some(RequiresSemi::No)
3183 } else if let ExprKind::Block(b, _) = e.kind
3184 && !b.targeted_by_break
3185 && b.expr.is_none()
3186 {
3187 None
3189 } else {
3190 let mut v = V {
3191 cx,
3192 break_targets: Vec::new(),
3193 break_targets_for_result_ty: 0,
3194 in_final_expr: true,
3195 requires_semi: false,
3196 is_never: false,
3197 };
3198 v.visit_expr(e);
3199 v.is_never
3200 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3201 RequiresSemi::Yes
3202 } else {
3203 RequiresSemi::No
3204 })
3205 }
3206}
3207
3208pub fn get_path_from_caller_to_method_type<'tcx>(
3214 tcx: TyCtxt<'tcx>,
3215 from: LocalDefId,
3216 method: DefId,
3217 args: GenericArgsRef<'tcx>,
3218) -> String {
3219 let assoc_item = tcx.associated_item(method);
3220 let def_id = assoc_item.container_id(tcx);
3221 match assoc_item.container {
3222 rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
3223 rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
3224 let ty = tcx.type_of(def_id).instantiate_identity();
3225 get_path_to_ty(tcx, from, ty, args)
3226 },
3227 }
3228}
3229
3230fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3231 match ty.kind() {
3232 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3233 rustc_ty::Array(..)
3235 | rustc_ty::Dynamic(..)
3236 | rustc_ty::Never
3237 | rustc_ty::RawPtr(_, _)
3238 | rustc_ty::Ref(..)
3239 | rustc_ty::Slice(_)
3240 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3241 _ => ty.to_string(),
3242 }
3243}
3244
3245fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3247 if callee.is_local() {
3249 let callee_path = tcx.def_path(callee);
3250 let caller_path = tcx.def_path(from.to_def_id());
3251 maybe_get_relative_path(&caller_path, &callee_path, 2)
3252 } else {
3253 tcx.def_path_str(callee)
3254 }
3255}
3256
3257fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3270 use itertools::EitherOrBoth::{Both, Left, Right};
3271
3272 let unique_parts = to
3274 .data
3275 .iter()
3276 .zip_longest(from.data.iter())
3277 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3278 .map(|el| match el {
3279 Both(l, r) => Both(l.data, r.data),
3280 Left(l) => Left(l.data),
3281 Right(r) => Right(r.data),
3282 });
3283
3284 let mut go_up_by = 0;
3286 let mut path = Vec::new();
3287 for el in unique_parts {
3288 match el {
3289 Both(l, r) => {
3290 if let DefPathData::TypeNs(sym) = l {
3300 path.push(sym);
3301 }
3302 if let DefPathData::TypeNs(_) = r {
3303 go_up_by += 1;
3304 }
3305 },
3306 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3311 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3316 _ => {},
3317 }
3318 }
3319
3320 if go_up_by > max_super {
3321 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3323 if let DefPathData::TypeNs(sym) = el.data {
3324 Some(sym)
3325 } else {
3326 None
3327 }
3328 })))
3329 } else if go_up_by == 0 && path.is_empty() {
3330 String::from("Self")
3331 } else {
3332 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3333 }
3334}
3335
3336pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3339 matches!(
3340 cx.tcx.parent_hir_node(id),
3341 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3342 )
3343}
3344
3345pub fn is_block_like(expr: &Expr<'_>) -> bool {
3348 matches!(
3349 expr.kind,
3350 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3351 )
3352}
3353
3354pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3356 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3357 match expr.kind {
3358 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3359 _ if is_block_like(expr) => is_operand,
3360 _ => false,
3361 }
3362 }
3363
3364 contains_block(expr, false)
3365}
3366
3367pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3369 if let Some(parent_expr) = get_parent_expr(cx, expr)
3370 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3371 && receiver.hir_id == expr.hir_id
3372 {
3373 return true;
3374 }
3375 false
3376}
3377
3378pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3381 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3382 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3383 && temporary_ty
3384 .walk()
3385 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3386 {
3387 ControlFlow::Break(())
3388 } else {
3389 ControlFlow::Continue(())
3390 }
3391 })
3392 .is_break()
3393}
3394
3395pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3406 let expr_ty_is_adjusted = cx
3407 .typeck_results()
3408 .expr_adjustments(expr)
3409 .iter()
3410 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3412 if expr_ty_is_adjusted {
3413 return true;
3414 }
3415
3416 match expr.kind {
3419 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3420 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3421
3422 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3423 return false;
3424 }
3425
3426 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3427 let mut args_with_ty_param = {
3428 fn_sig
3429 .inputs()
3430 .skip_binder()
3431 .iter()
3432 .skip(self_arg_count)
3433 .zip(args)
3434 .filter_map(|(arg_ty, arg)| {
3435 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3436 Some(arg)
3437 } else {
3438 None
3439 }
3440 })
3441 };
3442 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3443 },
3444 ExprKind::Struct(qpath, _, _) => {
3446 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3447 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3448 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3449 return true;
3451 };
3452 v_def
3453 .fields
3454 .iter()
3455 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3456 } else {
3457 false
3458 }
3459 },
3460 ExprKind::Block(
3462 &Block {
3463 expr: Some(ret_expr), ..
3464 },
3465 _,
3466 )
3467 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3468
3469 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3471 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3473 ExprKind::If(_, then, maybe_else) => {
3475 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3476 },
3477 ExprKind::Match(_, arms, _) => arms
3478 .iter()
3479 .map(|arm| arm.body)
3480 .any(|body| expr_requires_coercion(cx, body)),
3481 _ => false,
3482 }
3483}
3484
3485pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3488 if let Some(hir_id) = expr.res_local_id()
3489 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3490 {
3491 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3492 } else if let ExprKind::Path(p) = &expr.kind
3493 && let Some(mutability) = cx
3494 .qpath_res(p, expr.hir_id)
3495 .opt_def_id()
3496 .and_then(|id| cx.tcx.static_mutability(id))
3497 {
3498 mutability == Mutability::Mut
3499 } else if let ExprKind::Field(parent, _) = expr.kind {
3500 is_mutable(cx, parent)
3501 } else {
3502 true
3503 }
3504}
3505
3506pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3509 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3510 return hir_ty;
3511 };
3512 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3513 && let Some(segment) = path.segments.last()
3514 && segment.ident.name == sym::Option
3515 && let Res::Def(DefKind::Enum, def_id) = segment.res
3516 && def_id == option_def_id
3517 && let [GenericArg::Type(arg_ty)] = segment.args().args
3518 {
3519 hir_ty = arg_ty.as_unambig_ty();
3520 }
3521 hir_ty
3522}
3523
3524pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3527 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3528 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3529 && let ctxt = expr.span.ctxt()
3530 && for_each_expr_without_closures(into_future_arg, |e| {
3531 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3532 })
3533 .is_none()
3534 {
3535 Some(into_future_arg)
3536 } else {
3537 None
3538 }
3539}
3540
3541pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3543 if let ExprKind::Call(fn_expr, []) = &expr.kind
3544 && let ExprKind::Path(qpath) = &fn_expr.kind
3545 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3546 {
3547 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3548 } else {
3549 false
3550 }
3551}
3552
3553pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3570 let enclosing_body_owner = cx
3571 .tcx
3572 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3573 let mut prev_id = expr.hir_id;
3574 let mut skip_until_id = None;
3575 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3576 if hir_id == enclosing_body_owner {
3577 return true;
3578 }
3579 if let Some(id) = skip_until_id {
3580 prev_id = hir_id;
3581 if id == hir_id {
3582 skip_until_id = None;
3583 }
3584 continue;
3585 }
3586 match node {
3587 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3588 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3589 Node::Expr(expr) => match expr.kind {
3590 ExprKind::Ret(_) => return true,
3591 ExprKind::If(_, then, opt_else)
3592 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3593 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3594 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3595 ExprKind::Break(
3596 Destination {
3597 target_id: Ok(target_id),
3598 ..
3599 },
3600 _,
3601 ) => skip_until_id = Some(target_id),
3602 _ => break,
3603 },
3604 _ => break,
3605 }
3606 prev_id = hir_id;
3607 }
3608
3609 false
3612}
3613
3614pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3617 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3618 matches!(
3619 adj.kind,
3620 Adjust::Deref(DerefAdjustKind::Overloaded(_))
3621 | Adjust::Pointer(PointerCoercion::Unsize)
3622 | Adjust::NeverToAny
3623 )
3624 })
3625}
3626
3627pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3629 matches!(
3630 expr.kind,
3631 ExprKind::Closure(Closure {
3632 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3633 CoroutineDesugaring::Async,
3634 CoroutineSource::Block
3635 )),
3636 ..
3637 })
3638 )
3639}
3640
3641pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
3643 cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
3644}