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 rustc_abi;
29extern crate rustc_ast;
30extern crate rustc_attr_parsing;
31extern crate rustc_const_eval;
32extern crate rustc_data_structures;
33#[allow(unused_extern_crates)]
35extern crate rustc_driver;
36extern crate rustc_errors;
37extern crate rustc_hir;
38extern crate rustc_hir_analysis;
39extern crate rustc_hir_typeck;
40extern crate rustc_index;
41extern crate rustc_infer;
42extern crate rustc_lexer;
43extern crate rustc_lint;
44extern crate rustc_middle;
45extern crate rustc_mir_dataflow;
46extern crate rustc_session;
47extern crate rustc_span;
48extern crate rustc_trait_selection;
49extern crate smallvec;
50
51pub mod ast_utils;
52pub mod attrs;
53mod check_proc_macro;
54pub mod comparisons;
55pub mod consts;
56pub mod diagnostics;
57pub mod eager_or_lazy;
58pub mod higher;
59mod hir_utils;
60pub mod macros;
61pub mod mir;
62pub mod msrvs;
63pub mod numeric_literal;
64pub mod paths;
65pub mod ptr;
66pub mod qualify_min_const_fn;
67pub mod source;
68pub mod str_utils;
69pub mod sugg;
70pub mod sym;
71pub mod ty;
72pub mod usage;
73pub mod visitors;
74
75pub use self::attrs::*;
76pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
77pub use self::hir_utils::{
78 HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over,
79};
80
81use core::mem;
82use core::ops::ControlFlow;
83use std::collections::hash_map::Entry;
84use std::hash::BuildHasherDefault;
85use std::iter::{once, repeat_n};
86use std::sync::{Mutex, MutexGuard, OnceLock};
87
88use itertools::Itertools;
89use rustc_abi::Integer;
90use rustc_ast::ast::{self, LitKind, RangeLimits};
91use rustc_attr_parsing::{AttributeKind, find_attr};
92use rustc_data_structures::fx::FxHashMap;
93use rustc_data_structures::packed::Pu128;
94use rustc_data_structures::unhash::UnhashMap;
95use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
96use rustc_hir::def::{DefKind, Res};
97use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
98use rustc_hir::definitions::{DefPath, DefPathData};
99use rustc_hir::hir_id::{HirIdMap, HirIdSet};
100use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
101use rustc_hir::{
102 self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
103 CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl,
104 ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
105 Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
106 TraitItemKind, TraitRef, TyKind, UnOp, def,
107};
108use rustc_lexer::{TokenKind, tokenize};
109use rustc_lint::{LateContext, Level, Lint, LintContext};
110use rustc_middle::hir::nested_filter;
111use rustc_middle::hir::place::PlaceBase;
112use rustc_middle::lint::LevelAndSource;
113use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
114use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
115use rustc_middle::ty::layout::IntegerExt;
116use rustc_middle::ty::{
117 self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
118 TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
119};
120use rustc_span::hygiene::{ExpnKind, MacroKind};
121use rustc_span::source_map::SourceMap;
122use rustc_span::symbol::{Ident, Symbol, kw};
123use rustc_span::{InnerSpan, Span};
124use source::walk_span_to_context;
125use visitors::{Visitable, for_each_unconsumed_temporary};
126
127use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
128use crate::higher::Range;
129use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
130use crate::visitors::for_each_expr_without_closures;
131
132#[macro_export]
133macro_rules! extract_msrv_attr {
134 () => {
135 fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
136 let sess = rustc_lint::LintContext::sess(cx);
137 self.msrv.check_attributes(sess, attrs);
138 }
139
140 fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::ast::Attribute]) {
141 let sess = rustc_lint::LintContext::sess(cx);
142 self.msrv.check_attributes_post(sess, attrs);
143 }
144 };
145}
146
147pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
170 while let Some(init) = path_to_local(expr)
171 .and_then(|id| find_binding_init(cx, id))
172 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
173 {
174 expr = init;
175 }
176 expr
177}
178
179pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
188 if let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
189 && matches!(pat.kind, PatKind::Binding(BindingMode::NONE, ..))
190 && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
191 {
192 return local.init;
193 }
194 None
195}
196
197pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
201 for (_, node) in cx.tcx.hir_parent_iter(local) {
202 match node {
203 Node::Pat(..) | Node::PatField(..) => {},
204 Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
205 _ => return true,
206 }
207 }
208
209 false
210}
211
212pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
223 debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
224 cx.enclosing_body.is_some_and(|id| {
225 cx.tcx
226 .hir_body_const_context(cx.tcx.hir_body_owner_def_id(id))
227 .is_some()
228 })
229}
230
231pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
238 use rustc_hir::ConstContext::{Const, ConstFn, Static};
239 let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
240 return false;
241 };
242 match ctx {
243 ConstFn => false,
244 Static(_) | Const { inline: _ } => true,
245 }
246}
247
248pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
251 if let Res::Def(DefKind::Ctor(..), id) = res
252 && let Some(lang_id) = cx.tcx.lang_items().get(lang_item)
253 && let Some(id) = cx.tcx.opt_parent(id)
254 {
255 id == lang_id
256 } else {
257 false
258 }
259}
260
261pub fn is_enum_variant_ctor(
263 cx: &LateContext<'_>,
264 enum_item: Symbol,
265 variant_name: Symbol,
266 ctor_call_id: DefId,
267) -> bool {
268 let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
269 return false;
270 };
271
272 let variants = cx.tcx.adt_def(enum_def_id).variants().iter();
273 variants
274 .filter(|variant| variant.name == variant_name)
275 .filter_map(|variant| variant.ctor.as_ref())
276 .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id)
277}
278
279pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
281 let did = match cx.tcx.def_kind(did) {
282 DefKind::Ctor(..) => cx.tcx.parent(did),
283 DefKind::Variant => match cx.tcx.opt_parent(did) {
285 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
286 _ => did,
287 },
288 _ => did,
289 };
290
291 cx.tcx.is_diagnostic_item(item, did)
292}
293
294pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
296 let did = match cx.tcx.def_kind(did) {
297 DefKind::Ctor(..) => cx.tcx.parent(did),
298 DefKind::Variant => match cx.tcx.opt_parent(did) {
300 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
301 _ => did,
302 },
303 _ => did,
304 };
305
306 cx.tcx.lang_items().get(item) == Some(did)
307}
308
309pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
310 matches!(
311 expr.kind,
312 ExprKind::Block(
313 Block {
314 stmts: [],
315 expr: None,
316 ..
317 },
318 _
319 ) | ExprKind::Tup([])
320 )
321}
322
323pub fn is_wild(pat: &Pat<'_>) -> bool {
325 matches!(pat.kind, PatKind::Wild)
326}
327
328pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
330 matches!(
331 arm.pat.kind,
332 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
333 if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone)
334 )
335}
336
337pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
339 match *qpath {
340 QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
341 QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => is_ty_alias(&qpath),
342 _ => false,
343 }
344}
345
346pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
348 if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
349 cx.tcx.trait_of_item(method_id).is_none()
350 } else {
351 false
352 }
353}
354
355pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
357 if let Some(impl_did) = cx.tcx.impl_of_method(def_id)
358 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
359 {
360 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
361 }
362 false
363}
364
365pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
367 if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
368 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
369 }
370 false
371}
372
373pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
375 cx.typeck_results()
376 .type_dependent_def_id(expr.hir_id)
377 .is_some_and(|did| is_diag_trait_item(cx, did, diag_item))
378}
379
380pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
382 if let Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id))
383 && let ItemKind::Impl(imp) = item.kind
384 {
385 imp.of_trait.is_some()
386 } else {
387 false
388 }
389}
390
391pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
401 if let ExprKind::Path(ref qpath) = expr.kind {
402 cx.qpath_res(qpath, expr.hir_id)
403 .opt_def_id()
404 .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item))
405 } else {
406 false
407 }
408}
409
410pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
411 match *path {
412 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
413 QPath::TypeRelative(_, seg) => seg,
414 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
415 }
416}
417
418pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
419 last_path_segment(qpath)
420 .args
421 .map_or(&[][..], |a| a.args)
422 .iter()
423 .filter_map(|a| match a {
424 GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
425 _ => None,
426 })
427}
428
429pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
432 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id))
433}
434
435pub fn is_path_diagnostic_item<'tcx>(
438 cx: &LateContext<'_>,
439 maybe_path: &impl MaybePath<'tcx>,
440 diag_item: Symbol,
441) -> bool {
442 path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
443}
444
445pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
447 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
448 && let Res::Local(id) = path.res
449 {
450 return Some(id);
451 }
452 None
453}
454
455pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
458 path_to_local(expr) == Some(id)
459}
460
461pub trait MaybePath<'hir> {
462 fn hir_id(&self) -> HirId;
463 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
464}
465
466macro_rules! maybe_path {
467 ($ty:ident, $kind:ident) => {
468 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
469 fn hir_id(&self) -> HirId {
470 self.hir_id
471 }
472 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
473 match &self.kind {
474 hir::$kind::Path(qpath) => Some(qpath),
475 _ => None,
476 }
477 }
478 }
479 };
480}
481maybe_path!(Expr, ExprKind);
482impl<'hir> MaybePath<'hir> for Pat<'hir> {
483 fn hir_id(&self) -> HirId {
484 self.hir_id
485 }
486 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
487 match &self.kind {
488 PatKind::Expr(PatExpr {
489 kind: PatExprKind::Path(qpath),
490 ..
491 }) => Some(qpath),
492 _ => None,
493 }
494 }
495}
496maybe_path!(Ty, TyKind);
497
498pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
500 match maybe_path.qpath_opt() {
501 None => Res::Err,
502 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
503 }
504}
505
506pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
508 path_res(cx, maybe_path).opt_def_id()
509}
510
511pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
527 let hir_id = cx.tcx.local_def_id_to_hir_id(def_id);
529 let parent_impl = cx.tcx.hir_get_parent_item(hir_id);
530 if parent_impl != hir::CRATE_OWNER_ID
531 && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_impl.def_id)
532 && let ItemKind::Impl(impl_) = &item.kind
533 {
534 return impl_.of_trait.as_ref();
535 }
536 None
537}
538
539fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
547 let mut result = vec![];
548 let root = loop {
549 match e.kind {
550 ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => {
551 result.push(e);
552 e = ep;
553 },
554 _ => break e,
555 }
556 };
557 result.reverse();
558 (result, root)
559}
560
561pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
563 cx.typeck_results()
564 .expr_adjustments(e)
565 .iter()
566 .find_map(|a| match a.kind {
567 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
568 Adjust::Deref(None) => None,
569 _ => Some(None),
570 })
571 .and_then(|x| x)
572}
573
574pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
577 let (s1, r1) = projection_stack(e1);
578 let (s2, r2) = projection_stack(e2);
579 if !eq_expr_value(cx, r1, r2) {
580 return true;
581 }
582 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
583 return false;
584 }
585
586 for (x1, x2) in s1.iter().zip(s2.iter()) {
587 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
588 return false;
589 }
590
591 match (&x1.kind, &x2.kind) {
592 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
593 if i1 != i2 {
594 return true;
595 }
596 },
597 (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => {
598 if !eq_expr_value(cx, i1, i2) {
599 return false;
600 }
601 },
602 _ => return false,
603 }
604 }
605 false
606}
607
608fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
611 let std_types_symbols = &[
612 sym::Vec,
613 sym::VecDeque,
614 sym::LinkedList,
615 sym::HashMap,
616 sym::BTreeMap,
617 sym::HashSet,
618 sym::BTreeSet,
619 sym::BinaryHeap,
620 ];
621
622 if let QPath::TypeRelative(_, method) = path
623 && method.ident.name == sym::new
624 && let Some(impl_did) = cx.tcx.impl_of_method(def_id)
625 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
626 {
627 return std_types_symbols.iter().any(|&symbol| {
628 cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
629 });
630 }
631 false
632}
633
634pub fn is_default_equivalent_call(
636 cx: &LateContext<'_>,
637 repl_func: &Expr<'_>,
638 whole_call_expr: Option<&Expr<'_>>,
639) -> bool {
640 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
641 && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
642 && (is_diag_trait_item(cx, repl_def_id, sym::Default)
643 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath))
644 {
645 return true;
646 }
647
648 let Some(e) = whole_call_expr else { return false };
651 let Some(default_fn_def_id) = cx.tcx.get_diagnostic_item(sym::default_fn) else {
652 return false;
653 };
654 let Some(ty) = cx.tcx.typeck(e.hir_id.owner.def_id).expr_ty_adjusted_opt(e) else {
655 return false;
656 };
657 let args = rustc_ty::GenericArgs::for_item(cx.tcx, default_fn_def_id, |param, _| {
658 if let rustc_ty::GenericParamDefKind::Lifetime = param.kind {
659 cx.tcx.lifetimes.re_erased.into()
660 } else if param.index == 0 && param.name == kw::SelfUpper {
661 ty.into()
662 } else {
663 param.to_error(cx.tcx)
664 }
665 });
666 let instance = rustc_ty::Instance::try_resolve(cx.tcx, cx.typing_env(), default_fn_def_id, args);
667
668 let Ok(Some(instance)) = instance else { return false };
669 if let rustc_ty::InstanceKind::Item(def) = instance.def
670 && !cx.tcx.is_mir_available(def)
671 {
672 return false;
673 }
674 let ExprKind::Path(ref repl_func_qpath) = repl_func.kind else {
675 return false;
676 };
677 let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() else {
678 return false;
679 };
680
681 let body = cx.tcx.instance_mir(instance.def);
687 for block_data in body.basic_blocks.iter() {
688 if block_data.statements.len() == 1
689 && let StatementKind::Assign(assign) = &block_data.statements[0].kind
690 && assign.0.local == RETURN_PLACE
691 && let Rvalue::Aggregate(kind, _places) = &assign.1
692 && let AggregateKind::Adt(did, variant_index, _, _, _) = &**kind
693 && let def = cx.tcx.adt_def(did)
694 && let variant = &def.variant(*variant_index)
695 && variant.fields.is_empty()
696 && let Some((_, did)) = variant.ctor
697 && did == repl_def_id
698 {
699 return true;
700 } else if block_data.statements.is_empty()
701 && let Some(term) = &block_data.terminator
702 {
703 match &term.kind {
704 TerminatorKind::Call {
705 func: Operand::Constant(c),
706 ..
707 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
708 && *did == repl_def_id =>
709 {
710 return true;
711 },
712 TerminatorKind::TailCall {
713 func: Operand::Constant(c),
714 ..
715 } if let rustc_ty::FnDef(did, _args) = c.ty().kind()
716 && *did == repl_def_id =>
717 {
718 return true;
719 },
720 _ => {},
721 }
722 }
723 }
724 false
725}
726
727pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
731 match &e.kind {
732 ExprKind::Lit(lit) => match lit.node {
733 LitKind::Bool(false) | LitKind::Int(Pu128(0), _) => true,
734 LitKind::Str(s, _) => s.is_empty(),
735 _ => false,
736 },
737 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
738 ExprKind::Repeat(x, len) => {
739 if let ConstArgKind::Anon(anon_const) = len.kind
740 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
741 && let LitKind::Int(v, _) = const_lit.node
742 && v <= 32
743 && is_default_equivalent(cx, x)
744 {
745 true
746 } else {
747 false
748 }
749 },
750 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)),
751 ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
752 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
753 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
754 ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)),
755 _ => false,
756 }
757}
758
759fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
760 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind
761 && seg.ident.name == sym::from
762 {
763 match arg.kind {
764 ExprKind::Lit(hir::Lit {
765 node: LitKind::Str(sym, _),
766 ..
767 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
768 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
769 ExprKind::Repeat(_, len) => {
770 if let ConstArgKind::Anon(anon_const) = len.kind
771 && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind
772 && let LitKind::Int(v, _) = const_lit.node
773 {
774 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
775 }
776 },
777 _ => (),
778 }
779 }
780 false
781}
782
783pub fn can_move_expr_to_closure_no_visit<'tcx>(
815 cx: &LateContext<'tcx>,
816 expr: &'tcx Expr<'_>,
817 loop_ids: &[HirId],
818 ignore_locals: &HirIdSet,
819) -> bool {
820 match expr.kind {
821 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
822 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
823 if loop_ids.contains(&id) =>
824 {
825 true
826 },
827 ExprKind::Break(..)
828 | ExprKind::Continue(_)
829 | ExprKind::Ret(_)
830 | ExprKind::Yield(..)
831 | ExprKind::InlineAsm(_) => false,
832 ExprKind::Field(
835 &Expr {
836 hir_id,
837 kind:
838 ExprKind::Path(QPath::Resolved(
839 _,
840 Path {
841 res: Res::Local(local_id),
842 ..
843 },
844 )),
845 ..
846 },
847 _,
848 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
849 false
851 },
852 _ => true,
853 }
854}
855
856#[derive(Debug, Clone, Copy, PartialEq, Eq)]
858pub enum CaptureKind {
859 Value,
860 Use,
861 Ref(Mutability),
862}
863impl CaptureKind {
864 pub fn is_imm_ref(self) -> bool {
865 self == Self::Ref(Mutability::Not)
866 }
867}
868impl std::ops::BitOr for CaptureKind {
869 type Output = Self;
870 fn bitor(self, rhs: Self) -> Self::Output {
871 match (self, rhs) {
872 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
873 (CaptureKind::Use, _) | (_, CaptureKind::Use) => CaptureKind::Use,
874 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
875 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
876 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
877 }
878 }
879}
880impl std::ops::BitOrAssign for CaptureKind {
881 fn bitor_assign(&mut self, rhs: Self) {
882 *self = *self | rhs;
883 }
884}
885
886pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
892 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
893 let mut capture = CaptureKind::Ref(Mutability::Not);
894 pat.each_binding_or_first(&mut |_, id, span, _| match cx
895 .typeck_results()
896 .extract_binding_mode(cx.sess(), id, span)
897 .0
898 {
899 ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => {
900 capture = CaptureKind::Value;
901 },
902 ByRef::Yes(Mutability::Mut) if capture != CaptureKind::Value => {
903 capture = CaptureKind::Ref(Mutability::Mut);
904 },
905 _ => (),
906 });
907 capture
908 }
909
910 debug_assert!(matches!(
911 e.kind,
912 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
913 ));
914
915 let mut child_id = e.hir_id;
916 let mut capture = CaptureKind::Value;
917 let mut capture_expr_ty = e;
918
919 for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
920 if let [
921 Adjustment {
922 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
923 target,
924 },
925 ref adjust @ ..,
926 ] = *cx
927 .typeck_results()
928 .adjustments()
929 .get(child_id)
930 .map_or(&[][..], |x| &**x)
931 && let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) =
932 *adjust.last().map_or(target, |a| a.target).kind()
933 {
934 return CaptureKind::Ref(mutability);
935 }
936
937 match parent {
938 Node::Expr(e) => match e.kind {
939 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
940 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
941 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
942 return CaptureKind::Ref(Mutability::Mut);
943 },
944 ExprKind::Field(..) => {
945 if capture == CaptureKind::Value {
946 capture_expr_ty = e;
947 }
948 },
949 ExprKind::Let(let_expr) => {
950 let mutability = match pat_capture_kind(cx, let_expr.pat) {
951 CaptureKind::Value | CaptureKind::Use => Mutability::Not,
952 CaptureKind::Ref(m) => m,
953 };
954 return CaptureKind::Ref(mutability);
955 },
956 ExprKind::Match(_, arms, _) => {
957 let mut mutability = Mutability::Not;
958 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
959 match capture {
960 CaptureKind::Value | CaptureKind::Use => break,
961 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
962 CaptureKind::Ref(Mutability::Not) => (),
963 }
964 }
965 return CaptureKind::Ref(mutability);
966 },
967 _ => break,
968 },
969 Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) {
970 CaptureKind::Value | CaptureKind::Use => break,
971 capture @ CaptureKind::Ref(_) => return capture,
972 },
973 _ => break,
974 }
975
976 child_id = parent_id;
977 }
978
979 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
980 CaptureKind::Ref(Mutability::Not)
982 } else {
983 capture
984 }
985}
986
987pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
990 struct V<'cx, 'tcx> {
991 cx: &'cx LateContext<'tcx>,
992 loops: Vec<HirId>,
994 locals: HirIdSet,
996 allow_closure: bool,
998 captures: HirIdMap<CaptureKind>,
1001 }
1002 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1003 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1004 if !self.allow_closure {
1005 return;
1006 }
1007
1008 match e.kind {
1009 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1010 if !self.locals.contains(&l) {
1011 let cap = capture_local_usage(self.cx, e);
1012 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1013 }
1014 },
1015 ExprKind::Closure(closure) => {
1016 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
1017 let local_id = match capture.place.base {
1018 PlaceBase::Local(id) => id,
1019 PlaceBase::Upvar(var) => var.var_path.hir_id,
1020 _ => continue,
1021 };
1022 if !self.locals.contains(&local_id) {
1023 let capture = match capture.info.capture_kind {
1024 UpvarCapture::ByValue => CaptureKind::Value,
1025 UpvarCapture::ByUse => CaptureKind::Use,
1026 UpvarCapture::ByRef(kind) => match kind {
1027 BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
1028 BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
1029 CaptureKind::Ref(Mutability::Mut)
1030 },
1031 },
1032 };
1033 self.captures
1034 .entry(local_id)
1035 .and_modify(|e| *e |= capture)
1036 .or_insert(capture);
1037 }
1038 }
1039 },
1040 ExprKind::Loop(b, ..) => {
1041 self.loops.push(e.hir_id);
1042 self.visit_block(b);
1043 self.loops.pop();
1044 },
1045 _ => {
1046 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1047 walk_expr(self, e);
1048 },
1049 }
1050 }
1051
1052 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1053 p.each_binding_or_first(&mut |_, id, _, _| {
1054 self.locals.insert(id);
1055 });
1056 }
1057 }
1058
1059 let mut v = V {
1060 cx,
1061 loops: Vec::new(),
1062 locals: HirIdSet::default(),
1063 allow_closure: true,
1064 captures: HirIdMap::default(),
1065 };
1066 v.visit_expr(expr);
1067 v.allow_closure.then_some(v.captures)
1068}
1069
1070pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1072
1073pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1076 let mut method_names = Vec::with_capacity(max_depth);
1077 let mut arg_lists = Vec::with_capacity(max_depth);
1078 let mut spans = Vec::with_capacity(max_depth);
1079
1080 let mut current = expr;
1081 for _ in 0..max_depth {
1082 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1083 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1084 break;
1085 }
1086 method_names.push(path.ident.name);
1087 arg_lists.push((*receiver, &**args));
1088 spans.push(path.ident.span);
1089 current = receiver;
1090 } else {
1091 break;
1092 }
1093 }
1094
1095 (method_names, arg_lists, spans)
1096}
1097
1098pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1105 let mut current = expr;
1106 let mut matched = Vec::with_capacity(methods.len());
1107 for method_name in methods.iter().rev() {
1108 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1110 if path.ident.name.as_str() == *method_name {
1111 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1112 return None;
1113 }
1114 matched.push((receiver, args)); current = receiver; } else {
1117 return None;
1118 }
1119 } else {
1120 return None;
1121 }
1122 }
1123 matched.reverse();
1125 Some(matched)
1126}
1127
1128pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1130 cx.tcx
1131 .entry_fn(())
1132 .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1133}
1134
1135pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1137 let parent = cx.tcx.hir_get_parent_item(e.hir_id);
1138 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1139}
1140
1141pub fn parent_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1143 let parent_id = cx.tcx.hir_get_parent_item(expr.hir_id).def_id;
1144 match cx.tcx.hir_node_by_def_id(parent_id) {
1145 Node::Item(item) => item.kind.ident().map(|ident| ident.name),
1146 Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) => Some(ident.name),
1147 _ => None,
1148 }
1149}
1150
1151pub struct ContainsName<'a, 'tcx> {
1152 pub cx: &'a LateContext<'tcx>,
1153 pub name: Symbol,
1154}
1155
1156impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
1157 type Result = ControlFlow<()>;
1158 type NestedFilter = nested_filter::OnlyBodies;
1159
1160 fn visit_name(&mut self, name: Symbol) -> Self::Result {
1161 if self.name == name {
1162 ControlFlow::Break(())
1163 } else {
1164 ControlFlow::Continue(())
1165 }
1166 }
1167
1168 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1169 self.cx.tcx
1170 }
1171}
1172
1173pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1175 let mut cn = ContainsName { cx, name };
1176 cn.visit_expr(expr).is_break()
1177}
1178
1179pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
1181 for_each_expr_without_closures(expr, |e| {
1182 if matches!(e.kind, ExprKind::Ret(..)) {
1183 ControlFlow::Break(())
1184 } else {
1185 ControlFlow::Continue(())
1186 }
1187 })
1188 .is_some()
1189}
1190
1191pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1193 get_parent_expr_for_hir(cx, e.hir_id)
1194}
1195
1196pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
1199 match cx.tcx.parent_hir_node(hir_id) {
1200 Node::Expr(parent) => Some(parent),
1201 _ => None,
1202 }
1203}
1204
1205pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1207 let enclosing_node = cx
1208 .tcx
1209 .hir_get_enclosing_scope(hir_id)
1210 .map(|enclosing_id| cx.tcx.hir_node(enclosing_id));
1211 enclosing_node.and_then(|node| match node {
1212 Node::Block(block) => Some(block),
1213 Node::Item(&Item {
1214 kind: ItemKind::Fn { body: eid, .. },
1215 ..
1216 })
1217 | Node::ImplItem(&ImplItem {
1218 kind: ImplItemKind::Fn(_, eid),
1219 ..
1220 })
1221 | Node::TraitItem(&TraitItem {
1222 kind: TraitItemKind::Fn(_, TraitFn::Provided(eid)),
1223 ..
1224 }) => match cx.tcx.hir_body(eid).value.kind {
1225 ExprKind::Block(block, _) => Some(block),
1226 _ => None,
1227 },
1228 _ => None,
1229 })
1230}
1231
1232pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1234 cx: &LateContext<'tcx>,
1235 expr: &Expr<'_>,
1236) -> Option<&'tcx Expr<'tcx>> {
1237 for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
1238 match node {
1239 Node::Expr(e) => match e.kind {
1240 ExprKind::Closure { .. }
1241 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1242 && subs.as_closure().kind() == ClosureKind::FnOnce => {},
1243
1244 ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
1246 _ => (),
1247 },
1248 Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) | Node::ExprField(_) => (),
1249 _ => break,
1250 }
1251 }
1252 None
1253}
1254
1255pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1257 match tcx.hir_parent_iter(id).next() {
1258 Some((
1259 _,
1260 Node::Item(Item {
1261 kind: ItemKind::Impl(imp),
1262 ..
1263 }),
1264 )) => Some(imp),
1265 _ => None,
1266 }
1267}
1268
1269pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1280 while let ExprKind::Block(
1281 Block {
1282 stmts: [],
1283 expr: Some(inner),
1284 rules: BlockCheckMode::DefaultBlock,
1285 ..
1286 },
1287 _,
1288 ) = expr.kind
1289 {
1290 expr = inner;
1291 }
1292 expr
1293}
1294
1295pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1306 while let ExprKind::Block(
1307 Block {
1308 stmts: [],
1309 expr: Some(inner),
1310 rules: BlockCheckMode::DefaultBlock,
1311 ..
1312 }
1313 | Block {
1314 stmts:
1315 [
1316 Stmt {
1317 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1318 ..
1319 },
1320 ],
1321 expr: None,
1322 rules: BlockCheckMode::DefaultBlock,
1323 ..
1324 },
1325 _,
1326 ) = expr.kind
1327 {
1328 expr = inner;
1329 }
1330 expr
1331}
1332
1333pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1335 let mut iter = tcx.hir_parent_iter(expr.hir_id);
1336 match iter.next() {
1337 Some((
1338 _,
1339 Node::Expr(Expr {
1340 kind: ExprKind::If(_, _, Some(else_expr)),
1341 ..
1342 }),
1343 )) => else_expr.hir_id == expr.hir_id,
1344 _ => false,
1345 }
1346}
1347
1348pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1351 let mut child_id = expr.hir_id;
1352 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1353 if let Node::LetStmt(LetStmt {
1354 init: Some(init),
1355 els: Some(els),
1356 ..
1357 }) = node
1358 && (init.hir_id == child_id || els.hir_id == child_id)
1359 {
1360 return true;
1361 }
1362
1363 child_id = parent_id;
1364 }
1365
1366 false
1367}
1368
1369pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1371 let mut child_id = expr.hir_id;
1372 for (parent_id, node) in tcx.hir_parent_iter(child_id) {
1373 if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
1374 && els.hir_id == child_id
1375 {
1376 return true;
1377 }
1378
1379 child_id = parent_id;
1380 }
1381
1382 false
1383}
1384
1385pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
1400 let ty = cx.typeck_results().expr_ty(expr);
1401 if let Some(Range { start, end, limits }) = Range::hir(expr) {
1402 let start_is_none_or_min = start.is_none_or(|start| {
1403 if let rustc_ty::Adt(_, subst) = ty.kind()
1404 && let bnd_ty = subst.type_at(0)
1405 && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx)
1406 && let Some(min_const) = mir_to_const(cx.tcx, min_const)
1407 && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
1408 {
1409 start_const == min_const
1410 } else {
1411 false
1412 }
1413 });
1414 let end_is_none_or_max = end.is_none_or(|end| match limits {
1415 RangeLimits::Closed => {
1416 if let rustc_ty::Adt(_, subst) = ty.kind()
1417 && let bnd_ty = subst.type_at(0)
1418 && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx)
1419 && let Some(max_const) = mir_to_const(cx.tcx, max_const)
1420 && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
1421 {
1422 end_const == max_const
1423 } else {
1424 false
1425 }
1426 },
1427 RangeLimits::HalfOpen => {
1428 if let Some(container_path) = container_path
1429 && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
1430 && name.ident.name == sym::len
1431 && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
1432 {
1433 container_path.res == path.res
1434 } else {
1435 false
1436 }
1437 },
1438 });
1439 return start_is_none_or_min && end_is_none_or_max;
1440 }
1441 false
1442}
1443
1444pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1447 if is_integer_literal(e, value) {
1448 return true;
1449 }
1450 let enclosing_body = cx.tcx.hir_enclosing_body_owner(e.hir_id);
1451 if let Some(Constant::Int(v)) =
1452 ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
1453 {
1454 return value == v;
1455 }
1456 false
1457}
1458
1459pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1461 if let ExprKind::Lit(spanned) = expr.kind
1463 && let LitKind::Int(v, _) = spanned.node
1464 {
1465 return v == value;
1466 }
1467 false
1468}
1469
1470pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
1472 if let ExprKind::Lit(spanned) = expr.kind
1473 && let LitKind::Float(v, _) = spanned.node
1474 {
1475 v.as_str().parse() == Ok(value)
1476 } else {
1477 false
1478 }
1479}
1480
1481pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1489 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1490}
1491
1492#[must_use]
1496pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
1497 loop {
1498 if span.from_expansion() {
1499 let data = span.ctxt().outer_expn_data();
1500 let new_span = data.call_site;
1501
1502 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1503 && mac_name.as_str() == name
1504 {
1505 return Some(new_span);
1506 }
1507
1508 span = new_span;
1509 } else {
1510 return None;
1511 }
1512 }
1513}
1514
1515#[must_use]
1526pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
1527 if span.from_expansion() {
1528 let data = span.ctxt().outer_expn_data();
1529 let new_span = data.call_site;
1530
1531 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind
1532 && mac_name.as_str() == name
1533 {
1534 return Some(new_span);
1535 }
1536 }
1537
1538 None
1539}
1540
1541pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId) -> Ty<'tcx> {
1543 let ret_ty = cx.tcx.fn_sig(fn_def_id).instantiate_identity().output();
1544 cx.tcx.instantiate_bound_regions_with_erased(ret_ty)
1545}
1546
1547pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: OwnerId, nth: usize) -> Ty<'tcx> {
1549 let arg = cx.tcx.fn_sig(fn_def_id).instantiate_identity().input(nth);
1550 cx.tcx.instantiate_bound_regions_with_erased(arg)
1551}
1552
1553pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1555 if let ExprKind::Call(fun, _) = expr.kind
1556 && let ExprKind::Path(ref qp) = fun.kind
1557 {
1558 let res = cx.qpath_res(qp, fun.hir_id);
1559 return match res {
1560 Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1561 Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1562 _ => false,
1563 };
1564 }
1565 false
1566}
1567
1568pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1571 fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1572 matches!(
1573 cx.qpath_res(qpath, id),
1574 Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
1575 )
1576 }
1577
1578 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1579 i.into_iter().any(|pat| is_refutable(cx, pat))
1580 }
1581
1582 match pat.kind {
1583 PatKind::Missing => unreachable!(),
1584 PatKind::Wild | PatKind::Never => false, PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
1586 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1587 PatKind::Expr(PatExpr {
1588 kind: PatExprKind::Path(qpath),
1589 hir_id,
1590 ..
1591 }) => is_enum_variant(cx, qpath, *hir_id),
1592 PatKind::Or(pats) => {
1593 are_refutable(cx, pats)
1595 },
1596 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1597 PatKind::Struct(ref qpath, fields, _) => {
1598 is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1599 },
1600 PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
1601 PatKind::Slice(head, middle, tail) => {
1602 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1603 rustc_ty::Slice(..) => {
1604 !head.is_empty() || middle.is_none() || !tail.is_empty()
1606 },
1607 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1608 _ => {
1609 true
1611 },
1612 }
1613 },
1614 PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
1615 }
1616}
1617
1618pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1621 if let PatKind::Or(pats) = pat.kind {
1622 pats.iter().for_each(f);
1623 } else {
1624 f(pat);
1625 }
1626}
1627
1628pub fn is_self(slf: &Param<'_>) -> bool {
1629 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1630 name.name == kw::SelfLower
1631 } else {
1632 false
1633 }
1634}
1635
1636pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1637 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind
1638 && let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res
1639 {
1640 return true;
1641 }
1642 false
1643}
1644
1645pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1646 (0..decl.inputs.len()).map(move |i| &body.params[i])
1647}
1648
1649pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1652 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1653 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind
1654 && ddpos.as_opt_usize().is_none()
1655 && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk)
1656 && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind
1657 && path_to_local_id(arm.body, hir_id)
1658 {
1659 return true;
1660 }
1661 false
1662 }
1663
1664 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1665 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1666 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1667 } else {
1668 false
1669 }
1670 }
1671
1672 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1673 if let MatchSource::TryDesugar(_) = *source {
1675 return Some(expr);
1676 }
1677
1678 if arms.len() == 2
1679 && arms[0].guard.is_none()
1680 && arms[1].guard.is_none()
1681 && ((is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0])))
1682 {
1683 return Some(expr);
1684 }
1685 }
1686
1687 None
1688}
1689
1690pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
1700 let mut suppress_lint = false;
1701
1702 for id in ids {
1703 let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
1704 if let Some(expectation) = lint_id {
1705 cx.fulfill_expectation(expectation);
1706 }
1707
1708 match level {
1709 Level::Allow | Level::Expect => suppress_lint = true,
1710 Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
1711 }
1712 }
1713
1714 suppress_lint
1715}
1716
1717pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1725 cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
1726}
1727
1728pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1729 while let PatKind::Ref(subpat, _) = pat.kind {
1730 pat = subpat;
1731 }
1732 pat
1733}
1734
1735pub fn int_bits(tcx: TyCtxt<'_>, ity: IntTy) -> u64 {
1736 Integer::from_int_ty(&tcx, ity).size().bits()
1737}
1738
1739#[expect(clippy::cast_possible_wrap)]
1740pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: IntTy) -> i128 {
1742 let amt = 128 - int_bits(tcx, ity);
1743 ((u as i128) << amt) >> amt
1744}
1745
1746#[expect(clippy::cast_sign_loss)]
1747pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: IntTy) -> u128 {
1749 let amt = 128 - int_bits(tcx, ity);
1750 ((u as u128) << amt) >> amt
1751}
1752
1753pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 {
1755 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1756 let amt = 128 - bits;
1757 (u << amt) >> amt
1758}
1759
1760pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
1761 attrs.iter().any(|attr| attr.has_name(symbol))
1762}
1763
1764pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1765 find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr(..))
1766}
1767
1768pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1769 let mut prev_enclosing_node = None;
1770 let mut enclosing_node = node;
1771 while Some(enclosing_node) != prev_enclosing_node {
1772 if has_attr(tcx.hir_attrs(enclosing_node), symbol) {
1773 return true;
1774 }
1775 prev_enclosing_node = Some(enclosing_node);
1776 enclosing_node = tcx.hir_get_parent_item(enclosing_node).into();
1777 }
1778
1779 false
1780}
1781
1782pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
1785 tcx.hir_parent_owner_iter(id)
1786 .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
1787 .any(|(id, _)| {
1788 has_attr(
1789 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
1790 sym::automatically_derived,
1791 )
1792 })
1793}
1794
1795pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
1797 let path = cx.get_def_path(did);
1798 path.first().is_some_and(|s| *s == sym::libc) && path.last().is_some_and(|s| s.as_str() == name)
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| cx.tcx.has_attr(did, sym::must_use))
1892}
1893
1894fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1903 fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
1904 if cx
1905 .typeck_results()
1906 .pat_binding_modes()
1907 .get(pat.hir_id)
1908 .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_)))
1909 {
1910 return false;
1914 }
1915
1916 match (pat.kind, expr.kind) {
1917 (PatKind::Binding(_, id, _, _), _) => {
1918 path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty()
1919 },
1920 (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
1921 if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
1922 {
1923 pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr))
1924 },
1925 _ => false,
1926 }
1927 }
1928
1929 let [param] = func.params else {
1930 return false;
1931 };
1932
1933 let mut expr = func.value;
1934 loop {
1935 match expr.kind {
1936 ExprKind::Block(
1937 &Block {
1938 stmts: [],
1939 expr: Some(e),
1940 ..
1941 },
1942 _,
1943 )
1944 | ExprKind::Ret(Some(e)) => expr = e,
1945 ExprKind::Block(
1946 &Block {
1947 stmts: [stmt],
1948 expr: None,
1949 ..
1950 },
1951 _,
1952 ) => {
1953 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind
1954 && let ExprKind::Ret(Some(ret_val)) = e.kind
1955 {
1956 expr = ret_val;
1957 } else {
1958 return false;
1959 }
1960 },
1961 _ => return check_pat(cx, param.pat, expr),
1962 }
1963 }
1964}
1965
1966pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1971 match expr.kind {
1972 ExprKind::Closure(&Closure { body, fn_decl, .. })
1973 if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
1974 {
1975 is_body_identity_function(cx, cx.tcx.hir_body(body))
1976 },
1977 ExprKind::Path(QPath::Resolved(_, path))
1978 if path.segments.iter().all(|seg| seg.infer_args)
1979 && let Some(did) = path.res.opt_def_id() =>
1980 {
1981 cx.tcx.is_diagnostic_item(sym::convert_identity, did)
1982 },
1983 _ => false,
1984 }
1985}
1986
1987pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1996 match expr.kind {
1997 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)),
1998 _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
1999 }
2000}
2001
2002pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2005 let mut child_id = expr.hir_id;
2006 let mut iter = tcx.hir_parent_iter(child_id);
2007 loop {
2008 match iter.next() {
2009 None => break None,
2010 Some((id, Node::Block(_))) => child_id = id,
2011 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2012 Some((_, Node::Expr(expr))) => match expr.kind {
2013 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2014 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2015 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2016 _ => break Some((Node::Expr(expr), child_id)),
2017 },
2018 Some((_, node)) => break Some((node, child_id)),
2019 }
2020 }
2021}
2022
2023pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2025 !matches!(
2026 get_expr_use_or_unification_node(tcx, expr),
2027 None | Some((
2028 Node::Stmt(Stmt {
2029 kind: StmtKind::Expr(_)
2030 | StmtKind::Semi(_)
2031 | StmtKind::Let(LetStmt {
2032 pat: Pat {
2033 kind: PatKind::Wild,
2034 ..
2035 },
2036 ..
2037 }),
2038 ..
2039 }),
2040 _
2041 ))
2042 )
2043}
2044
2045pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2047 matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
2048}
2049
2050pub fn is_expr_temporary_value(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2054 !expr.is_place_expr(|base| {
2055 cx.typeck_results()
2056 .adjustments()
2057 .get(base.hir_id)
2058 .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
2059 })
2060}
2061
2062pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2063 if !is_no_std_crate(cx) {
2064 Some("std")
2065 } else if !is_no_core_crate(cx) {
2066 Some("core")
2067 } else {
2068 None
2069 }
2070}
2071
2072pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2073 cx.tcx
2074 .hir_attrs(hir::CRATE_HIR_ID)
2075 .iter()
2076 .any(|attr| attr.has_name(sym::no_std))
2077}
2078
2079pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2080 cx.tcx
2081 .hir_attrs(hir::CRATE_HIR_ID)
2082 .iter()
2083 .any(|attr| attr.has_name(sym::no_core))
2084}
2085
2086pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2096 if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
2097 matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }))
2098 } else {
2099 false
2100 }
2101}
2102
2103pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2113 use rustc_trait_selection::traits;
2114 let predicates = cx
2115 .tcx
2116 .predicates_of(did)
2117 .predicates
2118 .iter()
2119 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2120 traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
2121}
2122
2123pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2125 fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
2126}
2127
2128pub fn fn_def_id_with_node_args<'tcx>(
2131 cx: &LateContext<'tcx>,
2132 expr: &Expr<'_>,
2133) -> Option<(DefId, GenericArgsRef<'tcx>)> {
2134 let typeck = cx.typeck_results();
2135 match &expr.kind {
2136 ExprKind::MethodCall(..) => Some((
2137 typeck.type_dependent_def_id(expr.hir_id)?,
2138 typeck.node_args(expr.hir_id),
2139 )),
2140 ExprKind::Call(
2141 Expr {
2142 kind: ExprKind::Path(qpath),
2143 hir_id: path_hir_id,
2144 ..
2145 },
2146 ..,
2147 ) => {
2148 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2151 typeck.qpath_res(qpath, *path_hir_id)
2152 {
2153 Some((id, typeck.node_args(*path_hir_id)))
2154 } else {
2155 None
2156 }
2157 },
2158 _ => None,
2159 }
2160}
2161
2162pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2167 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2168 let expr_kind = expr_type.kind();
2169 let is_primitive = match expr_kind {
2170 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2171 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2172 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2173 is_recursively_primitive_type(*element_type)
2174 } else {
2175 unreachable!()
2176 }
2177 },
2178 _ => false,
2179 };
2180
2181 if is_primitive {
2182 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2185 rustc_ty::Slice(..) => return Some("slice".into()),
2186 rustc_ty::Array(..) => return Some("array".into()),
2187 rustc_ty::Tuple(..) => return Some("tuple".into()),
2188 _ => {
2189 let refs_peeled = expr_type.peel_refs();
2192 return Some(refs_peeled.walk().last().unwrap().to_string());
2193 },
2194 }
2195 }
2196 None
2197}
2198
2199pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<(&T, &T)>
2206where
2207 Hash: FnMut(&T) -> u64,
2208 Eq: FnMut(&T, &T) -> bool,
2209{
2210 match exprs {
2211 [a, b] if eq(a, b) => return vec![(a, b)],
2212 _ if exprs.len() <= 2 => return vec![],
2213 _ => {},
2214 }
2215
2216 let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
2217
2218 let mut map: UnhashMap<u64, Vec<&_>> =
2219 UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
2220
2221 for expr in exprs {
2222 match map.entry(hash(expr)) {
2223 Entry::Occupied(mut o) => {
2224 for o in o.get() {
2225 if eq(o, expr) {
2226 match_expr_list.push((o, expr));
2227 }
2228 }
2229 o.get_mut().push(expr);
2230 },
2231 Entry::Vacant(v) => {
2232 v.insert(vec![expr]);
2233 },
2234 }
2235 }
2236
2237 match_expr_list
2238}
2239
2240pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2243 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2244 if let PatKind::Ref(pat, _) = pat.kind {
2245 peel(pat, count + 1)
2246 } else {
2247 (pat, count)
2248 }
2249 }
2250 peel(pat, 0)
2251}
2252
2253pub fn peel_hir_expr_while<'tcx>(
2255 mut expr: &'tcx Expr<'tcx>,
2256 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2257) -> &'tcx Expr<'tcx> {
2258 while let Some(e) = f(expr) {
2259 expr = e;
2260 }
2261 expr
2262}
2263
2264pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2267 let mut remaining = count;
2268 let e = peel_hir_expr_while(expr, |e| match e.kind {
2269 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2270 remaining -= 1;
2271 Some(e)
2272 },
2273 _ => None,
2274 });
2275 (e, count - remaining)
2276}
2277
2278pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2281 let mut count: usize = 0;
2282 let mut curr_expr = expr;
2283 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2284 count = count.wrapping_add(1);
2285 curr_expr = local_expr;
2286 }
2287 (curr_expr, count)
2288}
2289
2290pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2293 let mut count = 0;
2294 let e = peel_hir_expr_while(expr, |e| match e.kind {
2295 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2296 count += 1;
2297 Some(e)
2298 },
2299 _ => None,
2300 });
2301 (e, count)
2302}
2303
2304pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2307 let mut count = 0;
2308 loop {
2309 match &ty.kind {
2310 TyKind::Ref(_, ref_ty) => {
2311 ty = ref_ty.ty;
2312 count += 1;
2313 },
2314 _ => break (ty, count),
2315 }
2316 }
2317}
2318
2319pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
2322 let mut count = 0;
2323 while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
2324 ty = *dest_ty;
2325 count += 1;
2326 }
2327 (ty, count)
2328}
2329
2330pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2333 loop {
2334 match expr.kind {
2335 ExprKind::AddrOf(_, _, e) => expr = e,
2336 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2337 _ => break,
2338 }
2339 }
2340 expr
2341}
2342
2343pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2344 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2345 && let Res::Def(_, def_id) = path.res
2346 {
2347 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2348 }
2349 false
2350}
2351
2352static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
2353
2354fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&[Symbol]) -> bool) -> bool {
2357 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2358 let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
2359 let value = map.entry(module);
2360 match value {
2361 Entry::Occupied(entry) => f(entry.get()),
2362 Entry::Vacant(entry) => {
2363 let mut names = Vec::new();
2364 for id in tcx.hir_module_free_items(module) {
2365 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2366 && let item = tcx.hir_item(id)
2367 && let ItemKind::Const(ident, ty, _generics, _body) = item.kind
2368 && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
2369 && let Res::Def(DefKind::Struct, _) = path.res
2371 {
2372 let has_test_marker = tcx
2373 .hir_attrs(item.hir_id())
2374 .iter()
2375 .any(|a| a.has_name(sym::rustc_test_marker));
2376 if has_test_marker {
2377 names.push(ident.name);
2378 }
2379 }
2380 }
2381 names.sort_unstable();
2382 f(entry.insert(names))
2383 },
2384 }
2385}
2386
2387pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
2391 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2392 let node = tcx.hir_node(id);
2393 once((id, node))
2394 .chain(tcx.hir_parent_iter(id))
2395 .any(|(_id, node)| {
2398 if let Node::Item(item) = node
2399 && let ItemKind::Fn { ident, .. } = item.kind
2400 {
2401 return names.binary_search(&ident.name).is_ok();
2404 }
2405 false
2406 })
2407 })
2408}
2409
2410pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
2417 let id = tcx.local_def_id_to_hir_id(fn_def_id);
2418 if let Node::Item(item) = tcx.hir_node(id)
2419 && let ItemKind::Fn { ident, .. } = item.kind
2420 {
2421 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2422 names.binary_search(&ident.name).is_ok()
2423 })
2424 } else {
2425 false
2426 }
2427}
2428
2429pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2434 tcx.hir_attrs(id).iter().any(|attr| {
2435 if attr.has_name(sym::cfg_trace)
2436 && let Some(items) = attr.meta_item_list()
2437 && let [item] = &*items
2438 && item.has_name(sym::test)
2439 {
2440 true
2441 } else {
2442 false
2443 }
2444 })
2445}
2446
2447pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
2449 tcx.hir_parent_id_iter(id).any(|parent_id| is_cfg_test(tcx, parent_id))
2450}
2451
2452pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
2454 is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
2455}
2456
2457pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2459 tcx.has_attr(def_id, sym::cfg_trace)
2460 || tcx
2461 .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
2462 .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
2463 .any(|attr| attr.has_name(sym::cfg_trace))
2464}
2465
2466pub fn walk_to_expr_usage<'tcx, T>(
2477 cx: &LateContext<'tcx>,
2478 e: &Expr<'tcx>,
2479 mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
2480) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
2481 let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
2482 let mut child_id = e.hir_id;
2483
2484 while let Some((parent_id, parent)) = iter.next() {
2485 if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
2486 return Some(ControlFlow::Break(x));
2487 }
2488 let parent_expr = match parent {
2489 Node::Expr(e) => e,
2490 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2491 child_id = parent_id;
2492 continue;
2493 },
2494 Node::Arm(a) if a.body.hir_id == child_id => {
2495 child_id = parent_id;
2496 continue;
2497 },
2498 _ => return Some(ControlFlow::Continue((parent, child_id))),
2499 };
2500 match parent_expr.kind {
2501 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2502 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2503 child_id = id;
2504 iter = cx.tcx.hir_parent_iter(id);
2505 },
2506 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
2507 _ => return Some(ControlFlow::Continue((parent, child_id))),
2508 }
2509 }
2510 debug_assert!(false, "no parent node found for `{child_id:?}`");
2511 None
2512}
2513
2514#[derive(Clone, Copy)]
2516pub enum DefinedTy<'tcx> {
2517 Hir(&'tcx hir::Ty<'tcx>),
2519 Mir {
2527 def_site_def_id: Option<DefId>,
2528 ty: Binder<'tcx, Ty<'tcx>>,
2529 },
2530}
2531
2532pub struct ExprUseCtxt<'tcx> {
2534 pub node: Node<'tcx>,
2536 pub child_id: HirId,
2538 pub adjustments: &'tcx [Adjustment<'tcx>],
2540 pub is_ty_unified: bool,
2542 pub moved_before_use: bool,
2544 pub same_ctxt: bool,
2546}
2547impl<'tcx> ExprUseCtxt<'tcx> {
2548 pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
2549 match self.node {
2550 Node::LetStmt(l) => ExprUseNode::LetStmt(l),
2551 Node::ExprField(field) => ExprUseNode::Field(field),
2552
2553 Node::Item(&Item {
2554 kind: ItemKind::Static(..) | ItemKind::Const(..),
2555 owner_id,
2556 ..
2557 })
2558 | Node::TraitItem(&TraitItem {
2559 kind: TraitItemKind::Const(..),
2560 owner_id,
2561 ..
2562 })
2563 | Node::ImplItem(&ImplItem {
2564 kind: ImplItemKind::Const(..),
2565 owner_id,
2566 ..
2567 }) => ExprUseNode::ConstStatic(owner_id),
2568
2569 Node::Item(&Item {
2570 kind: ItemKind::Fn { .. },
2571 owner_id,
2572 ..
2573 })
2574 | Node::TraitItem(&TraitItem {
2575 kind: TraitItemKind::Fn(..),
2576 owner_id,
2577 ..
2578 })
2579 | Node::ImplItem(&ImplItem {
2580 kind: ImplItemKind::Fn(..),
2581 owner_id,
2582 ..
2583 }) => ExprUseNode::Return(owner_id),
2584
2585 Node::Expr(use_expr) => match use_expr.kind {
2586 ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
2587 def_id: cx.tcx.hir_body_owner_def_id(cx.enclosing_body.unwrap()),
2588 }),
2589
2590 ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
2591 ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == self.child_id) {
2592 Some(i) => ExprUseNode::FnArg(func, i),
2593 None => ExprUseNode::Callee,
2594 },
2595 ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
2596 use_expr.hir_id,
2597 name.args,
2598 args.iter()
2599 .position(|arg| arg.hir_id == self.child_id)
2600 .map_or(0, |i| i + 1),
2601 ),
2602 ExprKind::Field(_, name) => ExprUseNode::FieldAccess(name),
2603 ExprKind::AddrOf(kind, mutbl, _) => ExprUseNode::AddrOf(kind, mutbl),
2604 _ => ExprUseNode::Other,
2605 },
2606 _ => ExprUseNode::Other,
2607 }
2608 }
2609}
2610
2611pub enum ExprUseNode<'tcx> {
2613 LetStmt(&'tcx LetStmt<'tcx>),
2615 ConstStatic(OwnerId),
2617 Return(OwnerId),
2619 Field(&'tcx ExprField<'tcx>),
2621 FnArg(&'tcx Expr<'tcx>, usize),
2623 MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
2625 Callee,
2627 FieldAccess(Ident),
2629 AddrOf(ast::BorrowKind, Mutability),
2631 Other,
2632}
2633impl<'tcx> ExprUseNode<'tcx> {
2634 pub fn is_return(&self) -> bool {
2636 matches!(self, Self::Return(_))
2637 }
2638
2639 pub fn is_recv(&self) -> bool {
2641 matches!(self, Self::MethodArg(_, _, 0))
2642 }
2643
2644 pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
2646 match *self {
2647 Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
2648 Self::ConstStatic(id) => Some(DefinedTy::Mir {
2649 def_site_def_id: Some(id.def_id.to_def_id()),
2650 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
2651 }),
2652 Self::Return(id) => {
2653 if let Node::Expr(Expr {
2654 kind: ExprKind::Closure(c),
2655 ..
2656 }) = cx.tcx.hir_node_by_def_id(id.def_id)
2657 {
2658 match c.fn_decl.output {
2659 FnRetTy::DefaultReturn(_) => None,
2660 FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
2661 }
2662 } else {
2663 let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
2664 Some(DefinedTy::Mir {
2665 def_site_def_id: Some(id.def_id.to_def_id()),
2666 ty,
2667 })
2668 }
2669 },
2670 Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
2671 Some(Expr {
2672 hir_id,
2673 kind: ExprKind::Struct(path, ..),
2674 ..
2675 }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
2676 .and_then(|(adt, variant)| {
2677 variant
2678 .fields
2679 .iter()
2680 .find(|f| f.name == field.ident.name)
2681 .map(|f| (adt, f))
2682 })
2683 .map(|(adt, field_def)| DefinedTy::Mir {
2684 def_site_def_id: Some(adt.did()),
2685 ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
2686 }),
2687 _ => None,
2688 },
2689 Self::FnArg(callee, i) => {
2690 let sig = expr_sig(cx, callee)?;
2691 let (hir_ty, ty) = sig.input_with_hir(i)?;
2692 Some(match hir_ty {
2693 Some(hir_ty) => DefinedTy::Hir(hir_ty),
2694 None => DefinedTy::Mir {
2695 def_site_def_id: sig.predicates_id(),
2696 ty,
2697 },
2698 })
2699 },
2700 Self::MethodArg(id, _, i) => {
2701 let id = cx.typeck_results().type_dependent_def_id(id)?;
2702 let sig = cx.tcx.fn_sig(id).skip_binder();
2703 Some(DefinedTy::Mir {
2704 def_site_def_id: Some(id),
2705 ty: sig.input(i),
2706 })
2707 },
2708 Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
2709 }
2710 }
2711}
2712
2713pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2715 let mut adjustments = [].as_slice();
2716 let mut is_ty_unified = false;
2717 let mut moved_before_use = false;
2718 let mut same_ctxt = true;
2719 let ctxt = e.span.ctxt();
2720 let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
2721 if adjustments.is_empty()
2722 && let Node::Expr(e) = cx.tcx.hir_node(child_id)
2723 {
2724 adjustments = cx.typeck_results().expr_adjustments(e);
2725 }
2726 same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
2727 if let Node::Expr(e) = parent {
2728 match e.kind {
2729 ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
2730 is_ty_unified = true;
2731 moved_before_use = true;
2732 },
2733 ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
2734 is_ty_unified = true;
2735 moved_before_use = true;
2736 },
2737 ExprKind::Block(..) => moved_before_use = true,
2738 _ => {},
2739 }
2740 }
2741 ControlFlow::Continue(())
2742 });
2743 match node {
2744 Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
2745 node,
2746 child_id,
2747 adjustments,
2748 is_ty_unified,
2749 moved_before_use,
2750 same_ctxt,
2751 },
2752 #[allow(unreachable_patterns)]
2753 Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
2754 None => ExprUseCtxt {
2755 node: Node::Crate(cx.tcx.hir_root_module()),
2756 child_id: HirId::INVALID,
2757 adjustments: &[],
2758 is_ty_unified: true,
2759 moved_before_use: true,
2760 same_ctxt: false,
2761 },
2762 }
2763}
2764
2765pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
2767 let mut pos = 0;
2768 tokenize(s).map(move |t| {
2769 let end = pos + t.len;
2770 let range = pos as usize..end as usize;
2771 let inner = InnerSpan::new(range.start, range.end);
2772 pos = end;
2773 (t.kind, s.get(range).unwrap_or_default(), inner)
2774 })
2775}
2776
2777pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2780 let Ok(snippet) = sm.span_to_snippet(span) else {
2781 return false;
2782 };
2783 return tokenize(&snippet).any(|token| {
2784 matches!(
2785 token.kind,
2786 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2787 )
2788 });
2789}
2790
2791pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2795 span_extract_comments(sm, span).join("\n")
2796}
2797
2798pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec<String> {
2802 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2803 tokenize_with_text(&snippet)
2804 .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
2805 .map(|(_, s, _)| s.to_string())
2806 .collect::<Vec<_>>()
2807}
2808
2809pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2810 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2811}
2812
2813pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
2838 cx: &LateContext<'_>,
2839 pat: &'a Pat<'hir>,
2840 else_body: &Expr<'_>,
2841) -> Option<&'a Pat<'hir>> {
2842 if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
2843 && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome)
2844 && !is_refutable(cx, inner_pat)
2845 && let else_body = peel_blocks(else_body)
2846 && let ExprKind::Ret(Some(ret_val)) = else_body.kind
2847 && let ExprKind::Path(ret_path) = ret_val.kind
2848 && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone)
2849 {
2850 Some(inner_pat)
2851 } else {
2852 None
2853 }
2854}
2855
2856macro_rules! op_utils {
2857 ($($name:ident $assign:ident)*) => {
2858 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2860
2861 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2863
2864 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2866 match kind {
2867 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2868 _ => None,
2869 }
2870 }
2871 };
2872}
2873
2874op_utils! {
2875 Add AddAssign
2876 Sub SubAssign
2877 Mul MulAssign
2878 Div DivAssign
2879 Rem RemAssign
2880 BitXor BitXorAssign
2881 BitAnd BitAndAssign
2882 BitOr BitOrAssign
2883 Shl ShlAssign
2884 Shr ShrAssign
2885}
2886
2887pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2890 match *pat {
2891 PatKind::Wild => true,
2892 PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2893 !visitors::is_local_used(cx, body, id)
2894 },
2895 _ => false,
2896 }
2897}
2898
2899#[derive(Clone, Copy)]
2900pub enum RequiresSemi {
2901 Yes,
2902 No,
2903}
2904impl RequiresSemi {
2905 pub fn requires_semi(self) -> bool {
2906 matches!(self, Self::Yes)
2907 }
2908}
2909
2910#[expect(clippy::too_many_lines)]
2913pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<RequiresSemi> {
2914 struct BreakTarget {
2915 id: HirId,
2916 unused: bool,
2917 }
2918
2919 struct V<'cx, 'tcx> {
2920 cx: &'cx LateContext<'tcx>,
2921 break_targets: Vec<BreakTarget>,
2922 break_targets_for_result_ty: u32,
2923 in_final_expr: bool,
2924 requires_semi: bool,
2925 is_never: bool,
2926 }
2927
2928 impl V<'_, '_> {
2929 fn push_break_target(&mut self, id: HirId) {
2930 self.break_targets.push(BreakTarget { id, unused: true });
2931 self.break_targets_for_result_ty += u32::from(self.in_final_expr);
2932 }
2933 }
2934
2935 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
2936 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
2937 if self.is_never && self.break_targets.is_empty() {
2954 if self.in_final_expr && !self.requires_semi {
2955 match e.kind {
2958 ExprKind::DropTemps(e) => self.visit_expr(e),
2959 ExprKind::If(_, then, Some(else_)) => {
2960 self.visit_expr(then);
2961 self.visit_expr(else_);
2962 },
2963 ExprKind::Match(_, arms, _) => {
2964 for arm in arms {
2965 self.visit_expr(arm.body);
2966 }
2967 },
2968 ExprKind::Loop(b, ..) => {
2969 self.push_break_target(e.hir_id);
2970 self.in_final_expr = false;
2971 self.visit_block(b);
2972 self.break_targets.pop();
2973 },
2974 ExprKind::Block(b, _) => {
2975 if b.targeted_by_break {
2976 self.push_break_target(b.hir_id);
2977 self.visit_block(b);
2978 self.break_targets.pop();
2979 } else {
2980 self.visit_block(b);
2981 }
2982 },
2983 _ => {
2984 self.requires_semi = !self.cx.typeck_results().expr_ty(e).is_never();
2985 },
2986 }
2987 }
2988 return;
2989 }
2990 match e.kind {
2991 ExprKind::DropTemps(e) => self.visit_expr(e),
2992 ExprKind::Ret(None) | ExprKind::Continue(_) => self.is_never = true,
2993 ExprKind::Ret(Some(e)) | ExprKind::Become(e) => {
2994 self.in_final_expr = false;
2995 self.visit_expr(e);
2996 self.is_never = true;
2997 },
2998 ExprKind::Break(dest, e) => {
2999 if let Some(e) = e {
3000 self.in_final_expr = false;
3001 self.visit_expr(e);
3002 }
3003 if let Ok(id) = dest.target_id
3004 && let Some((i, target)) = self
3005 .break_targets
3006 .iter_mut()
3007 .enumerate()
3008 .find(|(_, target)| target.id == id)
3009 {
3010 target.unused &= self.is_never;
3011 if i < self.break_targets_for_result_ty as usize {
3012 self.requires_semi = true;
3013 }
3014 }
3015 self.is_never = true;
3016 },
3017 ExprKind::If(cond, then, else_) => {
3018 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3019 self.visit_expr(cond);
3020 self.in_final_expr = in_final_expr;
3021
3022 if self.is_never {
3023 self.visit_expr(then);
3024 if let Some(else_) = else_ {
3025 self.visit_expr(else_);
3026 }
3027 } else {
3028 self.visit_expr(then);
3029 let is_never = mem::replace(&mut self.is_never, false);
3030 if let Some(else_) = else_ {
3031 self.visit_expr(else_);
3032 self.is_never &= is_never;
3033 }
3034 }
3035 },
3036 ExprKind::Match(scrutinee, arms, _) => {
3037 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3038 self.visit_expr(scrutinee);
3039 self.in_final_expr = in_final_expr;
3040
3041 if self.is_never {
3042 for arm in arms {
3043 self.visit_arm(arm);
3044 }
3045 } else {
3046 let mut is_never = true;
3047 for arm in arms {
3048 self.is_never = false;
3049 if let Some(guard) = arm.guard {
3050 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3051 self.visit_expr(guard);
3052 self.in_final_expr = in_final_expr;
3053 self.is_never = false;
3055 }
3056 self.visit_expr(arm.body);
3057 is_never &= self.is_never;
3058 }
3059 self.is_never = is_never;
3060 }
3061 },
3062 ExprKind::Loop(b, _, _, _) => {
3063 self.push_break_target(e.hir_id);
3064 self.in_final_expr = false;
3065 self.visit_block(b);
3066 self.is_never = self.break_targets.pop().unwrap().unused;
3067 },
3068 ExprKind::Block(b, _) => {
3069 if b.targeted_by_break {
3070 self.push_break_target(b.hir_id);
3071 self.visit_block(b);
3072 self.is_never &= self.break_targets.pop().unwrap().unused;
3073 } else {
3074 self.visit_block(b);
3075 }
3076 },
3077 _ => {
3078 self.in_final_expr = false;
3079 walk_expr(self, e);
3080 self.is_never |= self.cx.typeck_results().expr_ty(e).is_never();
3081 },
3082 }
3083 }
3084
3085 fn visit_block(&mut self, b: &'tcx Block<'_>) {
3086 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3087 for s in b.stmts {
3088 self.visit_stmt(s);
3089 }
3090 self.in_final_expr = in_final_expr;
3091 if let Some(e) = b.expr {
3092 self.visit_expr(e);
3093 }
3094 }
3095
3096 fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {
3097 if let Some(e) = l.init {
3098 self.visit_expr(e);
3099 }
3100 if let Some(else_) = l.els {
3101 let is_never = self.is_never;
3102 self.visit_block(else_);
3103 self.is_never = is_never;
3104 }
3105 }
3106
3107 fn visit_arm(&mut self, arm: &Arm<'tcx>) {
3108 if let Some(guard) = arm.guard {
3109 let in_final_expr = mem::replace(&mut self.in_final_expr, false);
3110 self.visit_expr(guard);
3111 self.in_final_expr = in_final_expr;
3112 }
3113 self.visit_expr(arm.body);
3114 }
3115 }
3116
3117 if cx.typeck_results().expr_ty(e).is_never() {
3118 Some(RequiresSemi::No)
3119 } else if let ExprKind::Block(b, _) = e.kind
3120 && !b.targeted_by_break
3121 && b.expr.is_none()
3122 {
3123 None
3125 } else {
3126 let mut v = V {
3127 cx,
3128 break_targets: Vec::new(),
3129 break_targets_for_result_ty: 0,
3130 in_final_expr: true,
3131 requires_semi: false,
3132 is_never: false,
3133 };
3134 v.visit_expr(e);
3135 v.is_never
3136 .then_some(if v.requires_semi && matches!(e.kind, ExprKind::Block(..)) {
3137 RequiresSemi::Yes
3138 } else {
3139 RequiresSemi::No
3140 })
3141 }
3142}
3143
3144pub fn get_path_from_caller_to_method_type<'tcx>(
3150 tcx: TyCtxt<'tcx>,
3151 from: LocalDefId,
3152 method: DefId,
3153 args: GenericArgsRef<'tcx>,
3154) -> String {
3155 let assoc_item = tcx.associated_item(method);
3156 let def_id = assoc_item.container_id(tcx);
3157 match assoc_item.container {
3158 rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
3159 rustc_ty::AssocItemContainer::Impl => {
3160 let ty = tcx.type_of(def_id).instantiate_identity();
3161 get_path_to_ty(tcx, from, ty, args)
3162 },
3163 }
3164}
3165
3166fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
3167 match ty.kind() {
3168 rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
3169 rustc_ty::Array(..)
3171 | rustc_ty::Dynamic(..)
3172 | rustc_ty::Never
3173 | rustc_ty::RawPtr(_, _)
3174 | rustc_ty::Ref(..)
3175 | rustc_ty::Slice(_)
3176 | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
3177 _ => ty.to_string(),
3178 }
3179}
3180
3181fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
3183 if callee.is_local() {
3185 let callee_path = tcx.def_path(callee);
3186 let caller_path = tcx.def_path(from.to_def_id());
3187 maybe_get_relative_path(&caller_path, &callee_path, 2)
3188 } else {
3189 tcx.def_path_str(callee)
3190 }
3191}
3192
3193fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
3206 use itertools::EitherOrBoth::{Both, Left, Right};
3207
3208 let unique_parts = to
3210 .data
3211 .iter()
3212 .zip_longest(from.data.iter())
3213 .skip_while(|el| matches!(el, Both(l, r) if l == r))
3214 .map(|el| match el {
3215 Both(l, r) => Both(l.data, r.data),
3216 Left(l) => Left(l.data),
3217 Right(r) => Right(r.data),
3218 });
3219
3220 let mut go_up_by = 0;
3222 let mut path = Vec::new();
3223 for el in unique_parts {
3224 match el {
3225 Both(l, r) => {
3226 if let DefPathData::TypeNs(s) = l {
3236 path.push(s.to_string());
3237 }
3238 if let DefPathData::TypeNs(_) = r {
3239 go_up_by += 1;
3240 }
3241 },
3242 Left(DefPathData::TypeNs(sym)) => path.push(sym.to_string()),
3247 Right(DefPathData::TypeNs(_)) => go_up_by += 1,
3252 _ => {},
3253 }
3254 }
3255
3256 if go_up_by > max_super {
3257 once(String::from("crate"))
3259 .chain(to.data.iter().filter_map(|el| {
3260 if let DefPathData::TypeNs(sym) = el.data {
3261 Some(sym.to_string())
3262 } else {
3263 None
3264 }
3265 }))
3266 .join("::")
3267 } else {
3268 repeat_n(String::from("super"), go_up_by).chain(path).join("::")
3269 }
3270}
3271
3272pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
3275 matches!(
3276 cx.tcx.parent_hir_node(id),
3277 Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
3278 )
3279}
3280
3281pub fn is_block_like(expr: &Expr<'_>) -> bool {
3284 matches!(
3285 expr.kind,
3286 ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
3287 )
3288}
3289
3290pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
3292 fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
3293 match expr.kind {
3294 ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => contains_block(lhs, true),
3295 _ if is_block_like(expr) => is_operand,
3296 _ => false,
3297 }
3298 }
3299
3300 contains_block(expr, false)
3301}
3302
3303pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3305 if let Some(parent_expr) = get_parent_expr(cx, expr)
3306 && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
3307 && receiver.hir_id == expr.hir_id
3308 {
3309 return true;
3310 }
3311 false
3312}
3313
3314pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
3317 for_each_unconsumed_temporary(cx, expr, |temporary_ty| {
3318 if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
3319 && temporary_ty
3320 .walk()
3321 .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static()))
3322 {
3323 ControlFlow::Break(())
3324 } else {
3325 ControlFlow::Continue(())
3326 }
3327 })
3328 .is_break()
3329}
3330
3331pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
3342 let expr_ty_is_adjusted = cx
3343 .typeck_results()
3344 .expr_adjustments(expr)
3345 .iter()
3346 .any(|adj| !matches!(adj.kind, Adjust::NeverToAny));
3348 if expr_ty_is_adjusted {
3349 return true;
3350 }
3351
3352 match expr.kind {
3355 ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) if let Some(def_id) = fn_def_id(cx, expr) => {
3356 let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
3357
3358 if !fn_sig.output().skip_binder().has_type_flags(TypeFlags::HAS_TY_PARAM) {
3359 return false;
3360 }
3361
3362 let self_arg_count = usize::from(matches!(expr.kind, ExprKind::MethodCall(..)));
3363 let mut args_with_ty_param = {
3364 fn_sig
3365 .inputs()
3366 .skip_binder()
3367 .iter()
3368 .skip(self_arg_count)
3369 .zip(args)
3370 .filter_map(|(arg_ty, arg)| {
3371 if arg_ty.has_type_flags(TypeFlags::HAS_TY_PARAM) {
3372 Some(arg)
3373 } else {
3374 None
3375 }
3376 })
3377 };
3378 args_with_ty_param.any(|arg| expr_requires_coercion(cx, arg))
3379 },
3380 ExprKind::Struct(qpath, _, _) => {
3382 let res = cx.typeck_results().qpath_res(qpath, expr.hir_id);
3383 if let Some((_, v_def)) = adt_and_variant_of_res(cx, res) {
3384 let rustc_ty::Adt(_, generic_args) = cx.typeck_results().expr_ty_adjusted(expr).kind() else {
3385 return true;
3387 };
3388 v_def
3389 .fields
3390 .iter()
3391 .any(|field| field.ty(cx.tcx, generic_args).has_type_flags(TypeFlags::HAS_TY_PARAM))
3392 } else {
3393 false
3394 }
3395 },
3396 ExprKind::Block(
3398 &Block {
3399 expr: Some(ret_expr), ..
3400 },
3401 _,
3402 )
3403 | ExprKind::Ret(Some(ret_expr)) => expr_requires_coercion(cx, ret_expr),
3404
3405 ExprKind::Array(elems) | ExprKind::Tup(elems) => elems.iter().any(|elem| expr_requires_coercion(cx, elem)),
3407 ExprKind::Repeat(rep_elem, _) => expr_requires_coercion(cx, rep_elem),
3409 ExprKind::If(_, then, maybe_else) => {
3411 expr_requires_coercion(cx, then) || maybe_else.is_some_and(|e| expr_requires_coercion(cx, e))
3412 },
3413 ExprKind::Match(_, arms, _) => arms
3414 .iter()
3415 .map(|arm| arm.body)
3416 .any(|body| expr_requires_coercion(cx, body)),
3417 _ => false,
3418 }
3419}
3420
3421pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
3424 if let Some(hir_id) = path_to_local(expr)
3425 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
3426 {
3427 matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
3428 } else if let ExprKind::Path(p) = &expr.kind
3429 && let Some(mutability) = cx
3430 .qpath_res(p, expr.hir_id)
3431 .opt_def_id()
3432 .and_then(|id| cx.tcx.static_mutability(id))
3433 {
3434 mutability == Mutability::Mut
3435 } else if let ExprKind::Field(parent, _) = expr.kind {
3436 is_mutable(cx, parent)
3437 } else {
3438 true
3439 }
3440}
3441
3442pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3445 let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3446 return hir_ty;
3447 };
3448 while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3449 && let Some(segment) = path.segments.last()
3450 && segment.ident.name == sym::Option
3451 && let Res::Def(DefKind::Enum, def_id) = segment.res
3452 && def_id == option_def_id
3453 && let [GenericArg::Type(arg_ty)] = segment.args().args
3454 {
3455 hir_ty = arg_ty.as_unambig_ty();
3456 }
3457 hir_ty
3458}
3459
3460pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
3463 if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
3464 && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
3465 && let ctxt = expr.span.ctxt()
3466 && for_each_expr_without_closures(into_future_arg, |e| {
3467 walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
3468 })
3469 .is_none()
3470 {
3471 Some(into_future_arg)
3472 } else {
3473 None
3474 }
3475}