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