1#![feature(box_patterns)]
2#![feature(if_let_guard)]
3#![feature(macro_metavar_expr)]
4#![feature(never_type)]
5#![feature(rustc_private)]
6#![feature(assert_matches)]
7#![feature(unwrap_infallible)]
8#![feature(array_windows)]
9#![recursion_limit = "512"]
10#![allow(
11 clippy::missing_errors_doc,
12 clippy::missing_panics_doc,
13 clippy::must_use_candidate,
14 rustc::diagnostic_outside_of_impl,
15 rustc::untranslatable_diagnostic
16)]
17#![warn(
18 trivial_casts,
19 trivial_numeric_casts,
20 rust_2018_idioms,
21 unused_lifetimes,
22 unused_qualifications,
23 rustc::internal
24)]
25
26extern crate indexmap;
29extern crate rustc_abi;
30extern crate rustc_ast;
31extern crate rustc_attr_parsing;
32extern crate rustc_const_eval;
33extern crate rustc_data_structures;
34#[allow(unused_extern_crates)]
36extern crate rustc_driver;
37extern crate rustc_errors;
38extern crate rustc_hir;
39extern crate rustc_hir_analysis;
40extern crate rustc_hir_typeck;
41extern crate rustc_index;
42extern crate rustc_infer;
43extern crate rustc_lexer;
44extern crate rustc_lint;
45extern crate rustc_middle;
46extern crate rustc_mir_dataflow;
47extern crate rustc_session;
48extern crate rustc_span;
49extern crate rustc_trait_selection;
50extern crate smallvec;
51
52pub mod ast_utils;
53pub mod attrs;
54mod check_proc_macro;
55pub mod comparisons;
56pub mod consts;
57pub mod diagnostics;
58pub mod eager_or_lazy;
59pub mod higher;
60mod hir_utils;
61pub mod macros;
62pub mod mir;
63pub mod msrvs;
64pub mod numeric_literal;
65pub mod paths;
66pub mod ptr;
67pub mod qualify_min_const_fn;
68pub mod source;
69pub mod str_utils;
70pub mod sugg;
71pub mod sym;
72pub mod ty;
73pub mod usage;
74pub mod visitors;
75
76pub use self::attrs::*;
77pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
78pub use self::hir_utils::{
79 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
80 hash_stmt, is_bool, over,
81};
82
83use core::mem;
84use core::ops::ControlFlow;
85use std::collections::hash_map::Entry;
86use std::iter::{once, repeat_n, zip};
87use std::sync::{Mutex, MutexGuard, OnceLock};
88
89use itertools::Itertools;
90use rustc_abi::Integer;
91use rustc_ast::ast::{self, LitKind, RangeLimits};
92use rustc_ast::join_path_syms;
93use rustc_data_structures::fx::FxHashMap;
94use rustc_data_structures::packed::Pu128;
95use rustc_data_structures::unhash::UnindexMap;
96use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
97use rustc_hir::attrs::AttributeKind;
98use rustc_hir::def::{DefKind, Res};
99use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
100use rustc_hir::definitions::{DefPath, DefPathData};
101use rustc_hir::hir_id::{HirIdMap, HirIdSet};
102use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
103use rustc_hir::{
104 self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
105 CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl,
106 ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
107 Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
108 TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr,
109};
110use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
111use rustc_lint::{LateContext, Level, Lint, LintContext};
112use rustc_middle::hir::nested_filter;
113use rustc_middle::hir::place::PlaceBase;
114use rustc_middle::lint::LevelAndSource;
115use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
116use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, PointerCoercion};
117use rustc_middle::ty::layout::IntegerExt;
118use rustc_middle::ty::{
119 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
120 TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
121};
122use rustc_span::hygiene::{ExpnKind, MacroKind};
123use rustc_span::source_map::SourceMap;
124use rustc_span::symbol::{Ident, Symbol, kw};
125use rustc_span::{InnerSpan, Span};
126use source::{SpanRangeExt, walk_span_to_context};
127use visitors::{Visitable, for_each_unconsumed_temporary};
128
129use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
130use crate::higher::Range;
131use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
132use crate::visitors::for_each_expr_without_closures;
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) = path_to_local(expr)
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_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
253 if let Res::Def(DefKind::Ctor(..), id) = res
254 && let Some(lang_id) = cx.tcx.lang_items().get(lang_item)
255 && let Some(id) = cx.tcx.opt_parent(id)
256 {
257 id == lang_id
258 } else {
259 false
260 }
261}
262
263pub fn is_enum_variant_ctor(
265 cx: &LateContext<'_>,
266 enum_item: Symbol,
267 variant_name: Symbol,
268 ctor_call_id: DefId,
269) -> bool {
270 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
271 return false;
272 };
273
274 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
275 variants
276 .filter(|variant| variant.name == variant_name)
277 .filter_map(|variant| variant.ctor.as_ref())
278 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
279}
280
281pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
283 let did = match cx.tcx.def_kind(did) {
284 DefKind::Ctor(..) => cx.tcx.parent(did),
285 DefKind::Variant => match cx.tcx.opt_parent(did) {
287 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
288 _ => did,
289 },
290 _ => did,
291 };
292
293 cx.tcx.is_diagnostic_item(item, did)
294}
295
296pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
298 let did = match cx.tcx.def_kind(did) {
299 DefKind::Ctor(..) => cx.tcx.parent(did),
300 DefKind::Variant => match cx.tcx.opt_parent(did) {
302 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
303 _ => did,
304 },
305 _ => did,
306 };
307
308 cx.tcx.lang_items().get(item) == Some(did)
309}
310
311pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
312 matches!(
313 expr.kind,
314 ExprKind::Block(
315 Block {
316 stmts: [],
317 expr: None,
318 ..
319 },
320 _
321 ) | ExprKind::Tup([])
322 )
323}
324
325pub fn is_wild(pat: &Pat<'_>) -> bool {
327 matches!(pat.kind, PatKind::Wild)
328}
329
330pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
332 matches!(
333 arm.pat.kind,
334 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
335 if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone)
336 )
337}
338
339pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
341 match *qpath {
342 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
343 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
344 _ => false,
345 }
346}
347
348pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
350 if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
351 cx.tcx.trait_of_assoc(method_id).is_none()
352 } else {
353 false
354 }
355}
356
357pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
359 if let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
360 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
361 {
362 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
363 }
364 false
365}
366
367pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
369 if let Some(trait_did) = cx.tcx.trait_of_assoc(def_id) {
370 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
371 }
372 false
373}
374
375pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
377 cx.typeck_results()
378 .type_dependent_def_id(expr.hir_id)
379 .is_some_and(|did| is_diag_trait_item(cx, did, diag_item))
380}
381
382pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
384 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
385 && let ItemKind::Impl(imp) = item.kind
386 {
387 imp.of_trait.is_some()
388 } else {
389 false
390 }
391}
392
393pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
403 if let ExprKind::Path(ref qpath) = expr.kind {
404 cx.qpath_res(qpath, expr.hir_id)
405 .opt_def_id()
406 .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item))
407 } else {
408 false
409 }
410}
411
412pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
413 match *path {
414 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
415 QPath::TypeRelative(_, seg) => seg,
416 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
417 }
418}
419
420pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
421 last_path_segment(qpath)
422 .args
423 .map_or(&[][..], |a| a.args)
424 .iter()
425 .filter_map(|a| match a {
426 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
427 _ => None,
428 })
429}
430
431pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
434 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id))
435}
436
437pub fn is_path_diagnostic_item<'tcx>(
440 cx: &LateContext<'_>,
441 maybe_path: &impl MaybePath<'tcx>,
442 diag_item: Symbol,
443) -> bool {
444 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
445}
446
447pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
449 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
450 && let Res::Local(id) = path.res
451 {
452 return Some(id);
453 }
454 None
455}
456
457pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
460 path_to_local(expr) == Some(id)
461}
462
463pub trait MaybePath<'hir> {
464 fn hir_id(&self) -> HirId;
465 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
466}
467
468macro_rules! maybe_path {
469 ($ty:ident, $kind:ident) => {
470 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
471 fn hir_id(&self) -> HirId {
472 self.hir_id
473 }
474 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
475 match &self.kind {
476 hir::$kind::Path(qpath) => Some(qpath),
477 _ => None,
478 }
479 }
480 }
481 };
482}
483maybe_path!(Expr, ExprKind);
484impl<'hir> MaybePath<'hir> for Pat<'hir> {
485 fn hir_id(&self) -> HirId {
486 self.hir_id
487 }
488 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
489 match &self.kind {
490 PatKind::Expr(PatExpr {
491 kind: PatExprKind::Path(qpath),
492 ..
493 }) => Some(qpath),
494 _ => None,
495 }
496 }
497}
498maybe_path!(Ty, TyKind);
499
500pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
502 match maybe_path.qpath_opt() {
503 None => Res::Err,
504 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
505 }
506}
507
508pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
510 path_res(cx, maybe_path).opt_def_id()
511}
512
513pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
529 if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
530 && let ItemKind::Impl(impl_) = &item.kind
531 && let Some(of_trait) = impl_.of_trait
532 {
533 return Some(&of_trait.trait_ref);
534 }
535 None
536}
537
538fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
546 let mut result = vec![];
547 let root = loop {
548 match e.kind {
549 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
550 result.push(e);
551 e = ep;
552 },
553 _ => break e,
554 }
555 };
556 result.reverse();
557 (result, root)
558}
559
560pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
562 cx.typeck_results()
563 .expr_adjustments(e)
564 .iter()
565 .find_map(|a| match a.kind {
566 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
567 Adjust::Deref(None) => None,
568 _ => Some(None),
569 })
570 .and_then(|x| x)
571}
572
573pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
576 let (s1, r1) = projection_stack(e1);
577 let (s2, r2) = projection_stack(e2);
578 if !eq_expr_value(cx, r1, r2) {
579 return true;
580 }
581 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
582 return false;
583 }
584
585 for (x1, x2) in zip(&s1, &s2) {
586 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
587 return false;
588 }
589
590 match (&x1.kind, &x2.kind) {
591 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
592 if i1 != i2 {
593 return true;
594 }
595 },
596 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
597 if !eq_expr_value(cx, i1, i2) {
598 return false;
599 }
600 },
601 _ => return false,
602 }
603 }
604 false
605}
606
607fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
610 let std_types_symbols = &[
611 sym::Vec,
612 sym::VecDeque,
613 sym::LinkedList,
614 sym::HashMap,
615 sym::BTreeMap,
616 sym::HashSet,
617 sym::BTreeSet,
618 sym::BinaryHeap,
619 ];
620
621 if let QPath::TypeRelative(_, method) = path
622 && method.ident.name == sym::new
623 && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
624 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
625 {
626 return std_types_symbols.iter().any(|&symbol| {
627 cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
628 });
629 }
630 false
631}
632
633pub fn is_default_equivalent_call(
635 cx: &LateContext<'_>,
636 repl_func: &Expr<'_>,
637 whole_call_expr: Option<&Expr<'_>>,
638) -> bool {
639 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
640 && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
641 && (is_diag_trait_item(cx, repl_def_id, sym::Default)
642 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath))
643 {
644 return true;
645 }
646
647 let Some(e) = whole_call_expr else { return false };
650 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
651 return false;
652 };
653 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
654 return false;
655 };
656 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
657 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
658 cx.tcx.lifetimes.re_erased.into()
659 } else if param.index == 0 && param.name == kw::SelfUpper {
660 ty.into()
661 } else {
662 param.to_error(cx.tcx)
663 }
664 });
665 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
666
667 let Ok(Some(instance)) = instance else { return false };
668 if let rustc_ty::InstanceKind::Item(def) = instance.def
669 && !cx.tcx.is_mir_available(def)
670 {
671 return false;
672 }
673 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
674 return false;
675 };
676 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
677 return false;
678 };
679
680 let body = cx.tcx.instance_mir(instance.def);
686 for block_data in body.basic_blocks.iter() {
687 if block_data.statements.len() == 1
688 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
689 && assign.0.local == RETURN_PLACE
690 && let Rvalue::Aggregate(kind, _places) = &assign.1
691 && let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
692 && let def = cx.tcx.adt_def(did)
693 && let variant = &def.variant(*variant_index)
694 && variant.fields.is_empty()
695 && let Some((_, did)) = variant.ctor
696 && did == repl_def_id
697 {
698 return true;
699 } else if block_data.statements.is_empty()
700 && let Some(term) = &block_data.terminator
701 {
702 match &term.kind {
703 TerminatorKind::Call {
704 func: Operand::Constant(c),
705 ..
706 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
707 && *did == repl_def_id =>
708 {
709 return true;
710 },
711 TerminatorKind::TailCall {
712 func: Operand::Constant(c),
713 ..
714 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
715 && *did == repl_def_id =>
716 {
717 return true;
718 },
719 _ => {},
720 }
721 }
722 }
723 false
724}
725
726pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
730 match &e.kind {
731 ExprKind::Lit(lit) => match lit.node {
732 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
733 LitKind::Str(s, _) => s.is_empty(),
734 _ => false,
735 },
736 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
737 ExprKind::Repeat(x, len) => {
738 if let ConstArgKind::Anon(anon_const) = len.kind
739 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
740 && let LitKind::Int(v, _) = const_lit.node
741 && v <= 32
742 && is_default_equivalent(cx, x)
743 {
744 true
745 } else {
746 false
747 }
748 },
749 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
750 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
751 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
752 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
753 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
754 _ => false,
755 }
756}
757
758fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
759 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
760 && seg.ident.name == sym::from
761 {
762 match arg.kind {
763 ExprKind::Lit(hir::Lit {
764 node: LitKind::Str(sym, _),
765 ..
766 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
767 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
768 ExprKind::Repeat(_, len) => {
769 if let ConstArgKind::Anon(anon_const) = len.kind
770 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
771 && let LitKind::Int(v, _) = const_lit.node
772 {
773 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
774 }
775 },
776 _ => (),
777 }
778 }
779 false
780}
781
782pub fn can_move_expr_to_closure_no_visit<'tcx>(
814 cx: &LateContext<'tcx>,
815 expr: &'tcx Expr<'_>,
816 loop_ids: &[HirId],
817 ignore_locals: &HirIdSet,
818) -> bool {
819 match expr.kind {
820 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
821 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
822 if loop_ids.contains(&id) =>
823 {
824 true
825 },
826 ExprKind::Break(..)
827 | ExprKind::Continue(_)
828 | ExprKind::Ret(_)
829 | ExprKind::Yield(..)
830 | ExprKind::InlineAsm(_) => false,
831 ExprKind::Field(
834 &Expr {
835 hir_id,
836 kind:
837 ExprKind::Path(QPath::Resolved(
838 _,
839 Path {
840 res: Res::Local(local_id),
841 ..
842 },
843 )),
844 ..
845 },
846 _,
847 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
848 false
850 },
851 _ => true,
852 }
853}
854
855#[derive(Debug, Clone, Copy, PartialEq, Eq)]
857pub enum CaptureKind {
858 Value,
859 Use,
860 Ref(Mutability),
861}
862impl CaptureKind {
863 pub fn is_imm_ref(self) -> bool {
864 self == Self::Ref(Mutability::Not)
865 }
866}
867impl std::ops::BitOr for CaptureKind {
868 type Output = Self;
869 fn bitor(self, rhs: Self) -> Self::Output {
870 match (self, rhs) {
871 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
872 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
873 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
874 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
875 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
876 }
877 }
878}
879impl std::ops::BitOrAssign for CaptureKind {
880 fn bitor_assign(&mut self, rhs: Self) {
881 *self = *self | rhs;
882 }
883}
884
885pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
891 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
892 let mut capture = CaptureKind::Ref(Mutability::Not);
893 pat.each_binding_or_first(&mut |_, id, span, _| match cx
894 .typeck_results()
895 .extract_binding_mode(cx.sess(), id, span)
896 .0
897 {
898 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
899 capture = CaptureKind::Value;
900 },
901 ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
902 capture = CaptureKind::Ref(Mutability::Mut);
903 },
904 _ => (),
905 });
906 capture
907 }
908
909 debug_assert!(matches!(
910 e.kind,
911 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
912 ));
913
914 let mut child_id = e.hir_id;
915 let mut capture = CaptureKind::Value;
916 let mut capture_expr_ty = e;
917
918 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
919 if let [
920 Adjustment {
921 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
922 target,
923 },
924 ref adjust @ ..,
925 ] = *cx
926 .typeck_results()
927 .adjustments()
928 .get(child_id)
929 .map_or(&[][..], |x| &**x)
930 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
931 *adjust.last().map_or(target, |a| a.target).kind()
932 {
933 return CaptureKind::Ref(mutability);
934 }
935
936 match parent {
937 Node::Expr(e) => match e.kind {
938 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
939 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
940 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
941 return CaptureKind::Ref(Mutability::Mut);
942 },
943 ExprKind::Field(..) => {
944 if capture == CaptureKind::Value {
945 capture_expr_ty = e;
946 }
947 },
948 ExprKind::Let(let_expr) => {
949 let mutability = match pat_capture_kind(cx, let_expr.pat) {
950 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
951 CaptureKind::Ref(m) => m,
952 };
953 return CaptureKind::Ref(mutability);
954 },
955 ExprKind::Match(_, arms, _) => {
956 let mut mutability = Mutability::Not;
957 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
958 match capture {
959 CaptureKind::Value | CaptureKind::Use => break,
960 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
961 CaptureKind::Ref(Mutability::Not) => (),
962 }
963 }
964 return CaptureKind::Ref(mutability);
965 },
966 _ => break,
967 },
968 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
969 CaptureKind::Value | CaptureKind::Use => break,
970 capture @ CaptureKind::Ref(_) => return capture,
971 },
972 _ => break,
973 }
974
975 child_id = parent_id;
976 }
977
978 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
979 CaptureKind::Ref(Mutability::Not)
981 } else {
982 capture
983 }
984}
985
986pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
989 struct V<'cx, 'tcx> {
990 cx: &'cx LateContext<'tcx>,
991 loops: Vec<HirId>,
993 locals: HirIdSet,
995 allow_closure: bool,
997 captures: HirIdMap<CaptureKind>,
1000 }
1001 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1002 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1003 if !self.allow_closure {
1004 return;
1005 }
1006
1007 match e.kind {
1008 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1009 if !self.locals.contains(&l) {
1010 let cap = capture_local_usage(self.cx, e);
1011 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1012 }
1013 },
1014 ExprKind::Closure(closure) => {
1015 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
1016 let local_id = match capture.place.base {
1017 PlaceBase::Local(id) => id,
1018 PlaceBase::Upvar(var) => var.var_path.hir_id,
1019 _ => continue,
1020 };
1021 if !self.locals.contains(&local_id) {
1022 let capture = match capture.info.capture_kind {
1023 UpvarCapture::ByValue => CaptureKind::Value,
1024 UpvarCapture::ByUse => CaptureKind::Use,
1025 UpvarCapture::ByRef(kind) => match kind {
1026 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
1027 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
1028 CaptureKind::Ref(Mutability::Mut)
1029 },
1030 },
1031 };
1032 self.captures
1033 .entry(local_id)
1034 .and_modify(|e| *e |= capture)
1035 .or_insert(capture);
1036 }
1037 }
1038 },
1039 ExprKind::Loop(b, ..) => {
1040 self.loops.push(e.hir_id);
1041 self.visit_block(b);
1042 self.loops.pop();
1043 },
1044 _ => {
1045 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1046 walk_expr(self, e);
1047 },
1048 }
1049 }
1050
1051 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1052 p.each_binding_or_first(&mut |_, id, _, _| {
1053 self.locals.insert(id);
1054 });
1055 }
1056 }
1057
1058 let mut v = V {
1059 cx,
1060 loops: Vec::new(),
1061 locals: HirIdSet::default(),
1062 allow_closure: true,
1063 captures: HirIdMap::default(),
1064 };
1065 v.visit_expr(expr);
1066 v.allow_closure.then_some(v.captures)
1067}
1068
1069pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1071
1072pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1075 let mut method_names = Vec::with_capacity(max_depth);
1076 let mut arg_lists = Vec::with_capacity(max_depth);
1077 let mut spans = Vec::with_capacity(max_depth);
1078
1079 let mut current = expr;
1080 for _ in 0..max_depth {
1081 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1082 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1083 break;
1084 }
1085 method_names.push(path.ident.name);
1086 arg_lists.push((*receiver, &**args));
1087 spans.push(path.ident.span);
1088 current = receiver;
1089 } else {
1090 break;
1091 }
1092 }
1093
1094 (method_names, arg_lists, spans)
1095}
1096
1097pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[Symbol]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1104 let mut current = expr;
1105 let mut matched = Vec::with_capacity(methods.len());
1106 for method_name in methods.iter().rev() {
1107 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1109 if path.ident.name == *method_name {
1110 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1111 return None;
1112 }
1113 matched.push((receiver, args)); current = receiver; } else {
1116 return None;
1117 }
1118 } else {
1119 return None;
1120 }
1121 }
1122 matched.reverse();
1124 Some(matched)
1125}
1126
1127pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1129 cx.tcx
1130 .entry_fn(())
1131 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1132}
1133
1134pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1136 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1137 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1138}
1139
1140pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1142 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1143 match cx.tcx.hir_node_by_def_id(parent_id) {
1144 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1145 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1146 _ => None,
1147 }
1148}
1149
1150pub struct ContainsName<'a, 'tcx> {
1151 pub cx: &'a LateContext<'tcx>,
1152 pub name: Symbol,
1153}
1154
1155impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1156 type Result = ControlFlow<()>;
1157 type NestedFilter = nested_filter::OnlyBodies;
1158
1159 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1160 if self.name == name {
1161 ControlFlow::Break(())
1162 } else {
1163 ControlFlow::Continue(())
1164 }
1165 }
1166
1167 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1168 self.cx.tcx
1169 }
1170}
1171
1172pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1174 let mut cn = ContainsName { cx, name };
1175 cn.visit_expr(expr).is_break()
1176}
1177
1178pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1180 for_each_expr_without_closures(expr, |e| {
1181 if matches!(e.kind, ExprKind::Ret(..)) {
1182 ControlFlow::Break(())
1183 } else {
1184 ControlFlow::Continue(())
1185 }
1186 })
1187 .is_some()
1188}
1189
1190pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1192 get_parent_expr_for_hir(cx, e.hir_id)
1193}
1194
1195pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1198 match cx.tcx.parent_hir_node(hir_id) {
1199 Node::Expr(parent) => Some(parent),
1200 _ => None,
1201 }
1202}
1203
1204pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1206 let enclosing_node = cx
1207 .tcx
1208 .hir_get_enclosing_scope(hir_id)
1209 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1210 enclosing_node.and_then(|node| match node {
1211 Node::Block(block) => Some(block),
1212 Node::Item(&Item {
1213 kind: ItemKind::Fn { body: eid, .. },
1214 ..
1215 })
1216 | Node::ImplItem(&ImplItem {
1217 kind: ImplItemKind::Fn(_, eid),
1218 ..
1219 })
1220 | Node::TraitItem(&TraitItem {
1221 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1222 ..
1223 }) => match cx.tcx.hir_body(eid).value.kind {
1224 ExprKind::Block(block, _) => Some(block),
1225 _ => None,
1226 },
1227 _ => None,
1228 })
1229}
1230
1231pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1233 cx: &LateContext<'tcx>,
1234 expr: &Expr<'_>,
1235) -> Option<&'tcx Expr<'tcx>> {
1236 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1237 match node {
1238 Node::Expr(e) => match e.kind {
1239 ExprKind::Closure { .. }
1240 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1241 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1242
1243 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1245 _ => (),
1246 },
1247 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1248 _ => break,
1249 }
1250 }
1251 None
1252}
1253
1254pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1256 match tcx.hir_parent_iter(id).next() {
1257 Some((
1258 _,
1259 Node::Item(Item {
1260 kind: ItemKind::Impl(imp),
1261 ..
1262 }),
1263 )) => Some(imp),
1264 _ => None,
1265 }
1266}
1267
1268pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1279 while let ExprKind::Block(
1280 Block {
1281 stmts: [],
1282 expr: Some(inner),
1283 rules: BlockCheckMode::DefaultBlock,
1284 ..
1285 },
1286 _,
1287 ) = expr.kind
1288 {
1289 expr = inner;
1290 }
1291 expr
1292}
1293
1294pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1305 while let ExprKind::Block(
1306 Block {
1307 stmts: [],
1308 expr: Some(inner),
1309 rules: BlockCheckMode::DefaultBlock,
1310 ..
1311 }
1312 | Block {
1313 stmts:
1314 [
1315 Stmt {
1316 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1317 ..
1318 },
1319 ],
1320 expr: None,
1321 rules: BlockCheckMode::DefaultBlock,
1322 ..
1323 },
1324 _,
1325 ) = expr.kind
1326 {
1327 expr = inner;
1328 }
1329 expr
1330}
1331
1332pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1334 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1335 match iter.next() {
1336 Some((
1337 _,
1338 Node::Expr(Expr {
1339 kind: ExprKind::If(_, _, Some(else_expr)),
1340 ..
1341 }),
1342 )) => else_expr.hir_id == expr.hir_id,
1343 _ => false,
1344 }
1345}
1346
1347pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1350 let mut child_id = expr.hir_id;
1351 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1352 if let Node::LetStmt(LetStmt {
1353 init: Some(init),
1354 els: Some(els),
1355 ..
1356 }) = node
1357 && (init.hir_id == child_id || els.hir_id == child_id)
1358 {
1359 return true;
1360 }
1361
1362 child_id = parent_id;
1363 }
1364
1365 false
1366}
1367
1368pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1370 let mut child_id = expr.hir_id;
1371 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1372 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1373 && els.hir_id == child_id
1374 {
1375 return true;
1376 }
1377
1378 child_id = parent_id;
1379 }
1380
1381 false
1382}
1383
1384pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1399 let ty = cx.typeck_results().expr_ty(expr);
1400 if let Some(Range { start, end, limits }) = Range::hir(expr) {
1401 let start_is_none_or_min = start.is_none_or(|start| {
1402 if let rustc_ty::Adt(_, subst) = ty.kind()
1403 && let bnd_ty = subst.type_at(0)
1404 && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx)
1405 && let Some(min_const) = mir_to_const(cx.tcx, min_const)
1406 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1407 {
1408 start_const == min_const
1409 } else {
1410 false
1411 }
1412 });
1413 let end_is_none_or_max = end.is_none_or(|end| match limits {
1414 RangeLimits::Closed => {
1415 if let rustc_ty::Adt(_, subst) = ty.kind()
1416 && let bnd_ty = subst.type_at(0)
1417 && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx)
1418 && let Some(max_const) = mir_to_const(cx.tcx, max_const)
1419 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1420 {
1421 end_const == max_const
1422 } else {
1423 false
1424 }
1425 },
1426 RangeLimits::HalfOpen => {
1427 if let Some(container_path) = container_path
1428 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1429 && name.ident.name == sym::len
1430 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1431 {
1432 container_path.res == path.res
1433 } else {
1434 false
1435 }
1436 },
1437 });
1438 return start_is_none_or_min && end_is_none_or_max;
1439 }
1440 false
1441}
1442
1443pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1446 if is_integer_literal(e, value) {
1447 return true;
1448 }
1449 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1450 if let Some(Constant::Int(v)) =
1451 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1452 {
1453 return value == v;
1454 }
1455 false
1456}
1457
1458pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1460 if let ExprKind::Lit(spanned) = expr.kind
1462 && let LitKind::Int(v, _) = spanned.node
1463 {
1464 return v == value;
1465 }
1466 false
1467}
1468
1469pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1471 if let ExprKind::Lit(spanned) = expr.kind
1472 && let LitKind::Float(v, _) = spanned.node
1473 {
1474 v.as_str().parse() == Ok(value)
1475 } else {
1476 false
1477 }
1478}
1479
1480pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1488 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1489}
1490
1491#[must_use]
1495pub fn is_expn_of(mut span: Span, name: Symbol) -> Option<Span> {
1496 loop {
1497 if span.from_expansion() {
1498 let data = span.ctxt().outer_expn_data();
1499 let new_span = data.call_site;
1500
1501 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1502 && mac_name == name
1503 {
1504 return Some(new_span);
1505 }
1506
1507 span = new_span;
1508 } else {
1509 return None;
1510 }
1511 }
1512}
1513
1514#[must_use]
1525pub fn is_direct_expn_of(span: Span, name: Symbol) -> Option<Span> {
1526 if span.from_expansion() {
1527 let data = span.ctxt().outer_expn_data();
1528 let new_span = data.call_site;
1529
1530 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1531 && mac_name == name
1532 {
1533 return Some(new_span);
1534 }
1535 }
1536
1537 None
1538}
1539
1540pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1542 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1543 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1544}
1545
1546pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1548 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1549 cx.tcx.instantiate_bound_regions_with_erased(arg)
1550}
1551
1552pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1554 if let ExprKind::Call(fun, _) = expr.kind
1555 && let ExprKind::Path(ref qp) = fun.kind
1556 {
1557 let res = cx.qpath_res(qp, fun.hir_id);
1558 return match res {
1559 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1560 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1561 _ => false,
1562 };
1563 }
1564 false
1565}
1566
1567pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1570 fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1571 !matches!(
1572 cx.qpath_res(qpath, id),
1573 Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _)
1574 )
1575 }
1576
1577 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1578 i.into_iter().any(|pat| is_refutable(cx, pat))
1579 }
1580
1581 match pat.kind {
1582 PatKind::Missing => unreachable!(),
1583 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1585 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1586 PatKind::Expr(PatExpr {
1587 kind: PatExprKind::Path(qpath),
1588 hir_id,
1589 ..
1590 }) => is_qpath_refutable(cx, qpath, *hir_id),
1591 PatKind::Or(pats) => {
1592 are_refutable(cx, pats)
1594 },
1595 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1596 PatKind::Struct(ref qpath, fields, _) => {
1597 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1598 },
1599 PatKind::TupleStruct(ref qpath, pats, _) => {
1600 is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
1601 },
1602 PatKind::Slice(head, middle, tail) => {
1603 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1604 rustc_ty::Slice(..) => {
1605 !head.is_empty() || middle.is_none() || !tail.is_empty()
1607 },
1608 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1609 _ => {
1610 true
1612 },
1613 }
1614 },
1615 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1616 }
1617}
1618
1619pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1622 if let PatKind::Or(pats) = pat.kind {
1623 pats.iter().for_each(f);
1624 } else {
1625 f(pat);
1626 }
1627}
1628
1629pub fn is_self(slf: &Param<'_>) -> bool {
1630 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1631 name.name == kw::SelfLower
1632 } else {
1633 false
1634 }
1635}
1636
1637pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1638 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1639 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1640 {
1641 return true;
1642 }
1643 false
1644}
1645
1646pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1647 (0..decl.inputs.len()).map(move |i| &body.params[i])
1648}
1649
1650pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1653 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1654 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1655 && ddpos.as_opt_usize().is_none()
1656 && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk)
1657 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1658 && path_to_local_id(arm.body, hir_id)
1659 {
1660 return true;
1661 }
1662 false
1663 }
1664
1665 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1666 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1667 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1668 } else {
1669 false
1670 }
1671 }
1672
1673 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1674 if let MatchSource::TryDesugar(_) = *source {
1676 return Some(expr);
1677 }
1678
1679 if arms.len() == 2
1680 && arms[0].guard.is_none()
1681 && arms[1].guard.is_none()
1682 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1683 {
1684 return Some(expr);
1685 }
1686 }
1687
1688 None
1689}
1690
1691pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1701 let mut suppress_lint = false;
1702
1703 for id in ids {
1704 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1705 if let Some(expectation) = lint_id {
1706 cx.fulfill_expectation(expectation);
1707 }
1708
1709 match level {
1710 Level::Allow | Level::Expect => suppress_lint = true,
1711 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1712 }
1713 }
1714
1715 suppress_lint
1716}
1717
1718pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1726 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1727}
1728
1729pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1730 while let PatKind::Ref(subpat, _) = pat.kind {
1731 pat = subpat;
1732 }
1733 pat
1734}
1735
1736pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1737 Integer::from_int_ty(&tcx, ity).size().bits()
1738}
1739
1740#[expect(clippy::cast_possible_wrap)]
1741pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1743 let amt = 128 - int_bits(tcx, ity);
1744 ((u as i128) << amt) >> amt
1745}
1746
1747#[expect(clippy::cast_sign_loss)]
1748pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1750 let amt = 128 - int_bits(tcx, ity);
1751 ((u as u128) << amt) >> amt
1752}
1753
1754pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1756 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1757 let amt = 128 - bits;
1758 (u << amt) >> amt
1759}
1760
1761pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1762 attrs.iter().any(|attr| attr.has_name(symbol))
1763}
1764
1765pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1766 find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr { .. })
1767}
1768
1769pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1770 let mut prev_enclosing_node = None;
1771 let mut enclosing_node = node;
1772 while Some(enclosing_node) != prev_enclosing_node {
1773 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1774 return true;
1775 }
1776 prev_enclosing_node = Some(enclosing_node);
1777 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1778 }
1779
1780 false
1781}
1782
1783pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1786 tcx.hir_parent_owner_iter(id)
1787 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1788 .any(|(id, _)| {
1789 find_attr!(
1790 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1791 AttributeKind::AutomaticallyDerived(..)
1792 )
1793 })
1794}
1795
1796pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: Symbol) -> bool {
1798 cx.tcx.crate_name(did.krate) == sym::libc && cx.tcx.def_path_str(did).ends_with(name.as_str())
1801}
1802
1803pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1808 let mut conds = Vec::new();
1809 let mut blocks: Vec<&Block<'_>> = Vec::new();
1810
1811 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1812 conds.push(cond);
1813 if let ExprKind::Block(block, _) = then.kind {
1814 blocks.push(block);
1815 } else {
1816 panic!("ExprKind::If node is not an ExprKind::Block");
1817 }
1818
1819 if let Some(else_expr) = r#else {
1820 expr = else_expr;
1821 } else {
1822 break;
1823 }
1824 }
1825
1826 if !blocks.is_empty()
1828 && let ExprKind::Block(block, _) = expr.kind
1829 {
1830 blocks.push(block);
1831 }
1832
1833 (conds, blocks)
1834}
1835
1836pub fn is_async_fn(kind: FnKind<'_>) -> bool {
1838 match kind {
1839 FnKind::ItemFn(_, _, header) => header.asyncness.is_async(),
1840 FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
1841 FnKind::Closure => false,
1842 }
1843}
1844
1845pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1847 if let ExprKind::Closure(&Closure {
1848 body,
1849 kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
1850 ..
1851 }) = expr.kind
1852 && let ExprKind::Block(
1853 Block {
1854 expr:
1855 Some(Expr {
1856 kind: ExprKind::DropTemps(inner_expr),
1857 ..
1858 }),
1859 ..
1860 },
1861 _,
1862 ) = tcx.hir_body(body).value.kind
1863 {
1864 Some(inner_expr)
1865 } else {
1866 None
1867 }
1868}
1869
1870pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1872 get_async_closure_expr(tcx, body.value)
1873}
1874
1875pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1877 let did = match expr.kind {
1878 ExprKind::Call(path, _) => {
1879 if let ExprKind::Path(ref qpath) = path.kind
1880 && let Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id)
1881 {
1882 Some(did)
1883 } else {
1884 None
1885 }
1886 },
1887 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1888 _ => None,
1889 };
1890
1891 did.is_some_and(|did| find_attr!(cx.tcx.get_all_attrs(did), AttributeKind::MustUse { .. }))
1892}
1893
1894fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1906 let [param] = func.params else {
1907 return false;
1908 };
1909
1910 let mut expr = func.value;
1911 loop {
1912 match expr.kind {
1913 ExprKind::Block(
1914 &Block {
1915 stmts: [],
1916 expr: Some(e),
1917 ..
1918 },
1919 _,
1920 )
1921 | ExprKind::Ret(Some(e)) => expr = e,
1922 ExprKind::Block(
1923 &Block {
1924 stmts: [stmt],
1925 expr: None,
1926 ..
1927 },
1928 _,
1929 ) => {
1930 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1931 && let ExprKind::Ret(Some(ret_val)) = e.kind
1932 {
1933 expr = ret_val;
1934 } else {
1935 return false;
1936 }
1937 },
1938 _ => return is_expr_identity_of_pat(cx, param.pat, expr, true),
1939 }
1940 }
1941}
1942
1943pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool {
1953 if cx
1954 .typeck_results()
1955 .pat_binding_modes()
1956 .get(pat.hir_id)
1957 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
1958 {
1959 return false;
1963 }
1964
1965 let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir);
1967
1968 match (pat.kind, expr.kind) {
1969 (PatKind::Binding(_, id, _, _), _) if by_hir => {
1970 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1971 },
1972 (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => {
1973 matches!(path.segments, [ segment] if segment.ident.name == ident.name)
1974 },
1975 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1976 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1977 {
1978 zip(pats, tup).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1979 },
1980 (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
1981 zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
1982 },
1983 (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields))
1984 if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() =>
1985 {
1986 if let ExprKind::Path(ident) = &ident.kind
1988 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
1989 && zip(field_pats, fields).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr,by_hir))
1991 {
1992 true
1993 } else {
1994 false
1995 }
1996 },
1997 (PatKind::Struct(pat_ident, field_pats, false), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
1998 if field_pats.len() == fields.len() =>
1999 {
2000 qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
2002 && field_pats.iter().all(|field_pat| {
2004 fields.iter().any(|field| {
2005 field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
2006 })
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 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
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_std_crate(cx) {
2111 Some("std")
2112 } else if !is_no_core_crate(cx) {
2113 Some("core")
2114 } else {
2115 None
2116 }
2117}
2118
2119pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2120 cx.tcx
2121 .hir_attrs(hir::CRATE_HIR_ID)
2122 .iter()
2123 .any(|attr| attr.has_name(sym::no_std))
2124}
2125
2126pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2127 cx.tcx
2128 .hir_attrs(hir::CRATE_HIR_ID)
2129 .iter()
2130 .any(|attr| attr.has_name(sym::no_core))
2131}
2132
2133pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2143 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2144 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2145 } else {
2146 false
2147 }
2148}
2149
2150pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2160 use rustc_trait_selection::traits;
2161 let predicates = cx
2162 .tcx
2163 .predicates_of(did)
2164 .predicates
2165 .iter()
2166 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2167 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2168}
2169
2170pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2172 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2173}
2174
2175pub fn fn_def_id_with_node_args<'tcx>(
2178 cx: &LateContext<'tcx>,
2179 expr: &Expr<'_>,
2180) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2181 let typeck = cx.typeck_results();
2182 match &expr.kind {
2183 ExprKind::MethodCall(..) => Some((
2184 typeck.type_dependent_def_id(expr.hir_id)?,
2185 typeck.node_args(expr.hir_id),
2186 )),
2187 ExprKind::Call(
2188 Expr {
2189 kind: ExprKind::Path(qpath),
2190 hir_id: path_hir_id,
2191 ..
2192 },
2193 ..,
2194 ) => {
2195 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2198 typeck.qpath_res(qpath, *path_hir_id)
2199 {
2200 Some((id, typeck.node_args(*path_hir_id)))
2201 } else {
2202 None
2203 }
2204 },
2205 _ => None,
2206 }
2207}
2208
2209pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2214 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2215 let expr_kind = expr_type.kind();
2216 let is_primitive = match expr_kind {
2217 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2218 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2219 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2220 is_recursively_primitive_type(*element_type)
2221 } else {
2222 unreachable!()
2223 }
2224 },
2225 _ => false,
2226 };
2227
2228 if is_primitive {
2229 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2232 rustc_ty::Slice(..) => return Some("slice".into()),
2233 rustc_ty::Array(..) => return Some("array".into()),
2234 rustc_ty::Tuple(..) => return Some("tuple".into()),
2235 _ => {
2236 let refs_peeled = expr_type.peel_refs();
2239 return Some(refs_peeled.walk().last().unwrap().to_string());
2240 },
2241 }
2242 }
2243 None
2244}
2245
2246pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
2254where
2255 Hash: FnMut(&T) -> u64,
2256 Eq: FnMut(&T, &T) -> bool,
2257{
2258 match exprs {
2259 [a, b] if eq(a, b) => return vec![vec![a, b]],
2260 _ if exprs.len() <= 2 => return vec![],
2261 _ => {},
2262 }
2263
2264 let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
2265
2266 for expr in exprs {
2267 match buckets.entry(hash(expr)) {
2268 indexmap::map::Entry::Occupied(mut o) => {
2269 let bucket = o.get_mut();
2270 match bucket.iter_mut().find(|group| eq(expr, group[0])) {
2271 Some(group) => group.push(expr),
2272 None => bucket.push(vec![expr]),
2273 }
2274 },
2275 indexmap::map::Entry::Vacant(v) => {
2276 v.insert(vec![vec![expr]]);
2277 },
2278 }
2279 }
2280
2281 buckets
2282 .into_values()
2283 .flatten()
2284 .filter(|group| group.len() > 1)
2285 .collect()
2286}
2287
2288pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2291 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2292 if let PatKind::Ref(pat, _) = pat.kind {
2293 peel(pat, count + 1)
2294 } else {
2295 (pat, count)
2296 }
2297 }
2298 peel(pat, 0)
2299}
2300
2301pub fn peel_hir_expr_while<'tcx>(
2303 mut expr: &'tcx Expr<'tcx>,
2304 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2305) -> &'tcx Expr<'tcx> {
2306 while let Some(e) = f(expr) {
2307 expr = e;
2308 }
2309 expr
2310}
2311
2312pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2315 let mut remaining = count;
2316 let e = peel_hir_expr_while(expr, |e| match e.kind {
2317 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2318 remaining -= 1;
2319 Some(e)
2320 },
2321 _ => None,
2322 });
2323 (e, count - remaining)
2324}
2325
2326pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2329 let mut count: usize = 0;
2330 let mut curr_expr = expr;
2331 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2332 count = count.wrapping_add(1);
2333 curr_expr = local_expr;
2334 }
2335 (curr_expr, count)
2336}
2337
2338pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2341 let mut count = 0;
2342 let e = peel_hir_expr_while(expr, |e| match e.kind {
2343 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2344 count += 1;
2345 Some(e)
2346 },
2347 _ => None,
2348 });
2349 (e, count)
2350}
2351
2352pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2355 let mut count = 0;
2356 loop {
2357 match &ty.kind {
2358 TyKind::Ref(_, ref_ty) => {
2359 ty = ref_ty.ty;
2360 count += 1;
2361 },
2362 _ => break (ty, count),
2363 }
2364 }
2365}
2366
2367pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2370 let mut count = 0;
2371 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2372 ty = *dest_ty;
2373 count += 1;
2374 }
2375 (ty, count)
2376}
2377
2378pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2381 loop {
2382 match expr.kind {
2383 ExprKind::AddrOf(_, _, e) => expr = e,
2384 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2385 _ => break,
2386 }
2387 }
2388 expr
2389}
2390
2391pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2392 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2393 && let Res::Def(_, def_id) = path.res
2394 {
2395 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2396 }
2397 false
2398}
2399
2400static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2401
2402fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2405 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2406 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2407 let value = map.entry(module);
2408 match value {
2409 Entry::Occupied(entry) => f(entry.get()),
2410 Entry::Vacant(entry) => {
2411 let mut names = Vec::new();
2412 for id in tcx.hir_module_free_items(module) {
2413 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2414 && let item = tcx.hir_item(id)
2415 && let ItemKind::Const(ident, _generics, ty, _body) = item.kind
2416 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2417 && let Res::Def(DefKind::Struct, _) = path.res
2419 {
2420 let has_test_marker = tcx
2421 .hir_attrs(item.hir_id())
2422 .iter()
2423 .any(|a| a.has_name(sym::rustc_test_marker));
2424 if has_test_marker {
2425 names.push(ident.name);
2426 }
2427 }
2428 }
2429 names.sort_unstable();
2430 f(entry.insert(names))
2431 },
2432 }
2433}
2434
2435pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2439 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2440 let node = tcx.hir_node(id);
2441 once((id, node))
2442 .chain(tcx.hir_parent_iter(id))
2443 .any(|(_id, node)| {
2446 if let Node::Item(item) = node
2447 && let ItemKind::Fn { ident, .. } = item.kind
2448 {
2449 return names.binary_search(&ident.name).is_ok();
2452 }
2453 false
2454 })
2455 })
2456}
2457
2458pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2465 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2466 if let Node::Item(item) = tcx.hir_node(id)
2467 && let ItemKind::Fn { ident, .. } = item.kind
2468 {
2469 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2470 names.binary_search(&ident.name).is_ok()
2471 })
2472 } else {
2473 false
2474 }
2475}
2476
2477pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2482 tcx.hir_attrs(id).iter().any(|attr| {
2483 if attr.has_name(sym::cfg_trace)
2484 && let Some(items) = attr.meta_item_list()
2485 && let [item] = &*items
2486 && item.has_name(sym::test)
2487 {
2488 true
2489 } else {
2490 false
2491 }
2492 })
2493}
2494
2495pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2497 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2498}
2499
2500pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2502 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2503}
2504
2505pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2507 tcx.has_attr(def_id, sym::cfg_trace)
2508 || tcx
2509 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2510 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2511 .any(|attr| attr.has_name(sym::cfg_trace))
2512}
2513
2514pub fn walk_to_expr_usage<'tcx, T>(
2525 cx: &LateContext<'tcx>,
2526 e: &Expr<'tcx>,
2527 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2528) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2529 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2530 let mut child_id = e.hir_id;
2531
2532 while let Some((parent_id, parent)) = iter.next() {
2533 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2534 return Some(ControlFlow::Break(x));
2535 }
2536 let parent_expr = match parent {
2537 Node::Expr(e) => e,
2538 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2539 child_id = parent_id;
2540 continue;
2541 },
2542 Node::Arm(a) if a.body.hir_id == child_id => {
2543 child_id = parent_id;
2544 continue;
2545 },
2546 _ => return Some(ControlFlow::Continue((parent, child_id))),
2547 };
2548 match parent_expr.kind {
2549 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2550 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2551 child_id = id;
2552 iter = cx.tcx.hir_parent_iter(id);
2553 },
2554 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2555 _ => return Some(ControlFlow::Continue((parent, child_id))),
2556 }
2557 }
2558 debug_assert!(false, "no parent node found for `{child_id:?}`");
2559 None
2560}
2561
2562#[derive(Clone, Copy)]
2564pub enum DefinedTy<'tcx> {
2565 Hir(&'tcx hir::Ty<'tcx>),
2567 Mir {
2575 def_site_def_id: Option<DefId>,
2576 ty: Binder<'tcx, Ty<'tcx>>,
2577 },
2578}
2579
2580pub struct ExprUseCtxt<'tcx> {
2582 pub node: Node<'tcx>,
2584 pub child_id: HirId,
2586 pub adjustments: &'tcx [Adjustment<'tcx>],
2588 pub is_ty_unified: bool,
2590 pub moved_before_use: bool,
2592 pub same_ctxt: bool,
2594}
2595impl<'tcx> ExprUseCtxt<'tcx> {
2596 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2597 match self.node {
2598 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2599 Node::ExprField(field) => ExprUseNode::Field(field),
2600
2601 Node::Item(&Item {
2602 kind: ItemKind::Static(..) | ItemKind::Const(..),
2603 owner_id,
2604 ..
2605 })
2606 | Node::TraitItem(&TraitItem {
2607 kind: TraitItemKind::Const(..),
2608 owner_id,
2609 ..
2610 })
2611 | Node::ImplItem(&ImplItem {
2612 kind: ImplItemKind::Const(..),
2613 owner_id,
2614 ..
2615 }) => ExprUseNode::ConstStatic(owner_id),
2616
2617 Node::Item(&Item {
2618 kind: ItemKind::Fn { .. },
2619 owner_id,
2620 ..
2621 })
2622 | Node::TraitItem(&TraitItem {
2623 kind: TraitItemKind::Fn(..),
2624 owner_id,
2625 ..
2626 })
2627 | Node::ImplItem(&ImplItem {
2628 kind: ImplItemKind::Fn(..),
2629 owner_id,
2630 ..
2631 }) => ExprUseNode::Return(owner_id),
2632
2633 Node::Expr(use_expr) => match use_expr.kind {
2634 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2635 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2636 }),
2637
2638 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2639 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2640 Some(i) => ExprUseNode::FnArg(func, i),
2641 None => ExprUseNode::Callee,
2642 },
2643 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2644 use_expr.hir_id,
2645 name.args,
2646 args.iter()
2647 .position(|arg| arg.hir_id == self.child_id)
2648 .map_or(0, |i| i + 1),
2649 ),
2650 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2651 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2652 _ => ExprUseNode::Other,
2653 },
2654 _ => ExprUseNode::Other,
2655 }
2656 }
2657}
2658
2659pub enum ExprUseNode<'tcx> {
2661 LetStmt(&'tcx LetStmt<'tcx>),
2663 ConstStatic(OwnerId),
2665 Return(OwnerId),
2667 Field(&'tcx ExprField<'tcx>),
2669 FnArg(&'tcx Expr<'tcx>, usize),
2671 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2673 Callee,
2675 FieldAccess(Ident),
2677 AddrOf(ast::BorrowKind, Mutability),
2679 Other,
2680}
2681impl<'tcx> ExprUseNode<'tcx> {
2682 pub fn is_return(&self) -> bool {
2684 matches!(self, Self::Return(_))
2685 }
2686
2687 pub fn is_recv(&self) -> bool {
2689 matches!(self, Self::MethodArg(_, _, 0))
2690 }
2691
2692 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2694 match *self {
2695 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2696 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2697 def_site_def_id: Some(id.def_id.to_def_id()),
2698 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2699 }),
2700 Self::Return(id) => {
2701 if let Node::Expr(Expr {
2702 kind: ExprKind::Closure(c),
2703 ..
2704 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2705 {
2706 match c.fn_decl.output {
2707 FnRetTy::DefaultReturn(_) => None,
2708 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2709 }
2710 } else {
2711 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2712 Some(DefinedTy::Mir {
2713 def_site_def_id: Some(id.def_id.to_def_id()),
2714 ty,
2715 })
2716 }
2717 },
2718 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2719 Some(Expr {
2720 hir_id,
2721 kind: ExprKind::Struct(path, ..),
2722 ..
2723 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2724 .and_then(|(adt, variant)| {
2725 variant
2726 .fields
2727 .iter()
2728 .find(|f| f.name == field.ident.name)
2729 .map(|f| (adt, f))
2730 })
2731 .map(|(adt, field_def)| DefinedTy::Mir {
2732 def_site_def_id: Some(adt.did()),
2733 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2734 }),
2735 _ => None,
2736 },
2737 Self::FnArg(callee, i) => {
2738 let sig = expr_sig(cx, callee)?;
2739 let (hir_ty, ty) = sig.input_with_hir(i)?;
2740 Some(match hir_ty {
2741 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2742 None => DefinedTy::Mir {
2743 def_site_def_id: sig.predicates_id(),
2744 ty,
2745 },
2746 })
2747 },
2748 Self::MethodArg(id, _, i) => {
2749 let id = cx.typeck_results().type_dependent_def_id(id)?;
2750 let sig = cx.tcx.fn_sig(id).skip_binder();
2751 Some(DefinedTy::Mir {
2752 def_site_def_id: Some(id),
2753 ty: sig.input(i),
2754 })
2755 },
2756 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2757 }
2758 }
2759}
2760
2761pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2763 let mut adjustments = [].as_slice();
2764 let mut is_ty_unified = false;
2765 let mut moved_before_use = false;
2766 let mut same_ctxt = true;
2767 let ctxt = e.span.ctxt();
2768 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2769 if adjustments.is_empty()
2770 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2771 {
2772 adjustments = cx.typeck_results().expr_adjustments(e);
2773 }
2774 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2775 if let Node::Expr(e) = parent {
2776 match e.kind {
2777 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2778 is_ty_unified = true;
2779 moved_before_use = true;
2780 },
2781 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2782 is_ty_unified = true;
2783 moved_before_use = true;
2784 },
2785 ExprKind::Block(..) => moved_before_use = true,
2786 _ => {},
2787 }
2788 }
2789 ControlFlow::Continue(())
2790 });
2791 match node {
2792 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2793 node,
2794 child_id,
2795 adjustments,
2796 is_ty_unified,
2797 moved_before_use,
2798 same_ctxt,
2799 },
2800 #[allow(unreachable_patterns)]
2801 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
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(sm: &SourceMap, span: Span) -> bool {
2828 let Ok(snippet) = sm.span_to_snippet(span) else {
2829 return false;
2830 };
2831 return tokenize(&snippet, FrontmatterAllowed::No).any(|token| {
2832 matches!(
2833 token.kind,
2834 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2835 )
2836 });
2837}
2838
2839pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2844 matches!(span.get_source_text(cx), Some(snippet) if tokenize_with_text(&snippet).any(|(token, _, _)|
2845 match token {
2846 TokenKind::Whitespace => false,
2847 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2848 _ => true,
2849 }
2850 ))
2851}
2852pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2856 span_extract_comments(sm, span).join("\n")
2857}
2858
2859pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2863 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2864 tokenize_with_text(&snippet)
2865 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2866 .map(|(_, s, _)| s.to_string())
2867 .collect::<Vec<_>>()
2868}
2869
2870pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2871 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2872}
2873
2874pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2899 cx: &LateContext<'_>,
2900 pat: &'a Pat<'hir>,
2901 else_body: &Expr<'_>,
2902) -> Option<&'a Pat<'hir>> {
2903 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
2904 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
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 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
2910 {
2911 Some(inner_pat)
2912 } else {
2913 None
2914 }
2915}
2916
2917macro_rules! op_utils {
2918 ($($name:ident $assign:ident)*) => {
2919 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2921
2922 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2924
2925 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2927 match kind {
2928 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2929 _ => None,
2930 }
2931 }
2932 };
2933}
2934
2935op_utils! {
2936 Add AddAssign
2937 Sub SubAssign
2938 Mul MulAssign
2939 Div DivAssign
2940 Rem RemAssign
2941 BitXor BitXorAssign
2942 BitAnd BitAndAssign
2943 BitOr BitOrAssign
2944 Shl ShlAssign
2945 Shr ShrAssign
2946}
2947
2948pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2951 match *pat {
2952 PatKind::Wild => true,
2953 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2954 !visitors::is_local_used(cx, body, id)
2955 },
2956 _ => false,
2957 }
2958}
2959
2960#[derive(Clone, Copy)]
2961pub enum RequiresSemi {
2962 Yes,
2963 No,
2964}
2965impl RequiresSemi {
2966 pub fn requires_semi(self) -> bool {
2967 matches!(self, Self::Yes)
2968 }
2969}
2970
2971#[expect(clippy::too_many_lines)]
2974pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2975 struct BreakTarget {
2976 id: HirId,
2977 unused: bool,
2978 }
2979
2980 struct V<'cx, 'tcx> {
2981 cx: &'cx LateContext<'tcx>,
2982 break_targets: Vec<BreakTarget>,
2983 break_targets_for_result_ty: u32,
2984 in_final_expr: bool,
2985 requires_semi: bool,
2986 is_never: bool,
2987 }
2988
2989 impl V<'_, '_> {
2990 fn push_break_target(&mut self, id: HirId) {
2991 self.break_targets.push(BreakTarget { id, unused: true });
2992 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2993 }
2994 }
2995
2996 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
2997 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
2998 if self.is_never && self.break_targets.is_empty() {
3015 if self.in_final_expr && !self.requires_semi {
3016 match e.kind {
3019 ExprKind::DropTemps(e) => self.visit_expr(e),
3020 ExprKind::If(_, then, Some(else_)) => {
3021 self.visit_expr(then);
3022 self.visit_expr(else_);
3023 },
3024 ExprKind::Match(_, arms, _) => {
3025 for arm in arms {
3026 self.visit_expr(arm.body);
3027 }
3028 },
3029 ExprKind::Loop(b, ..) => {
3030 self.push_break_target(e.hir_id);
3031 self.in_final_expr = false;
3032 self.visit_block(b);
3033 self.break_targets.pop();
3034 },
3035 ExprKind::Block(b, _) => {
3036 if b.targeted_by_break {
3037 self.push_break_target(b.hir_id);
3038 self.visit_block(b);
3039 self.break_targets.pop();
3040 } else {
3041 self.visit_block(b);
3042 }
3043 },
3044 _ => {
3045 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
3046 },
3047 }
3048 }
3049 return;
3050 }
3051 match e.kind {
3052 ExprKind::DropTemps(e) => self.visit_expr(e),
3053 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
3054 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
3055 self.in_final_expr = false;
3056 self.visit_expr(e);
3057 self.is_never = true;
3058 },
3059 ExprKind::Break(dest, e) => {
3060 if let Some(e) = e {
3061 self.in_final_expr = false;
3062 self.visit_expr(e);
3063 }
3064 if let Ok(id) = dest.target_id
3065 && let Some((i, target)) = self
3066 .break_targets
3067 .iter_mut()
3068 .enumerate()
3069 .find(|(_, target)| target.id == id)
3070 {
3071 target.unused &= self.is_never;
3072 if i < self.break_targets_for_result_ty as usize {
3073 self.requires_semi = true;
3074 }
3075 }
3076 self.is_never = true;
3077 },
3078 ExprKind::If(cond, then, else_) => {
3079 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3080 self.visit_expr(cond);
3081 self.in_final_expr = in_final_expr;
3082
3083 if self.is_never {
3084 self.visit_expr(then);
3085 if let Some(else_) = else_ {
3086 self.visit_expr(else_);
3087 }
3088 } else {
3089 self.visit_expr(then);
3090 let is_never = mem::replace(&mut self.is_never, false);
3091 if let Some(else_) = else_ {
3092 self.visit_expr(else_);
3093 self.is_never &= is_never;
3094 }
3095 }
3096 },
3097 ExprKind::Match(scrutinee, arms, _) => {
3098 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3099 self.visit_expr(scrutinee);
3100 self.in_final_expr = in_final_expr;
3101
3102 if self.is_never {
3103 for arm in arms {
3104 self.visit_arm(arm);
3105 }
3106 } else {
3107 let mut is_never = true;
3108 for arm in arms {
3109 self.is_never = false;
3110 if let Some(guard) = arm.guard {
3111 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3112 self.visit_expr(guard);
3113 self.in_final_expr = in_final_expr;
3114 self.is_never = false;
3116 }
3117 self.visit_expr(arm.body);
3118 is_never &= self.is_never;
3119 }
3120 self.is_never = is_never;
3121 }
3122 },
3123 ExprKind::Loop(b, _, _, _) => {
3124 self.push_break_target(e.hir_id);
3125 self.in_final_expr = false;
3126 self.visit_block(b);
3127 self.is_never = self.break_targets.pop().unwrap().unused;
3128 },
3129 ExprKind::Block(b, _) => {
3130 if b.targeted_by_break {
3131 self.push_break_target(b.hir_id);
3132 self.visit_block(b);
3133 self.is_never &= self.break_targets.pop().unwrap().unused;
3134 } else {
3135 self.visit_block(b);
3136 }
3137 },
3138 _ => {
3139 self.in_final_expr = false;
3140 walk_expr(self, e);
3141 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3142 },
3143 }
3144 }
3145
3146 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3147 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3148 for s in b.stmts {
3149 self.visit_stmt(s);
3150 }
3151 self.in_final_expr = in_final_expr;
3152 if let Some(e) = b.expr {
3153 self.visit_expr(e);
3154 }
3155 }
3156
3157 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3158 if let Some(e) = l.init {
3159 self.visit_expr(e);
3160 }
3161 if let Some(else_) = l.els {
3162 let is_never = self.is_never;
3163 self.visit_block(else_);
3164 self.is_never = is_never;
3165 }
3166 }
3167
3168 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3169 if let Some(guard) = arm.guard {
3170 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3171 self.visit_expr(guard);
3172 self.in_final_expr = in_final_expr;
3173 }
3174 self.visit_expr(arm.body);
3175 }
3176 }
3177
3178 if cx.typeck_results().expr_ty(e).is_never() {
3179 Some(RequiresSemi::No)
3180 } else if let ExprKind::Block(b, _) = e.kind
3181 && !b.targeted_by_break
3182 && b.expr.is_none()
3183 {
3184 None
3186 } else {
3187 let mut v = V {
3188 cx,
3189 break_targets: Vec::new(),
3190 break_targets_for_result_ty: 0,
3191 in_final_expr: true,
3192 requires_semi: false,
3193 is_never: false,
3194 };
3195 v.visit_expr(e);
3196 v.is_never
3197 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3198 RequiresSemi::Yes
3199 } else {
3200 RequiresSemi::No
3201 })
3202 }
3203}
3204
3205pub fn get_path_from_caller_to_method_type<'tcx>(
3211 tcx: TyCtxt<'tcx>,
3212 from: LocalDefId,
3213 method: DefId,
3214 args: GenericArgsRef<'tcx>,
3215) -> String {
3216 let assoc_item = tcx.associated_item(method);
3217 let def_id = assoc_item.container_id(tcx);
3218 match assoc_item.container {
3219 rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
3220 rustc_ty::AssocItemContainer::Impl => {
3221 let ty = tcx.type_of(def_id).instantiate_identity();
3222 get_path_to_ty(tcx, from, ty, args)
3223 },
3224 }
3225}
3226
3227fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3228 match ty.kind() {
3229 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3230 rustc_ty::Array(..)
3232 | rustc_ty::Dynamic(..)
3233 | rustc_ty::Never
3234 | rustc_ty::RawPtr(_, _)
3235 | rustc_ty::Ref(..)
3236 | rustc_ty::Slice(_)
3237 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3238 _ => ty.to_string(),
3239 }
3240}
3241
3242fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3244 if callee.is_local() {
3246 let callee_path = tcx.def_path(callee);
3247 let caller_path = tcx.def_path(from.to_def_id());
3248 maybe_get_relative_path(&caller_path, &callee_path, 2)
3249 } else {
3250 tcx.def_path_str(callee)
3251 }
3252}
3253
3254fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3267 use itertools::EitherOrBoth::{Both, Left, Right};
3268
3269 let unique_parts = to
3271 .data
3272 .iter()
3273 .zip_longest(from.data.iter())
3274 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3275 .map(|el| match el {
3276 Both(l, r) => Both(l.data, r.data),
3277 Left(l) => Left(l.data),
3278 Right(r) => Right(r.data),
3279 });
3280
3281 let mut go_up_by = 0;
3283 let mut path = Vec::new();
3284 for el in unique_parts {
3285 match el {
3286 Both(l, r) => {
3287 if let DefPathData::TypeNs(sym) = l {
3297 path.push(sym);
3298 }
3299 if let DefPathData::TypeNs(_) = r {
3300 go_up_by += 1;
3301 }
3302 },
3303 Left(DefPathData::TypeNs(sym)) => path.push(sym),
3308 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3313 _ => {},
3314 }
3315 }
3316
3317 if go_up_by > max_super {
3318 join_path_syms(once(kw::Crate).chain(to.data.iter().filter_map(|el| {
3320 if let DefPathData::TypeNs(sym) = el.data {
3321 Some(sym)
3322 } else {
3323 None
3324 }
3325 })))
3326 } else {
3327 join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
3328 }
3329}
3330
3331pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3334 matches!(
3335 cx.tcx.parent_hir_node(id),
3336 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3337 )
3338}
3339
3340pub fn is_block_like(expr: &Expr<'_>) -> bool {
3343 matches!(
3344 expr.kind,
3345 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3346 )
3347}
3348
3349pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3351 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3352 match expr.kind {
3353 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3354 _ if is_block_like(expr) => is_operand,
3355 _ => false,
3356 }
3357 }
3358
3359 contains_block(expr, false)
3360}
3361
3362pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3364 if let Some(parent_expr) = get_parent_expr(cx, expr)
3365 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3366 && receiver.hir_id == expr.hir_id
3367 {
3368 return true;
3369 }
3370 false
3371}
3372
3373pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3376 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3377 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3378 && temporary_ty
3379 .walk()
3380 .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
3381 {
3382 ControlFlow::Break(())
3383 } else {
3384 ControlFlow::Continue(())
3385 }
3386 })
3387 .is_break()
3388}
3389
3390pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3401 let expr_ty_is_adjusted = cx
3402 .typeck_results()
3403 .expr_adjustments(expr)
3404 .iter()
3405 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3407 if expr_ty_is_adjusted {
3408 return true;
3409 }
3410
3411 match expr.kind {
3414 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3415 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3416
3417 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3418 return false;
3419 }
3420
3421 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3422 let mut args_with_ty_param = {
3423 fn_sig
3424 .inputs()
3425 .skip_binder()
3426 .iter()
3427 .skip(self_arg_count)
3428 .zip(args)
3429 .filter_map(|(arg_ty, arg)| {
3430 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3431 Some(arg)
3432 } else {
3433 None
3434 }
3435 })
3436 };
3437 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3438 },
3439 ExprKind::Struct(qpath, _, _) => {
3441 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3442 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3443 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3444 return true;
3446 };
3447 v_def
3448 .fields
3449 .iter()
3450 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3451 } else {
3452 false
3453 }
3454 },
3455 ExprKind::Block(
3457 &Block {
3458 expr: Some(ret_expr), ..
3459 },
3460 _,
3461 )
3462 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3463
3464 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3466 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3468 ExprKind::If(_, then, maybe_else) => {
3470 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3471 },
3472 ExprKind::Match(_, arms, _) => arms
3473 .iter()
3474 .map(|arm| arm.body)
3475 .any(|body| expr_requires_coercion(cx, body)),
3476 _ => false,
3477 }
3478}
3479
3480pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3483 if let Some(hir_id) = path_to_local(expr)
3484 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3485 {
3486 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3487 } else if let ExprKind::Path(p) = &expr.kind
3488 && let Some(mutability) = cx
3489 .qpath_res(p, expr.hir_id)
3490 .opt_def_id()
3491 .and_then(|id| cx.tcx.static_mutability(id))
3492 {
3493 mutability == Mutability::Mut
3494 } else if let ExprKind::Field(parent, _) = expr.kind {
3495 is_mutable(cx, parent)
3496 } else {
3497 true
3498 }
3499}
3500
3501pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3504 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3505 return hir_ty;
3506 };
3507 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3508 && let Some(segment) = path.segments.last()
3509 && segment.ident.name == sym::Option
3510 && let Res::Def(DefKind::Enum, def_id) = segment.res
3511 && def_id == option_def_id
3512 && let [GenericArg::Type(arg_ty)] = segment.args().args
3513 {
3514 hir_ty = arg_ty.as_unambig_ty();
3515 }
3516 hir_ty
3517}
3518
3519pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3522 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3523 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3524 && let ctxt = expr.span.ctxt()
3525 && for_each_expr_without_closures(into_future_arg, |e| {
3526 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3527 })
3528 .is_none()
3529 {
3530 Some(into_future_arg)
3531 } else {
3532 None
3533 }
3534}
3535
3536pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3538 if let ExprKind::Call(fn_expr, []) = &expr.kind
3539 && let ExprKind::Path(qpath) = &fn_expr.kind
3540 && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
3541 {
3542 cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
3543 } else {
3544 false
3545 }
3546}
3547
3548pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3565 let enclosing_body_owner = cx
3566 .tcx
3567 .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
3568 let mut prev_id = expr.hir_id;
3569 let mut skip_until_id = None;
3570 for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
3571 if hir_id == enclosing_body_owner {
3572 return true;
3573 }
3574 if let Some(id) = skip_until_id {
3575 prev_id = hir_id;
3576 if id == hir_id {
3577 skip_until_id = None;
3578 }
3579 continue;
3580 }
3581 match node {
3582 Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
3583 Node::Arm(arm) if arm.body.hir_id == prev_id => {},
3584 Node::Expr(expr) => match expr.kind {
3585 ExprKind::Ret(_) => return true,
3586 ExprKind::If(_, then, opt_else)
3587 if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
3588 ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
3589 ExprKind::Block(block, _) if block.hir_id == prev_id => {},
3590 ExprKind::Break(
3591 Destination {
3592 target_id: Ok(target_id),
3593 ..
3594 },
3595 _,
3596 ) => skip_until_id = Some(target_id),
3597 _ => break,
3598 },
3599 _ => break,
3600 }
3601 prev_id = hir_id;
3602 }
3603
3604 false
3607}
3608
3609pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3612 cx.typeck_results().expr_adjustments(expr).iter().any(|adj| {
3613 matches!(
3614 adj.kind,
3615 Adjust::Deref(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny
3616 )
3617 })
3618}