1 #![feature(array_chunks)]
2 #![feature(box_patterns)]
3 #![feature(control_flow_enum)]
4 #![feature(let_chains)]
5 #![feature(lint_reasons)]
6 #![feature(never_type)]
8 #![feature(rustc_private)]
9 #![recursion_limit = "512"]
10 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
11 #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
12 // warn on the same lints as `clippy_lints`
13 #![warn(trivial_casts, trivial_numeric_casts)]
14 // warn on lints, that are included in `rust-lang/rust`s bootstrap
15 #![warn(rust_2018_idioms, unused_lifetimes)]
16 // warn on rustc internal lints
17 #![warn(rustc::internal)]
19 // FIXME: switch to something more ergonomic here, once available.
20 // (Currently there is no way to opt into sysroot crates without `extern crate`.)
21 extern crate rustc_ast;
22 extern crate rustc_ast_pretty;
23 extern crate rustc_attr;
24 extern crate rustc_data_structures;
25 extern crate rustc_errors;
26 extern crate rustc_hir;
27 extern crate rustc_hir_typeck;
28 extern crate rustc_index;
29 extern crate rustc_infer;
30 extern crate rustc_lexer;
31 extern crate rustc_lint;
32 extern crate rustc_middle;
33 extern crate rustc_mir_dataflow;
34 extern crate rustc_parse_format;
35 extern crate rustc_session;
36 extern crate rustc_span;
37 extern crate rustc_target;
38 extern crate rustc_trait_selection;
49 pub mod eager_or_lazy;
55 pub mod numeric_literal;
58 pub mod qualify_min_const_fn;
66 pub use self::attrs::*;
67 pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
68 pub use self::hir_utils::{
69 both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
72 use core::ops::ControlFlow;
73 use std::collections::hash_map::Entry;
74 use std::hash::BuildHasherDefault;
75 use std::sync::OnceLock;
76 use std::sync::{Mutex, MutexGuard};
78 use if_chain::if_chain;
79 use rustc_ast::ast::{self, LitKind};
80 use rustc_ast::Attribute;
81 use rustc_data_structures::fx::FxHashMap;
82 use rustc_data_structures::unhash::UnhashMap;
83 use rustc_hir::def::{DefKind, Res};
84 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
85 use rustc_hir::hir_id::{HirIdMap, HirIdSet};
86 use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
87 use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
89 self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination,
90 Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local,
91 MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
92 TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp,
94 use rustc_lexer::{tokenize, TokenKind};
95 use rustc_lint::{LateContext, Level, Lint, LintContext};
96 use rustc_middle::hir::place::PlaceBase;
97 use rustc_middle::ty as rustc_ty;
98 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
99 use rustc_middle::ty::binding::BindingMode;
100 use rustc_middle::ty::fast_reject::SimplifiedType::{
101 ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
102 PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
104 use rustc_middle::ty::{
105 layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
107 use rustc_middle::ty::{FloatTy, IntTy, UintTy};
108 use rustc_span::hygiene::{ExpnKind, MacroKind};
109 use rustc_span::source_map::SourceMap;
111 use rustc_span::symbol::{kw, Ident, Symbol};
112 use rustc_span::Span;
113 use rustc_target::abi::Integer;
115 use crate::consts::{constant, Constant};
116 use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
117 use crate::visitors::for_each_expr;
120 macro_rules! extract_msrv_attr {
121 ($context:ident) => {
122 fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
123 let sess = rustc_lint::LintContext::sess(cx);
124 self.msrv.enter_lint_attrs(sess, attrs);
127 fn exit_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
128 let sess = rustc_lint::LintContext::sess(cx);
129 self.msrv.exit_lint_attrs(sess, attrs);
134 /// If the given expression is a local binding, find the initializer expression.
135 /// If that initializer expression is another local binding, find its initializer again.
136 /// This process repeats as long as possible (but usually no more than once). Initializer
137 /// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
150 /// let def = abc + 2;
151 /// // ^^^^^^^ output
155 pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
156 while let Some(init) = path_to_local(expr)
157 .and_then(|id| find_binding_init(cx, id))
158 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
165 /// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
166 /// By only considering immutable bindings, we guarantee that the returned expression represents the
167 /// value of the binding wherever it is referenced.
169 /// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
170 /// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
171 /// canonical binding `HirId`.
172 pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
173 let hir = cx.tcx.hir();
175 if let Some(Node::Pat(pat)) = hir.find(hir_id);
176 if matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..));
177 let parent = hir.parent_id(hir_id);
178 if let Some(Node::Local(local)) = hir.find(parent);
186 /// Returns `true` if the given `NodeId` is inside a constant context
191 /// if in_constant(cx, expr.hir_id) {
195 pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
196 let parent_id = cx.tcx.hir().get_parent_item(id).def_id;
197 match cx.tcx.hir().get_by_def_id(parent_id) {
199 kind: ItemKind::Const(..) | ItemKind::Static(..) | ItemKind::Enum(..),
202 | Node::TraitItem(&TraitItem {
203 kind: TraitItemKind::Const(..),
206 | Node::ImplItem(&ImplItem {
207 kind: ImplItemKind::Const(..),
210 | Node::AnonConst(_) => true,
212 kind: ItemKind::Fn(ref sig, ..),
215 | Node::ImplItem(&ImplItem {
216 kind: ImplItemKind::Fn(ref sig, _),
218 }) => sig.header.constness == Constness::Const,
223 /// Checks if a `Res` refers to a constructor of a `LangItem`
224 /// For example, use this to check whether a function call or a pattern is `Some(..)`.
225 pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
226 if let Res::Def(DefKind::Ctor(..), id) = res
227 && let Some(lang_id) = cx.tcx.lang_items().get(lang_item)
228 && let Some(id) = cx.tcx.opt_parent(id)
236 pub fn is_res_diagnostic_ctor(cx: &LateContext<'_>, res: Res, diag_item: Symbol) -> bool {
237 if let Res::Def(DefKind::Ctor(..), id) = res
238 && let Some(id) = cx.tcx.opt_parent(id)
240 cx.tcx.is_diagnostic_item(diag_item, id)
246 /// Checks if a `QPath` resolves to a constructor of a diagnostic item.
247 pub fn is_diagnostic_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, diagnostic_item: Symbol) -> bool {
248 if let QPath::Resolved(_, path) = qpath {
249 if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
250 return cx.tcx.is_diagnostic_item(diagnostic_item, cx.tcx.parent(ctor_id));
256 /// Checks if the `DefId` matches the given diagnostic item or it's constructor.
257 pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
258 let did = match cx.tcx.def_kind(did) {
259 DefKind::Ctor(..) => cx.tcx.parent(did),
260 // Constructors for types in external crates seem to have `DefKind::Variant`
261 DefKind::Variant => match cx.tcx.opt_parent(did) {
262 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
268 cx.tcx.is_diagnostic_item(item, did)
271 /// Checks if the `DefId` matches the given `LangItem` or it's constructor.
272 pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
273 let did = match cx.tcx.def_kind(did) {
274 DefKind::Ctor(..) => cx.tcx.parent(did),
275 // Constructors for types in external crates seem to have `DefKind::Variant`
276 DefKind::Variant => match cx.tcx.opt_parent(did) {
277 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
283 cx.tcx.lang_items().get(item) == Some(did)
286 pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
296 ) | ExprKind::Tup([])
300 /// Checks if given pattern is a wildcard (`_`)
301 pub fn is_wild(pat: &Pat<'_>) -> bool {
302 matches!(pat.kind, PatKind::Wild)
305 /// Checks if the method call given in `expr` belongs to the given trait.
306 /// This is a deprecated function, consider using [`is_trait_method`].
307 pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
308 let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
309 let trt_id = cx.tcx.trait_of_item(def_id);
310 trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
313 /// Checks if a method is defined in an impl of a diagnostic item
314 pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
315 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
316 if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
317 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
323 /// Checks if a method is in a diagnostic item trait
324 pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
325 if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
326 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
331 /// Checks if the method call given in `expr` belongs to the given trait.
332 pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
334 .type_dependent_def_id(expr.hir_id)
335 .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
338 /// Checks if the given expression is a path referring an item on the trait
339 /// that is marked with the given diagnostic item.
341 /// For checking method call expressions instead of path expressions, use
342 /// [`is_trait_method`].
344 /// For example, this can be used to find if an expression like `u64::default`
345 /// refers to an item of the trait `Default`, which is associated with the
346 /// `diag_item` of `sym::Default`.
347 pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
348 if let hir::ExprKind::Path(ref qpath) = expr.kind {
349 cx.qpath_res(qpath, expr.hir_id)
351 .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
357 pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
359 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
360 QPath::TypeRelative(_, seg) => seg,
361 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
365 pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
366 last_path_segment(qpath)
368 .map_or(&[][..], |a| a.args)
370 .filter_map(|a| match a {
371 hir::GenericArg::Type(ty) => Some(*ty),
376 /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
377 /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
378 /// `QPath::Resolved.1.res.opt_def_id()`.
380 /// Matches a `QPath` against a slice of segment string literals.
382 /// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
383 /// `rustc_hir::QPath`.
387 /// match_qpath(path, &["std", "rt", "begin_unwind"])
389 pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
391 QPath::Resolved(_, path) => match_path(path, segments),
392 QPath::TypeRelative(ty, segment) => match ty.kind {
393 TyKind::Path(ref inner_path) => {
394 if let [prefix @ .., end] = segments {
395 if match_qpath(inner_path, prefix) {
396 return segment.ident.name.as_str() == *end;
403 QPath::LangItem(..) => false,
407 /// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
409 /// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
410 pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
411 path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
414 /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
415 /// it matches the given lang item.
416 pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
417 path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id))
420 /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
421 /// it matches the given diagnostic item.
422 pub fn is_path_diagnostic_item<'tcx>(
423 cx: &LateContext<'_>,
424 maybe_path: &impl MaybePath<'tcx>,
427 path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
430 /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
431 /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
432 /// `QPath::Resolved.1.res.opt_def_id()`.
434 /// Matches a `Path` against a slice of segment string literals.
436 /// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
437 /// `rustc_hir::Path`.
442 /// if match_path(&trait_ref.path, &paths::HASH) {
443 /// // This is the `std::hash::Hash` trait.
446 /// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
447 /// // This is a `rustc_middle::lint::Lint`.
450 pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
454 .zip(segments.iter().rev())
455 .all(|(a, b)| a.ident.name.as_str() == *b)
458 /// If the expression is a path to a local, returns the canonical `HirId` of the local.
459 pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
460 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
461 if let Res::Local(id) = path.res {
468 /// Returns true if the expression is a path to a local with the specified `HirId`.
469 /// Use this function to see if an expression matches a function argument or a match binding.
470 pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
471 path_to_local(expr) == Some(id)
474 pub trait MaybePath<'hir> {
475 fn hir_id(&self) -> HirId;
476 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
479 macro_rules! maybe_path {
480 ($ty:ident, $kind:ident) => {
481 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
482 fn hir_id(&self) -> HirId {
485 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
487 hir::$kind::Path(qpath) => Some(qpath),
494 maybe_path!(Expr, ExprKind);
495 maybe_path!(Pat, PatKind);
496 maybe_path!(Ty, TyKind);
498 /// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
499 pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
500 match maybe_path.qpath_opt() {
502 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
506 /// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
507 pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
508 path_res(cx, maybe_path).opt_def_id()
511 fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
512 let ty = match name {
513 "bool" => BoolSimplifiedType,
514 "char" => CharSimplifiedType,
515 "str" => StrSimplifiedType,
516 "array" => ArraySimplifiedType,
517 "slice" => SliceSimplifiedType,
518 // FIXME: rustdoc documents these two using just `pointer`.
520 // Maybe this is something we should do here too.
521 "const_ptr" => PtrSimplifiedType(Mutability::Not),
522 "mut_ptr" => PtrSimplifiedType(Mutability::Mut),
523 "isize" => IntSimplifiedType(IntTy::Isize),
524 "i8" => IntSimplifiedType(IntTy::I8),
525 "i16" => IntSimplifiedType(IntTy::I16),
526 "i32" => IntSimplifiedType(IntTy::I32),
527 "i64" => IntSimplifiedType(IntTy::I64),
528 "i128" => IntSimplifiedType(IntTy::I128),
529 "usize" => UintSimplifiedType(UintTy::Usize),
530 "u8" => UintSimplifiedType(UintTy::U8),
531 "u16" => UintSimplifiedType(UintTy::U16),
532 "u32" => UintSimplifiedType(UintTy::U32),
533 "u64" => UintSimplifiedType(UintTy::U64),
534 "u128" => UintSimplifiedType(UintTy::U128),
535 "f32" => FloatSimplifiedType(FloatTy::F32),
536 "f64" => FloatSimplifiedType(FloatTy::F64),
537 _ => return [].iter().copied(),
540 tcx.incoherent_impls(ty).iter().copied()
543 fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
544 match tcx.def_kind(def_id) {
545 DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
546 .module_children(def_id)
548 .filter(|item| item.ident.name == name)
549 .map(|child| child.res.expect_non_local())
552 .associated_item_def_ids(def_id)
555 .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
556 .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
562 fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
566 let item_kind = match hir.find_by_def_id(local_id) {
567 Some(Node::Crate(r#mod)) => {
568 root_mod = ItemKind::Mod(r#mod);
571 Some(Node::Item(item)) => &item.kind,
572 _ => return Vec::new(),
575 let res = |ident: Ident, owner_id: OwnerId| {
576 if ident.name == name {
577 let def_id = owner_id.to_def_id();
578 Some(Res::Def(tcx.def_kind(def_id), def_id))
585 ItemKind::Mod(r#mod) => r#mod
588 .filter_map(|&item_id| res(hir.item(item_id).ident, item_id.owner_id))
590 ItemKind::Impl(r#impl) => r#impl
593 .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
595 ItemKind::Trait(.., trait_item_refs) => trait_item_refs
597 .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
603 fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
604 if let Some(local_id) = def_id.as_local() {
605 local_item_children_by_name(tcx, local_id, name)
607 non_local_item_children_by_name(tcx, def_id, name)
611 /// Resolves a def path like `std::vec::Vec`.
613 /// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
614 /// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
616 /// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec`
617 /// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
619 /// This function is expensive and should be used sparingly.
620 pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> {
621 fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator<Item = DefId> + '_ {
625 .filter(move |&num| tcx.crate_name(num) == name)
626 .map(CrateNum::as_def_id)
631 let (base, mut path) = match *path {
633 return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
635 [base, ref path @ ..] => (base, path),
636 _ => return Vec::new(),
639 let base_sym = Symbol::intern(base);
641 let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
642 Some(LOCAL_CRATE.as_def_id())
647 let starts = find_primitive_impls(tcx, base)
648 .chain(find_crates(tcx, base_sym))
650 .map(|id| Res::Def(tcx.def_kind(id), id));
652 let mut resolutions: Vec<Res> = starts.collect();
654 while let [segment, rest @ ..] = path {
656 let segment = Symbol::intern(segment);
658 resolutions = resolutions
660 .filter_map(|res| res.opt_def_id())
662 // When the current def_id is e.g. `struct S`, check the impl items in
664 let inherent_impl_children = tcx
665 .inherent_impls(def_id)
667 .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
669 let direct_children = item_children_by_name(tcx, def_id, segment);
671 inherent_impl_children.chain(direct_children)
679 /// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`].
680 pub fn def_path_def_ids(cx: &LateContext<'_>, path: &[&str]) -> impl Iterator<Item = DefId> {
681 def_path_res(cx, path).into_iter().filter_map(|res| res.opt_def_id())
684 /// Convenience function to get the `DefId` of a trait by path.
685 /// It could be a trait or trait alias.
687 /// This function is expensive and should be used sparingly.
688 pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
689 def_path_res(cx, path).into_iter().find_map(|res| match res {
690 Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
695 /// Gets the `hir::TraitRef` of the trait the given method is implemented for.
697 /// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
700 /// struct Point(isize, isize);
702 /// impl std::ops::Add for Point {
703 /// type Output = Self;
705 /// fn add(self, other: Self) -> Self {
710 pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
711 // Get the implemented trait for the current function
712 let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
713 let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
715 if parent_impl != hir::CRATE_OWNER_ID;
716 if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl.def_id);
717 if let hir::ItemKind::Impl(impl_) = &item.kind;
719 return impl_.of_trait.as_ref();
725 /// This method will return tuple of projection stack and root of the expression,
726 /// used in `can_mut_borrow_both`.
728 /// For example, if `e` represents the `v[0].a.b[x]`
729 /// this method will return a tuple, composed of a `Vec`
730 /// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
731 /// and an `Expr` for root of them, `v`
732 fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
733 let mut result = vec![];
736 ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
747 /// Gets the mutability of the custom deref adjustment, if any.
748 pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
752 .find_map(|a| match a.kind {
753 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
754 Adjust::Deref(None) => None,
760 /// Checks if two expressions can be mutably borrowed simultaneously
761 /// and they aren't dependent on borrowing same thing twice
762 pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
763 let (s1, r1) = projection_stack(e1);
764 let (s2, r2) = projection_stack(e2);
765 if !eq_expr_value(cx, r1, r2) {
768 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
772 for (x1, x2) in s1.iter().zip(s2.iter()) {
773 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
777 match (&x1.kind, &x2.kind) {
778 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
783 (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
784 if !eq_expr_value(cx, i1, i2) {
794 /// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
795 /// constructor from the std library
796 fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
797 let std_types_symbols = &[
808 if let QPath::TypeRelative(_, method) = path {
809 if method.ident.name == sym::new {
810 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
811 if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
812 return std_types_symbols.iter().any(|&symbol| {
813 cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
822 /// Return true if the expr is equal to `Default::default` when evaluated.
823 pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
825 if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
826 if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
827 if is_diag_trait_item(cx, repl_def_id, sym::Default)
828 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
829 then { true } else { false }
833 /// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
834 /// It doesn't cover all cases, for example indirect function calls (some of std
835 /// functions are supported) but it is the best we have.
836 pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
838 ExprKind::Lit(lit) => match lit.node {
839 LitKind::Bool(false) | LitKind::Int(0, _) => true,
840 LitKind::Str(s, _) => s.is_empty(),
843 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
844 ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
845 if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
846 if let LitKind::Int(v, _) = const_lit.node;
847 if v <= 32 && is_default_equivalent(cx, x);
855 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func),
856 ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg),
857 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
858 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
863 fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
864 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind &&
865 seg.ident.name == sym::from
868 ExprKind::Lit(hir::Lit {
869 node: LitKind::Str(ref sym, _),
871 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
872 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
873 ExprKind::Repeat(_, ArrayLen::Body(len)) => {
874 if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind &&
875 let LitKind::Int(v, _) = const_lit.node
877 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
886 /// Checks if the top level expression can be moved into a closure as is.
887 /// Currently checks for:
888 /// * Break/Continue outside the given loop HIR ids.
889 /// * Yield/Return statements.
890 /// * Inline assembly.
891 /// * Usages of a field of a local where the type of the local can be partially moved.
893 /// For example, given the following function:
896 /// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
897 /// for item in iter {
908 /// When called on the expression `item.0` this will return false unless the local `item` is in the
909 /// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
910 /// isn't always safe to move into a closure when only a single field is needed.
912 /// When called on the `continue` expression this will return false unless the outer loop expression
913 /// is in the `loop_ids` set.
915 /// Note that this check is not recursive, so passing the `if` expression will always return true
916 /// even though sub-expressions might return false.
917 pub fn can_move_expr_to_closure_no_visit<'tcx>(
918 cx: &LateContext<'tcx>,
919 expr: &'tcx Expr<'_>,
921 ignore_locals: &HirIdSet,
924 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
925 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
926 if loop_ids.contains(&id) =>
931 | ExprKind::Continue(_)
933 | ExprKind::Yield(..)
934 | ExprKind::InlineAsm(_) => false,
935 // Accessing a field of a local value can only be done if the type isn't
941 ExprKind::Path(QPath::Resolved(
944 res: Res::Local(local_id),
951 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
952 // TODO: check if the local has been partially moved. Assume it has for now.
959 /// How a local is captured by a closure
960 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
961 pub enum CaptureKind {
966 pub fn is_imm_ref(self) -> bool {
967 self == Self::Ref(Mutability::Not)
970 impl std::ops::BitOr for CaptureKind {
972 fn bitor(self, rhs: Self) -> Self::Output {
974 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
975 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
976 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
977 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
981 impl std::ops::BitOrAssign for CaptureKind {
982 fn bitor_assign(&mut self, rhs: Self) {
987 /// Given an expression referencing a local, determines how it would be captured in a closure.
988 /// Note as this will walk up to parent expressions until the capture can be determined it should
989 /// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
990 /// function argument (other than a receiver).
991 pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
992 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
993 let mut capture = CaptureKind::Ref(Mutability::Not);
994 pat.each_binding_or_first(&mut |_, id, span, _| match cx
996 .extract_binding_mode(cx.sess(), id, span)
999 BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
1000 capture = CaptureKind::Value;
1002 BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
1003 capture = CaptureKind::Ref(Mutability::Mut);
1010 debug_assert!(matches!(
1012 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
1015 let mut child_id = e.hir_id;
1016 let mut capture = CaptureKind::Value;
1017 let mut capture_expr_ty = e;
1019 for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
1022 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
1030 .map_or(&[][..], |x| &**x)
1032 if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
1033 *adjust.last().map_or(target, |a| a.target).kind()
1035 return CaptureKind::Ref(mutability);
1040 Node::Expr(e) => match e.kind {
1041 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
1042 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
1043 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
1044 return CaptureKind::Ref(Mutability::Mut);
1046 ExprKind::Field(..) => {
1047 if capture == CaptureKind::Value {
1048 capture_expr_ty = e;
1051 ExprKind::Let(let_expr) => {
1052 let mutability = match pat_capture_kind(cx, let_expr.pat) {
1053 CaptureKind::Value => Mutability::Not,
1054 CaptureKind::Ref(m) => m,
1056 return CaptureKind::Ref(mutability);
1058 ExprKind::Match(_, arms, _) => {
1059 let mut mutability = Mutability::Not;
1060 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
1062 CaptureKind::Value => break,
1063 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
1064 CaptureKind::Ref(Mutability::Not) => (),
1067 return CaptureKind::Ref(mutability);
1071 Node::Local(l) => match pat_capture_kind(cx, l.pat) {
1072 CaptureKind::Value => break,
1073 capture @ CaptureKind::Ref(_) => return capture,
1078 child_id = parent_id;
1081 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
1082 // Copy types are never automatically captured by value.
1083 CaptureKind::Ref(Mutability::Not)
1089 /// Checks if the expression can be moved into a closure as is. This will return a list of captures
1090 /// if so, otherwise, `None`.
1091 pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
1092 struct V<'cx, 'tcx> {
1093 cx: &'cx LateContext<'tcx>,
1094 // Stack of potential break targets contained in the expression.
1096 /// Local variables created in the expression. These don't need to be captured.
1098 /// Whether this expression can be turned into a closure.
1099 allow_closure: bool,
1100 /// Locals which need to be captured, and whether they need to be by value, reference, or
1101 /// mutable reference.
1102 captures: HirIdMap<CaptureKind>,
1104 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1105 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1106 if !self.allow_closure {
1111 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1112 if !self.locals.contains(&l) {
1113 let cap = capture_local_usage(self.cx, e);
1114 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1117 ExprKind::Closure { .. } => {
1118 let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id);
1119 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
1120 let local_id = match capture.place.base {
1121 PlaceBase::Local(id) => id,
1122 PlaceBase::Upvar(var) => var.var_path.hir_id,
1125 if !self.locals.contains(&local_id) {
1126 let capture = match capture.info.capture_kind {
1127 UpvarCapture::ByValue => CaptureKind::Value,
1128 UpvarCapture::ByRef(kind) => match kind {
1129 BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
1130 BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
1131 CaptureKind::Ref(Mutability::Mut)
1137 .and_modify(|e| *e |= capture)
1138 .or_insert(capture);
1142 ExprKind::Loop(b, ..) => {
1143 self.loops.push(e.hir_id);
1144 self.visit_block(b);
1148 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1154 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1155 p.each_binding_or_first(&mut |_, id, _, _| {
1156 self.locals.insert(id);
1163 allow_closure: true,
1165 locals: HirIdSet::default(),
1166 captures: HirIdMap::default(),
1169 v.allow_closure.then_some(v.captures)
1172 /// Arguments of a method: the receiver and all the additional arguments.
1173 pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1175 /// Returns the method names and argument list of nested method call expressions that make up
1176 /// `expr`. method/span lists are sorted with the most recent call first.
1177 pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1178 let mut method_names = Vec::with_capacity(max_depth);
1179 let mut arg_lists = Vec::with_capacity(max_depth);
1180 let mut spans = Vec::with_capacity(max_depth);
1182 let mut current = expr;
1183 for _ in 0..max_depth {
1184 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1185 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1188 method_names.push(path.ident.name);
1189 arg_lists.push((*receiver, &**args));
1190 spans.push(path.ident.span);
1197 (method_names, arg_lists, spans)
1200 /// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
1202 /// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
1203 /// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
1204 /// containing the `Expr`s for
1205 /// `.bar()` and `.baz()`
1206 pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1207 let mut current = expr;
1208 let mut matched = Vec::with_capacity(methods.len());
1209 for method_name in methods.iter().rev() {
1210 // method chains are stored last -> first
1211 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1212 if path.ident.name.as_str() == *method_name {
1213 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1216 matched.push((receiver, args)); // build up `matched` backwards
1217 current = receiver; // go to parent expression
1225 // Reverse `matched` so that it is in the same order as `methods`.
1230 /// Returns `true` if the provided `def_id` is an entrypoint to a program.
1231 pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1234 .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1237 /// Returns `true` if the expression is in the program's `#[panic_handler]`.
1238 pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1239 let parent = cx.tcx.hir().get_parent_item(e.hir_id);
1240 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1243 /// Gets the name of the item the expression is in, if available.
1244 pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1245 let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id).def_id;
1246 match cx.tcx.hir().find_by_def_id(parent_id) {
1248 Node::Item(Item { ident, .. })
1249 | Node::TraitItem(TraitItem { ident, .. })
1250 | Node::ImplItem(ImplItem { ident, .. }),
1251 ) => Some(ident.name),
1256 pub struct ContainsName {
1261 impl<'tcx> Visitor<'tcx> for ContainsName {
1262 fn visit_name(&mut self, name: Symbol) {
1263 if self.name == name {
1269 /// Checks if an `Expr` contains a certain name.
1270 pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
1271 let mut cn = ContainsName { name, result: false };
1272 cn.visit_expr(expr);
1276 /// Returns `true` if `expr` contains a return expression
1277 pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
1278 for_each_expr(expr, |e| {
1279 if matches!(e.kind, hir::ExprKind::Ret(..)) {
1280 ControlFlow::Break(())
1282 ControlFlow::Continue(())
1288 /// Gets the parent node, if any.
1289 pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
1290 tcx.hir().find_parent(id)
1293 /// Gets the parent expression, if any –- this is useful to constrain a lint.
1294 pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1295 get_parent_expr_for_hir(cx, e.hir_id)
1298 /// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
1299 /// constraint lints
1300 pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
1301 match get_parent_node(cx.tcx, hir_id) {
1302 Some(Node::Expr(parent)) => Some(parent),
1307 pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1308 let map = &cx.tcx.hir();
1309 let enclosing_node = map
1310 .get_enclosing_scope(hir_id)
1311 .and_then(|enclosing_id| map.find(enclosing_id));
1312 enclosing_node.and_then(|node| match node {
1313 Node::Block(block) => Some(block),
1315 kind: ItemKind::Fn(_, _, eid),
1318 | Node::ImplItem(&ImplItem {
1319 kind: ImplItemKind::Fn(_, eid),
1321 }) => match cx.tcx.hir().body(eid).value.kind {
1322 ExprKind::Block(block, _) => Some(block),
1329 /// Gets the loop or closure enclosing the given expression, if any.
1330 pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1331 cx: &LateContext<'tcx>,
1333 ) -> Option<&'tcx Expr<'tcx>> {
1334 for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
1336 Node::Expr(e) => match e.kind {
1337 ExprKind::Closure { .. } => {
1338 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1339 && subs.as_closure().kind() == ClosureKind::FnOnce
1343 let is_once = walk_to_expr_usage(cx, e, |node, id| {
1344 let Node::Expr(e) = node else {
1348 ExprKind::Call(f, _) if f.hir_id == id => Some(()),
1349 ExprKind::Call(f, args) => {
1350 let i = args.iter().position(|arg| arg.hir_id == id)?;
1351 let sig = expr_sig(cx, f)?;
1352 let predicates = sig
1354 .map_or(cx.param_env, |id| cx.tcx.param_env(id))
1356 sig.input(i).and_then(|ty| {
1357 ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
1360 ExprKind::MethodCall(_, receiver, args, _) => {
1361 let i = std::iter::once(receiver)
1363 .position(|arg| arg.hir_id == id)?;
1364 let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
1365 let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
1366 ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
1376 ExprKind::Loop(..) => return Some(e),
1379 Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
1386 /// Gets the parent node if it's an impl block.
1387 pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1388 match tcx.hir().parent_iter(id).next() {
1392 kind: ItemKind::Impl(imp),
1400 /// Removes blocks around an expression, only if the block contains just one expression
1401 /// and no statements. Unsafe blocks are not removed.
1405 /// * `{ x }` -> `x`
1406 /// * `{{ x }}` -> `x`
1407 /// * `{ x; }` -> `{ x; }`
1408 /// * `{ x; y }` -> `{ x; y }`
1409 /// * `{ unsafe { x } }` -> `unsafe { x }`
1410 pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1411 while let ExprKind::Block(
1415 rules: BlockCheckMode::DefaultBlock,
1426 /// Removes blocks around an expression, only if the block contains just one expression
1427 /// or just one expression statement with a semicolon. Unsafe blocks are not removed.
1431 /// * `{ x }` -> `x`
1432 /// * `{ x; }` -> `x`
1433 /// * `{{ x; }}` -> `x`
1434 /// * `{ x; y }` -> `{ x; y }`
1435 /// * `{ unsafe { x } }` -> `unsafe { x }`
1436 pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1437 while let ExprKind::Block(
1441 rules: BlockCheckMode::DefaultBlock,
1448 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1453 rules: BlockCheckMode::DefaultBlock,
1464 /// Checks if the given expression is the else clause of either an `if` or `if let` expression.
1465 pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1466 let mut iter = tcx.hir().parent_iter(expr.hir_id);
1471 kind: ExprKind::If(_, _, Some(else_expr)),
1474 )) => else_expr.hir_id == expr.hir_id,
1479 /// Checks whether the given expression is a constant integer of the given value.
1480 /// unlike `is_integer_literal`, this version does const folding
1481 pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1482 if is_integer_literal(e, value) {
1485 let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
1486 if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
1492 /// Checks whether the given expression is a constant literal of the given value.
1493 pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1494 // FIXME: use constant folding
1495 if let ExprKind::Lit(ref spanned) = expr.kind {
1496 if let LitKind::Int(v, _) = spanned.node {
1503 /// Returns `true` if the given `Expr` has been coerced before.
1505 /// Examples of coercions can be found in the Nomicon at
1506 /// <https://doc.rust-lang.org/nomicon/coercions.html>.
1508 /// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for
1509 /// more information on adjustments and coercions.
1510 pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1511 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1514 /// Returns the pre-expansion span if this comes from an expansion of the
1516 /// See also [`is_direct_expn_of`].
1518 pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
1520 if span.from_expansion() {
1521 let data = span.ctxt().outer_expn_data();
1522 let new_span = data.call_site;
1524 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
1525 if mac_name.as_str() == name {
1526 return Some(new_span);
1537 /// Returns the pre-expansion span if the span directly comes from an expansion
1538 /// of the macro `name`.
1539 /// The difference with [`is_expn_of`] is that in
1541 /// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
1542 /// # macro_rules! bar { ($e:expr) => { $e } }
1545 /// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
1546 /// from `bar!` by `is_direct_expn_of`.
1548 pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
1549 if span.from_expansion() {
1550 let data = span.ctxt().outer_expn_data();
1551 let new_span = data.call_site;
1553 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
1554 if mac_name.as_str() == name {
1555 return Some(new_span);
1563 /// Convenience function to get the return type of a function.
1564 pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
1565 let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
1566 let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
1567 cx.tcx.erase_late_bound_regions(ret_ty)
1570 /// Convenience function to get the nth argument type of a function.
1571 pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId, nth: usize) -> Ty<'tcx> {
1572 let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
1573 let arg = cx.tcx.fn_sig(fn_def_id).input(nth);
1574 cx.tcx.erase_late_bound_regions(arg)
1577 /// Checks if an expression is constructing a tuple-like enum variant or struct
1578 pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1579 if let ExprKind::Call(fun, _) = expr.kind {
1580 if let ExprKind::Path(ref qp) = fun.kind {
1581 let res = cx.qpath_res(qp, fun.hir_id);
1583 def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1584 def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1592 /// Returns `true` if a pattern is refutable.
1593 // TODO: should be implemented using rustc/mir_build/thir machinery
1594 pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1595 fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1597 cx.qpath_res(qpath, id),
1598 def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
1602 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1603 i.into_iter().any(|pat| is_refutable(cx, pat))
1607 PatKind::Wild => false,
1608 PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
1609 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1610 PatKind::Lit(..) | PatKind::Range(..) => true,
1611 PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
1612 PatKind::Or(pats) => {
1613 // TODO: should be the honest check, that pats is exhaustive set
1614 are_refutable(cx, pats)
1616 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1617 PatKind::Struct(ref qpath, fields, _) => {
1618 is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1620 PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
1621 PatKind::Slice(head, middle, tail) => {
1622 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1623 rustc_ty::Slice(..) => {
1624 // [..] is the only irrefutable slice pattern.
1625 !head.is_empty() || middle.is_none() || !tail.is_empty()
1627 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1637 /// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
1638 /// the function once on the given pattern.
1639 pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1640 if let PatKind::Or(pats) = pat.kind {
1641 pats.iter().for_each(f);
1647 pub fn is_self(slf: &Param<'_>) -> bool {
1648 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1649 name.name == kw::SelfLower
1655 pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1656 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
1657 if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res {
1664 pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1665 (0..decl.inputs.len()).map(move |i| &body.params[i])
1668 /// Checks if a given expression is a match expression expanded from the `?`
1669 /// operator or the `try` macro.
1670 pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1671 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1673 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind;
1674 if ddpos.as_opt_usize().is_none();
1675 if is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk);
1676 if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
1677 if path_to_local_id(arm.body, hir_id);
1685 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1686 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1687 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1693 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1694 // desugared from a `?` operator
1695 if *source == MatchSource::TryDesugar {
1701 if arms[0].guard.is_none();
1702 if arms[1].guard.is_none();
1703 if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
1713 /// Returns `true` if the lint is allowed in the current context. This is useful for
1714 /// skipping long running code when it's unnecessary
1716 /// This function should check the lint level for the same node, that the lint will
1717 /// be emitted at. If the information is buffered to be emitted at a later point, please
1718 /// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
1719 /// expectations at the checked nodes will be fulfilled.
1720 pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1721 cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
1724 pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1725 while let PatKind::Ref(subpat, _) = pat.kind {
1731 pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
1732 Integer::from_int_ty(&tcx, ity).size().bits()
1735 #[expect(clippy::cast_possible_wrap)]
1736 /// Turn a constant int byte representation into an i128
1737 pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
1738 let amt = 128 - int_bits(tcx, ity);
1739 ((u as i128) << amt) >> amt
1742 #[expect(clippy::cast_sign_loss)]
1743 /// clip unused bytes
1744 pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
1745 let amt = 128 - int_bits(tcx, ity);
1746 ((u as u128) << amt) >> amt
1749 /// clip unused bytes
1750 pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
1751 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1752 let amt = 128 - bits;
1756 pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool {
1757 attrs.iter().any(|attr| attr.has_name(symbol))
1760 pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1761 has_attr(cx.tcx.hir().attrs(hir_id), sym::repr)
1764 pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1765 let map = &tcx.hir();
1766 let mut prev_enclosing_node = None;
1767 let mut enclosing_node = node;
1768 while Some(enclosing_node) != prev_enclosing_node {
1769 if has_attr(map.attrs(enclosing_node), symbol) {
1772 prev_enclosing_node = Some(enclosing_node);
1773 enclosing_node = map.get_parent_item(enclosing_node).into();
1779 pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
1780 any_parent_has_attr(tcx, node, sym::automatically_derived)
1783 /// Matches a function call with the given path and returns the arguments.
1788 /// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
1790 /// This function is deprecated. Use [`match_function_call_with_def_id`].
1791 pub fn match_function_call<'tcx>(
1792 cx: &LateContext<'tcx>,
1793 expr: &'tcx Expr<'_>,
1795 ) -> Option<&'tcx [Expr<'tcx>]> {
1797 if let ExprKind::Call(fun, args) = expr.kind;
1798 if let ExprKind::Path(ref qpath) = fun.kind;
1799 if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
1800 if match_def_path(cx, fun_def_id, path);
1808 pub fn match_function_call_with_def_id<'tcx>(
1809 cx: &LateContext<'tcx>,
1810 expr: &'tcx Expr<'_>,
1812 ) -> Option<&'tcx [Expr<'tcx>]> {
1814 if let ExprKind::Call(fun, args) = expr.kind;
1815 if let ExprKind::Path(ref qpath) = fun.kind;
1816 if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id);
1824 /// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
1827 /// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
1828 pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
1829 let search_path = cx.get_def_path(did);
1832 .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
1835 /// Checks if the given `DefId` matches the path.
1836 pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool {
1837 // We should probably move to Symbols in Clippy as well rather than interning every time.
1838 let path = cx.get_def_path(did);
1839 syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
1842 /// Checks if the given `DefId` matches the `libc` item.
1843 pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
1844 let path = cx.get_def_path(did);
1845 // libc is meant to be used as a flat list of names, but they're all actually defined in different
1846 // modules based on the target platform. Ignore everything but crate name and the item name.
1847 path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
1850 /// Returns the list of condition expressions and the list of blocks in a
1851 /// sequence of `if/else`.
1852 /// E.g., this returns `([a, b], [c, d, e])` for the expression
1853 /// `if a { c } else if b { d } else { e }`.
1854 pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1855 let mut conds = Vec::new();
1856 let mut blocks: Vec<&Block<'_>> = Vec::new();
1858 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1860 if let ExprKind::Block(block, _) = then.kind {
1863 panic!("ExprKind::If node is not an ExprKind::Block");
1866 if let Some(else_expr) = r#else {
1873 // final `else {..}`
1874 if !blocks.is_empty() {
1875 if let ExprKind::Block(block, _) = expr.kind {
1883 /// Checks if the given function kind is an async function.
1884 pub fn is_async_fn(kind: FnKind<'_>) -> bool {
1886 FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async,
1887 FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async,
1888 FnKind::Closure => false,
1892 /// Peels away all the compiler generated code surrounding the body of an async function,
1893 pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1894 if let ExprKind::Call(
1898 kind: ExprKind::Closure(&Closure { body, .. }),
1904 if let ExprKind::Block(
1909 kind: ExprKind::DropTemps(expr),
1915 ) = tcx.hir().body(body).value.kind
1923 // check if expr is calling method or function with #[must_use] attribute
1924 pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1925 let did = match expr.kind {
1926 ExprKind::Call(path, _) => if_chain! {
1927 if let ExprKind::Path(ref qpath) = path.kind;
1928 if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
1935 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1939 did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
1942 /// Checks if an expression represents the identity function
1943 /// Only examines closures and `std::convert::identity`
1944 pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1945 /// Checks if a function's body represents the identity function. Looks for bodies of the form:
1947 /// * `|x| return x`
1948 /// * `|x| { return x }`
1949 /// * `|x| { return x; }`
1950 fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1951 let id = if_chain! {
1952 if let [param] = func.params;
1953 if let PatKind::Binding(_, id, _, _) = param.pat.kind;
1961 let mut expr = func.value;
1965 ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
1966 | ExprKind::Ret(Some(e)) => expr = e,
1968 ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
1970 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
1971 if let ExprKind::Ret(Some(ret_val)) = e.kind;
1979 _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
1985 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
1986 _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
1990 /// Gets the node where an expression is either used, or it's type is unified with another branch.
1991 /// Returns both the node and the `HirId` of the closest child node.
1992 pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
1993 let mut child_id = expr.hir_id;
1994 let mut iter = tcx.hir().parent_iter(child_id);
1998 Some((id, Node::Block(_))) => child_id = id,
1999 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2000 Some((_, Node::Expr(expr))) => match expr.kind {
2001 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2002 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2003 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2004 _ => break Some((Node::Expr(expr), child_id)),
2006 Some((_, node)) => break Some((node, child_id)),
2011 /// Checks if the result of an expression is used, or it's type is unified with another branch.
2012 pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2014 get_expr_use_or_unification_node(tcx, expr),
2017 kind: StmtKind::Expr(_)
2019 | StmtKind::Local(Local {
2021 kind: PatKind::Wild,
2033 /// Checks if the expression is the final expression returned from a block.
2034 pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2035 matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
2038 pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2039 if !is_no_std_crate(cx) {
2041 } else if !is_no_core_crate(cx) {
2048 pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2049 cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
2050 if let ast::AttrKind::Normal(ref normal) = attr.kind {
2051 normal.item.path == sym::no_std
2058 pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2059 cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
2060 if let ast::AttrKind::Normal(ref normal) = attr.kind {
2061 normal.item.path == sym::no_core
2068 /// Check if parent of a hir node is a trait implementation block.
2069 /// For example, `f` in
2072 /// # trait Trait { fn f(); }
2073 /// impl Trait for S {
2077 pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2078 if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) {
2079 matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
2085 /// Check if it's even possible to satisfy the `where` clause for the item.
2087 /// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
2090 /// fn foo() where i32: Iterator {
2091 /// for _ in 2i32 {}
2094 pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2095 use rustc_trait_selection::traits;
2101 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2102 traits::impossible_predicates(
2104 traits::elaborate_predicates(cx.tcx, predicates)
2105 .map(|o| o.predicate)
2106 .collect::<Vec<_>>(),
2110 /// Returns the `DefId` of the callee if the given expression is a function or method call.
2111 pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2113 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
2116 kind: ExprKind::Path(qpath),
2117 hir_id: path_hir_id,
2122 // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
2123 // deref to fn pointers, dyn Fn, impl Fn - #8850
2124 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2125 cx.typeck_results().qpath_res(qpath, *path_hir_id)
2136 /// Returns `Option<String>` where String is a textual representation of the type encapsulated in
2137 /// the slice iff the given expression is a slice of primitives (as defined in the
2138 /// `is_recursively_primitive_type` function) and `None` otherwise.
2139 pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2140 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2141 let expr_kind = expr_type.kind();
2142 let is_primitive = match expr_kind {
2143 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2144 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2145 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2146 is_recursively_primitive_type(*element_type)
2155 // if we have wrappers like Array, Slice or Tuple, print these
2156 // and get the type enclosed in the slice ref
2157 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2158 rustc_ty::Slice(..) => return Some("slice".into()),
2159 rustc_ty::Array(..) => return Some("array".into()),
2160 rustc_ty::Tuple(..) => return Some("tuple".into()),
2162 // is_recursively_primitive_type() should have taken care
2163 // of the rest and we can rely on the type that is found
2164 let refs_peeled = expr_type.peel_refs();
2165 return Some(refs_peeled.walk().last().unwrap().to_string());
2172 /// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
2173 /// `hash` must be comformed with `eq`
2174 pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
2176 Hash: Fn(&T) -> u64,
2177 Eq: Fn(&T, &T) -> bool,
2180 [a, b] if eq(a, b) => return vec![(a, b)],
2181 _ if exprs.len() <= 2 => return vec![],
2185 let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
2187 let mut map: UnhashMap<u64, Vec<&_>> =
2188 UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
2191 match map.entry(hash(expr)) {
2192 Entry::Occupied(mut o) => {
2195 match_expr_list.push((o, expr));
2198 o.get_mut().push(expr);
2200 Entry::Vacant(v) => {
2201 v.insert(vec![expr]);
2209 /// Peels off all references on the pattern. Returns the underlying pattern and the number of
2210 /// references removed.
2211 pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2212 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2213 if let PatKind::Ref(pat, _) = pat.kind {
2214 peel(pat, count + 1)
2222 /// Peels of expressions while the given closure returns `Some`.
2223 pub fn peel_hir_expr_while<'tcx>(
2224 mut expr: &'tcx Expr<'tcx>,
2225 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2226 ) -> &'tcx Expr<'tcx> {
2227 while let Some(e) = f(expr) {
2233 /// Peels off up to the given number of references on the expression. Returns the underlying
2234 /// expression and the number of references removed.
2235 pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2236 let mut remaining = count;
2237 let e = peel_hir_expr_while(expr, |e| match e.kind {
2238 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2244 (e, count - remaining)
2247 /// Peels off all references on the expression. Returns the underlying expression and the number of
2248 /// references removed.
2249 pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2251 let e = peel_hir_expr_while(expr, |e| match e.kind {
2252 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2261 /// Peels off all references on the type. Returns the underlying type and the number of references
2263 pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2267 TyKind::Ref(_, ref_ty) => {
2271 _ => break (ty, count),
2276 /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
2277 /// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
2278 pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2281 ExprKind::AddrOf(_, _, e) => expr = e,
2282 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2289 pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2290 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2291 if let Res::Def(_, def_id) = path.res {
2292 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2298 static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
2300 fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
2301 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2302 let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
2303 let value = map.entry(module);
2305 Entry::Occupied(entry) => f(entry.get()),
2306 Entry::Vacant(entry) => {
2307 let mut names = Vec::new();
2308 for id in tcx.hir().module_items(module) {
2309 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2310 && let item = tcx.hir().item(id)
2311 && let ItemKind::Const(ty, _body) = item.kind {
2312 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2313 // We could also check for the type name `test::TestDescAndFn`
2314 if let Res::Def(DefKind::Struct, _) = path.res {
2315 let has_test_marker = tcx
2317 .attrs(item.hir_id())
2319 .any(|a| a.has_name(sym::rustc_test_marker));
2320 if has_test_marker {
2321 names.push(item.ident.name);
2327 names.sort_unstable();
2328 f(entry.insert(names))
2333 /// Checks if the function containing the given `HirId` is a `#[test]` function
2335 /// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
2336 pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
2337 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2340 // Since you can nest functions we need to collect all until we leave
2342 .any(|(_id, node)| {
2343 if let Node::Item(item) = node {
2344 if let ItemKind::Fn(_, _, _) = item.kind {
2345 // Note that we have sorted the item names in the visitor,
2346 // so the binary_search gets the same as `contains`, but faster.
2347 return names.binary_search(&item.ident.name).is_ok();
2355 /// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
2357 /// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function
2358 pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
2359 fn is_cfg_test(attr: &Attribute) -> bool {
2360 if attr.has_name(sym::cfg)
2361 && let Some(items) = attr.meta_item_list()
2362 && let [item] = &*items
2363 && item.has_name(sym::test)
2372 .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id))
2376 /// Checks whether item either has `test` attribute applied, or
2377 /// is a module with `test` in its name.
2379 /// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
2380 pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
2381 is_in_test_function(tcx, item.hir_id())
2382 || matches!(item.kind, ItemKind::Mod(..))
2383 && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests")
2386 /// Walks the HIR tree from the given expression, up to the node where the value produced by the
2387 /// expression is consumed. Calls the function for every node encountered this way until it returns
2390 /// This allows walking through `if`, `match`, `break`, block expressions to find where the value
2391 /// produced by the expression is consumed.
2392 pub fn walk_to_expr_usage<'tcx, T>(
2393 cx: &LateContext<'tcx>,
2395 mut f: impl FnMut(Node<'tcx>, HirId) -> Option<T>,
2397 let map = cx.tcx.hir();
2398 let mut iter = map.parent_iter(e.hir_id);
2399 let mut child_id = e.hir_id;
2401 while let Some((parent_id, parent)) = iter.next() {
2402 if let Some(x) = f(parent, child_id) {
2405 let parent = match parent {
2407 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2408 child_id = parent_id;
2411 Node::Arm(a) if a.body.hir_id == child_id => {
2412 child_id = parent_id;
2418 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2419 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2421 iter = map.parent_iter(id);
2423 ExprKind::Block(..) => child_id = parent_id,
2430 /// Checks whether a given span has any comment token
2431 /// This checks for all types of comment: line "//", block "/**", doc "///" "//!"
2432 pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2433 let Ok(snippet) = sm.span_to_snippet(span) else { return false };
2434 return tokenize(&snippet).any(|token| {
2437 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2442 /// Return all the comments a given span contains
2443 /// Comments are returned wrapped with their relevant delimiters
2444 pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2445 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2446 let mut comments_buf: Vec<String> = Vec::new();
2447 let mut index: usize = 0;
2449 for token in tokenize(&snippet) {
2450 let token_range = index..(index + token.len as usize);
2451 index += token.len as usize;
2453 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => {
2454 if let Some(comment) = snippet.get(token_range) {
2455 comments_buf.push(comment.to_string());
2462 comments_buf.join("\n")
2465 macro_rules! op_utils {
2466 ($($name:ident $assign:ident)*) => {
2467 /// Binary operation traits like `LangItem::Add`
2468 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2470 /// Operator-Assign traits like `LangItem::AddAssign`
2471 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2473 /// Converts `BinOpKind::Add` to `(LangItem::Add, LangItem::AddAssign)`, for example
2474 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2476 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*