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 // The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate.
26 #[allow(unused_extern_crates)]
27 extern crate rustc_driver;
28 extern crate rustc_errors;
29 extern crate rustc_hir;
30 extern crate rustc_hir_typeck;
31 extern crate rustc_index;
32 extern crate rustc_infer;
33 extern crate rustc_lexer;
34 extern crate rustc_lint;
35 extern crate rustc_middle;
36 extern crate rustc_mir_dataflow;
37 extern crate rustc_parse_format;
38 extern crate rustc_session;
39 extern crate rustc_span;
40 extern crate rustc_target;
41 extern crate rustc_trait_selection;
52 pub mod eager_or_lazy;
58 pub mod numeric_literal;
61 pub mod qualify_min_const_fn;
69 pub use self::attrs::*;
70 pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
71 pub use self::hir_utils::{
72 both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
75 use core::ops::ControlFlow;
76 use std::collections::hash_map::Entry;
77 use std::hash::BuildHasherDefault;
78 use std::sync::OnceLock;
79 use std::sync::{Mutex, MutexGuard};
81 use if_chain::if_chain;
82 use rustc_ast::ast::{self, LitKind};
83 use rustc_ast::Attribute;
84 use rustc_data_structures::fx::FxHashMap;
85 use rustc_data_structures::unhash::UnhashMap;
86 use rustc_hir::def::{DefKind, Res};
87 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
88 use rustc_hir::hir_id::{HirIdMap, HirIdSet};
89 use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
90 use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
92 self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination,
93 Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local,
94 MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
95 TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp,
97 use rustc_lexer::{tokenize, TokenKind};
98 use rustc_lint::{LateContext, Level, Lint, LintContext};
99 use rustc_middle::hir::place::PlaceBase;
100 use rustc_middle::ty as rustc_ty;
101 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
102 use rustc_middle::ty::binding::BindingMode;
103 use rustc_middle::ty::fast_reject::SimplifiedType::{
104 ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
105 PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
107 use rustc_middle::ty::{
108 layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
110 use rustc_middle::ty::{FloatTy, IntTy, UintTy};
111 use rustc_span::hygiene::{ExpnKind, MacroKind};
112 use rustc_span::source_map::SourceMap;
114 use rustc_span::symbol::{kw, Ident, Symbol};
115 use rustc_span::Span;
116 use rustc_target::abi::Integer;
118 use crate::consts::{constant, Constant};
119 use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
120 use crate::visitors::for_each_expr;
122 use rustc_middle::hir::nested_filter;
125 macro_rules! extract_msrv_attr {
126 ($context:ident) => {
127 fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
128 let sess = rustc_lint::LintContext::sess(cx);
129 self.msrv.enter_lint_attrs(sess, attrs);
132 fn exit_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
133 let sess = rustc_lint::LintContext::sess(cx);
134 self.msrv.exit_lint_attrs(sess, attrs);
139 /// If the given expression is a local binding, find the initializer expression.
140 /// If that initializer expression is another local binding, find its initializer again.
141 /// This process repeats as long as possible (but usually no more than once). Initializer
142 /// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
155 /// let def = abc + 2;
156 /// // ^^^^^^^ output
160 pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
161 while let Some(init) = path_to_local(expr)
162 .and_then(|id| find_binding_init(cx, id))
163 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
170 /// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
171 /// By only considering immutable bindings, we guarantee that the returned expression represents the
172 /// value of the binding wherever it is referenced.
174 /// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
175 /// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
176 /// canonical binding `HirId`.
177 pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
178 let hir = cx.tcx.hir();
180 if let Some(Node::Pat(pat)) = hir.find(hir_id);
181 if matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..));
182 let parent = hir.parent_id(hir_id);
183 if let Some(Node::Local(local)) = hir.find(parent);
191 /// Returns `true` if the given `NodeId` is inside a constant context
196 /// if in_constant(cx, expr.hir_id) {
200 pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
201 let parent_id = cx.tcx.hir().get_parent_item(id).def_id;
202 match cx.tcx.hir().get_by_def_id(parent_id) {
204 kind: ItemKind::Const(..) | ItemKind::Static(..) | ItemKind::Enum(..),
207 | Node::TraitItem(&TraitItem {
208 kind: TraitItemKind::Const(..),
211 | Node::ImplItem(&ImplItem {
212 kind: ImplItemKind::Const(..),
215 | Node::AnonConst(_) => true,
217 kind: ItemKind::Fn(ref sig, ..),
220 | Node::ImplItem(&ImplItem {
221 kind: ImplItemKind::Fn(ref sig, _),
223 }) => sig.header.constness == Constness::Const,
228 /// Checks if a `Res` refers to a constructor of a `LangItem`
229 /// For example, use this to check whether a function call or a pattern is `Some(..)`.
230 pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
231 if let Res::Def(DefKind::Ctor(..), id) = res
232 && let Some(lang_id) = cx.tcx.lang_items().get(lang_item)
233 && let Some(id) = cx.tcx.opt_parent(id)
241 pub fn is_res_diagnostic_ctor(cx: &LateContext<'_>, res: Res, diag_item: Symbol) -> bool {
242 if let Res::Def(DefKind::Ctor(..), id) = res
243 && let Some(id) = cx.tcx.opt_parent(id)
245 cx.tcx.is_diagnostic_item(diag_item, id)
251 /// Checks if a `QPath` resolves to a constructor of a diagnostic item.
252 pub fn is_diagnostic_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, diagnostic_item: Symbol) -> bool {
253 if let QPath::Resolved(_, path) = qpath {
254 if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
255 return cx.tcx.is_diagnostic_item(diagnostic_item, cx.tcx.parent(ctor_id));
261 /// Checks if the `DefId` matches the given diagnostic item or it's constructor.
262 pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
263 let did = match cx.tcx.def_kind(did) {
264 DefKind::Ctor(..) => cx.tcx.parent(did),
265 // Constructors for types in external crates seem to have `DefKind::Variant`
266 DefKind::Variant => match cx.tcx.opt_parent(did) {
267 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
273 cx.tcx.is_diagnostic_item(item, did)
276 /// Checks if the `DefId` matches the given `LangItem` or it's constructor.
277 pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
278 let did = match cx.tcx.def_kind(did) {
279 DefKind::Ctor(..) => cx.tcx.parent(did),
280 // Constructors for types in external crates seem to have `DefKind::Variant`
281 DefKind::Variant => match cx.tcx.opt_parent(did) {
282 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
288 cx.tcx.lang_items().get(item) == Some(did)
291 pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
301 ) | ExprKind::Tup([])
305 /// Checks if given pattern is a wildcard (`_`)
306 pub fn is_wild(pat: &Pat<'_>) -> bool {
307 matches!(pat.kind, PatKind::Wild)
310 /// Checks if the method call given in `expr` belongs to the given trait.
311 /// This is a deprecated function, consider using [`is_trait_method`].
312 pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
313 let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
314 let trt_id = cx.tcx.trait_of_item(def_id);
315 trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
318 /// Checks if a method is defined in an impl of a diagnostic item
319 pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
320 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
321 if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
322 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
328 /// Checks if a method is in a diagnostic item trait
329 pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
330 if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
331 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
336 /// Checks if the method call given in `expr` belongs to the given trait.
337 pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
339 .type_dependent_def_id(expr.hir_id)
340 .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
343 /// Checks if the given expression is a path referring an item on the trait
344 /// that is marked with the given diagnostic item.
346 /// For checking method call expressions instead of path expressions, use
347 /// [`is_trait_method`].
349 /// For example, this can be used to find if an expression like `u64::default`
350 /// refers to an item of the trait `Default`, which is associated with the
351 /// `diag_item` of `sym::Default`.
352 pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
353 if let hir::ExprKind::Path(ref qpath) = expr.kind {
354 cx.qpath_res(qpath, expr.hir_id)
356 .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
362 pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
364 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
365 QPath::TypeRelative(_, seg) => seg,
366 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
370 pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
371 last_path_segment(qpath)
373 .map_or(&[][..], |a| a.args)
375 .filter_map(|a| match a {
376 hir::GenericArg::Type(ty) => Some(*ty),
381 /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
382 /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
383 /// `QPath::Resolved.1.res.opt_def_id()`.
385 /// Matches a `QPath` against a slice of segment string literals.
387 /// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
388 /// `rustc_hir::QPath`.
392 /// match_qpath(path, &["std", "rt", "begin_unwind"])
394 pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
396 QPath::Resolved(_, path) => match_path(path, segments),
397 QPath::TypeRelative(ty, segment) => match ty.kind {
398 TyKind::Path(ref inner_path) => {
399 if let [prefix @ .., end] = segments {
400 if match_qpath(inner_path, prefix) {
401 return segment.ident.name.as_str() == *end;
408 QPath::LangItem(..) => false,
412 /// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
414 /// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
415 pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
416 path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
419 /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
420 /// it matches the given lang item.
421 pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
422 path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id))
425 /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
426 /// it matches the given diagnostic item.
427 pub fn is_path_diagnostic_item<'tcx>(
428 cx: &LateContext<'_>,
429 maybe_path: &impl MaybePath<'tcx>,
432 path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
435 /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
436 /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
437 /// `QPath::Resolved.1.res.opt_def_id()`.
439 /// Matches a `Path` against a slice of segment string literals.
441 /// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
442 /// `rustc_hir::Path`.
447 /// if match_path(&trait_ref.path, &paths::HASH) {
448 /// // This is the `std::hash::Hash` trait.
451 /// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
452 /// // This is a `rustc_middle::lint::Lint`.
455 pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
459 .zip(segments.iter().rev())
460 .all(|(a, b)| a.ident.name.as_str() == *b)
463 /// If the expression is a path to a local, returns the canonical `HirId` of the local.
464 pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
465 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
466 if let Res::Local(id) = path.res {
473 /// Returns true if the expression is a path to a local with the specified `HirId`.
474 /// Use this function to see if an expression matches a function argument or a match binding.
475 pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
476 path_to_local(expr) == Some(id)
479 pub trait MaybePath<'hir> {
480 fn hir_id(&self) -> HirId;
481 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
484 macro_rules! maybe_path {
485 ($ty:ident, $kind:ident) => {
486 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
487 fn hir_id(&self) -> HirId {
490 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
492 hir::$kind::Path(qpath) => Some(qpath),
499 maybe_path!(Expr, ExprKind);
500 maybe_path!(Pat, PatKind);
501 maybe_path!(Ty, TyKind);
503 /// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
504 pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
505 match maybe_path.qpath_opt() {
507 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
511 /// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
512 pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
513 path_res(cx, maybe_path).opt_def_id()
516 fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
517 let ty = match name {
518 "bool" => BoolSimplifiedType,
519 "char" => CharSimplifiedType,
520 "str" => StrSimplifiedType,
521 "array" => ArraySimplifiedType,
522 "slice" => SliceSimplifiedType,
523 // FIXME: rustdoc documents these two using just `pointer`.
525 // Maybe this is something we should do here too.
526 "const_ptr" => PtrSimplifiedType(Mutability::Not),
527 "mut_ptr" => PtrSimplifiedType(Mutability::Mut),
528 "isize" => IntSimplifiedType(IntTy::Isize),
529 "i8" => IntSimplifiedType(IntTy::I8),
530 "i16" => IntSimplifiedType(IntTy::I16),
531 "i32" => IntSimplifiedType(IntTy::I32),
532 "i64" => IntSimplifiedType(IntTy::I64),
533 "i128" => IntSimplifiedType(IntTy::I128),
534 "usize" => UintSimplifiedType(UintTy::Usize),
535 "u8" => UintSimplifiedType(UintTy::U8),
536 "u16" => UintSimplifiedType(UintTy::U16),
537 "u32" => UintSimplifiedType(UintTy::U32),
538 "u64" => UintSimplifiedType(UintTy::U64),
539 "u128" => UintSimplifiedType(UintTy::U128),
540 "f32" => FloatSimplifiedType(FloatTy::F32),
541 "f64" => FloatSimplifiedType(FloatTy::F64),
542 _ => return [].iter().copied(),
545 tcx.incoherent_impls(ty).iter().copied()
548 fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
549 match tcx.def_kind(def_id) {
550 DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
551 .module_children(def_id)
553 .filter(|item| item.ident.name == name)
554 .map(|child| child.res.expect_non_local())
557 .associated_item_def_ids(def_id)
560 .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
561 .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
567 fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
571 let item_kind = match hir.find_by_def_id(local_id) {
572 Some(Node::Crate(r#mod)) => {
573 root_mod = ItemKind::Mod(r#mod);
576 Some(Node::Item(item)) => &item.kind,
577 _ => return Vec::new(),
580 let res = |ident: Ident, owner_id: OwnerId| {
581 if ident.name == name {
582 let def_id = owner_id.to_def_id();
583 Some(Res::Def(tcx.def_kind(def_id), def_id))
590 ItemKind::Mod(r#mod) => r#mod
593 .filter_map(|&item_id| res(hir.item(item_id).ident, item_id.owner_id))
595 ItemKind::Impl(r#impl) => r#impl
598 .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
600 ItemKind::Trait(.., trait_item_refs) => trait_item_refs
602 .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
608 fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
609 if let Some(local_id) = def_id.as_local() {
610 local_item_children_by_name(tcx, local_id, name)
612 non_local_item_children_by_name(tcx, def_id, name)
616 /// Resolves a def path like `std::vec::Vec`.
618 /// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
619 /// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
621 /// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec`
622 /// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
624 /// This function is expensive and should be used sparingly.
625 pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> {
626 fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator<Item = DefId> + '_ {
630 .filter(move |&num| tcx.crate_name(num) == name)
631 .map(CrateNum::as_def_id)
636 let (base, mut path) = match *path {
638 return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
640 [base, ref path @ ..] => (base, path),
641 _ => return Vec::new(),
644 let base_sym = Symbol::intern(base);
646 let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
647 Some(LOCAL_CRATE.as_def_id())
652 let starts = find_primitive_impls(tcx, base)
653 .chain(find_crates(tcx, base_sym))
655 .map(|id| Res::Def(tcx.def_kind(id), id));
657 let mut resolutions: Vec<Res> = starts.collect();
659 while let [segment, rest @ ..] = path {
661 let segment = Symbol::intern(segment);
663 resolutions = resolutions
665 .filter_map(|res| res.opt_def_id())
667 // When the current def_id is e.g. `struct S`, check the impl items in
669 let inherent_impl_children = tcx
670 .inherent_impls(def_id)
672 .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
674 let direct_children = item_children_by_name(tcx, def_id, segment);
676 inherent_impl_children.chain(direct_children)
684 /// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`].
685 pub fn def_path_def_ids(cx: &LateContext<'_>, path: &[&str]) -> impl Iterator<Item = DefId> {
686 def_path_res(cx, path).into_iter().filter_map(|res| res.opt_def_id())
689 /// Convenience function to get the `DefId` of a trait by path.
690 /// It could be a trait or trait alias.
692 /// This function is expensive and should be used sparingly.
693 pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
694 def_path_res(cx, path).into_iter().find_map(|res| match res {
695 Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
700 /// Gets the `hir::TraitRef` of the trait the given method is implemented for.
702 /// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
705 /// struct Point(isize, isize);
707 /// impl std::ops::Add for Point {
708 /// type Output = Self;
710 /// fn add(self, other: Self) -> Self {
715 pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
716 // Get the implemented trait for the current function
717 let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
718 let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
720 if parent_impl != hir::CRATE_OWNER_ID;
721 if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl.def_id);
722 if let hir::ItemKind::Impl(impl_) = &item.kind;
724 return impl_.of_trait.as_ref();
730 /// This method will return tuple of projection stack and root of the expression,
731 /// used in `can_mut_borrow_both`.
733 /// For example, if `e` represents the `v[0].a.b[x]`
734 /// this method will return a tuple, composed of a `Vec`
735 /// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
736 /// and an `Expr` for root of them, `v`
737 fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
738 let mut result = vec![];
741 ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
752 /// Gets the mutability of the custom deref adjustment, if any.
753 pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
757 .find_map(|a| match a.kind {
758 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
759 Adjust::Deref(None) => None,
765 /// Checks if two expressions can be mutably borrowed simultaneously
766 /// and they aren't dependent on borrowing same thing twice
767 pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
768 let (s1, r1) = projection_stack(e1);
769 let (s2, r2) = projection_stack(e2);
770 if !eq_expr_value(cx, r1, r2) {
773 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
777 for (x1, x2) in s1.iter().zip(s2.iter()) {
778 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
782 match (&x1.kind, &x2.kind) {
783 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
788 (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
789 if !eq_expr_value(cx, i1, i2) {
799 /// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
800 /// constructor from the std library
801 fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
802 let std_types_symbols = &[
813 if let QPath::TypeRelative(_, method) = path {
814 if method.ident.name == sym::new {
815 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
816 if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
817 return std_types_symbols.iter().any(|&symbol| {
818 cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
827 /// Return true if the expr is equal to `Default::default` when evaluated.
828 pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
830 if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
831 if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
832 if is_diag_trait_item(cx, repl_def_id, sym::Default)
833 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
834 then { true } else { false }
838 /// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
839 /// It doesn't cover all cases, for example indirect function calls (some of std
840 /// functions are supported) but it is the best we have.
841 pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
843 ExprKind::Lit(lit) => match lit.node {
844 LitKind::Bool(false) | LitKind::Int(0, _) => true,
845 LitKind::Str(s, _) => s.is_empty(),
848 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
849 ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
850 if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
851 if let LitKind::Int(v, _) = const_lit.node;
852 if v <= 32 && is_default_equivalent(cx, x);
860 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func),
861 ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg),
862 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
863 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
868 fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
869 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind &&
870 seg.ident.name == sym::from
873 ExprKind::Lit(hir::Lit {
874 node: LitKind::Str(ref sym, _),
876 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
877 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
878 ExprKind::Repeat(_, ArrayLen::Body(len)) => {
879 if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind &&
880 let LitKind::Int(v, _) = const_lit.node
882 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
891 /// Checks if the top level expression can be moved into a closure as is.
892 /// Currently checks for:
893 /// * Break/Continue outside the given loop HIR ids.
894 /// * Yield/Return statements.
895 /// * Inline assembly.
896 /// * Usages of a field of a local where the type of the local can be partially moved.
898 /// For example, given the following function:
901 /// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
902 /// for item in iter {
913 /// When called on the expression `item.0` this will return false unless the local `item` is in the
914 /// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
915 /// isn't always safe to move into a closure when only a single field is needed.
917 /// When called on the `continue` expression this will return false unless the outer loop expression
918 /// is in the `loop_ids` set.
920 /// Note that this check is not recursive, so passing the `if` expression will always return true
921 /// even though sub-expressions might return false.
922 pub fn can_move_expr_to_closure_no_visit<'tcx>(
923 cx: &LateContext<'tcx>,
924 expr: &'tcx Expr<'_>,
926 ignore_locals: &HirIdSet,
929 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
930 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
931 if loop_ids.contains(&id) =>
936 | ExprKind::Continue(_)
938 | ExprKind::Yield(..)
939 | ExprKind::InlineAsm(_) => false,
940 // Accessing a field of a local value can only be done if the type isn't
946 ExprKind::Path(QPath::Resolved(
949 res: Res::Local(local_id),
956 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
957 // TODO: check if the local has been partially moved. Assume it has for now.
964 /// How a local is captured by a closure
965 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
966 pub enum CaptureKind {
971 pub fn is_imm_ref(self) -> bool {
972 self == Self::Ref(Mutability::Not)
975 impl std::ops::BitOr for CaptureKind {
977 fn bitor(self, rhs: Self) -> Self::Output {
979 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
980 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
981 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
982 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
986 impl std::ops::BitOrAssign for CaptureKind {
987 fn bitor_assign(&mut self, rhs: Self) {
992 /// Given an expression referencing a local, determines how it would be captured in a closure.
993 /// Note as this will walk up to parent expressions until the capture can be determined it should
994 /// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
995 /// function argument (other than a receiver).
996 pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
997 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
998 let mut capture = CaptureKind::Ref(Mutability::Not);
999 pat.each_binding_or_first(&mut |_, id, span, _| match cx
1001 .extract_binding_mode(cx.sess(), id, span)
1004 BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
1005 capture = CaptureKind::Value;
1007 BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
1008 capture = CaptureKind::Ref(Mutability::Mut);
1015 debug_assert!(matches!(
1017 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
1020 let mut child_id = e.hir_id;
1021 let mut capture = CaptureKind::Value;
1022 let mut capture_expr_ty = e;
1024 for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
1027 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
1035 .map_or(&[][..], |x| &**x)
1037 if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
1038 *adjust.last().map_or(target, |a| a.target).kind()
1040 return CaptureKind::Ref(mutability);
1045 Node::Expr(e) => match e.kind {
1046 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
1047 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
1048 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
1049 return CaptureKind::Ref(Mutability::Mut);
1051 ExprKind::Field(..) => {
1052 if capture == CaptureKind::Value {
1053 capture_expr_ty = e;
1056 ExprKind::Let(let_expr) => {
1057 let mutability = match pat_capture_kind(cx, let_expr.pat) {
1058 CaptureKind::Value => Mutability::Not,
1059 CaptureKind::Ref(m) => m,
1061 return CaptureKind::Ref(mutability);
1063 ExprKind::Match(_, arms, _) => {
1064 let mut mutability = Mutability::Not;
1065 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
1067 CaptureKind::Value => break,
1068 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
1069 CaptureKind::Ref(Mutability::Not) => (),
1072 return CaptureKind::Ref(mutability);
1076 Node::Local(l) => match pat_capture_kind(cx, l.pat) {
1077 CaptureKind::Value => break,
1078 capture @ CaptureKind::Ref(_) => return capture,
1083 child_id = parent_id;
1086 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
1087 // Copy types are never automatically captured by value.
1088 CaptureKind::Ref(Mutability::Not)
1094 /// Checks if the expression can be moved into a closure as is. This will return a list of captures
1095 /// if so, otherwise, `None`.
1096 pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
1097 struct V<'cx, 'tcx> {
1098 cx: &'cx LateContext<'tcx>,
1099 // Stack of potential break targets contained in the expression.
1101 /// Local variables created in the expression. These don't need to be captured.
1103 /// Whether this expression can be turned into a closure.
1104 allow_closure: bool,
1105 /// Locals which need to be captured, and whether they need to be by value, reference, or
1106 /// mutable reference.
1107 captures: HirIdMap<CaptureKind>,
1109 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1110 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1111 if !self.allow_closure {
1116 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1117 if !self.locals.contains(&l) {
1118 let cap = capture_local_usage(self.cx, e);
1119 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1122 ExprKind::Closure { .. } => {
1123 let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id);
1124 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
1125 let local_id = match capture.place.base {
1126 PlaceBase::Local(id) => id,
1127 PlaceBase::Upvar(var) => var.var_path.hir_id,
1130 if !self.locals.contains(&local_id) {
1131 let capture = match capture.info.capture_kind {
1132 UpvarCapture::ByValue => CaptureKind::Value,
1133 UpvarCapture::ByRef(kind) => match kind {
1134 BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
1135 BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
1136 CaptureKind::Ref(Mutability::Mut)
1142 .and_modify(|e| *e |= capture)
1143 .or_insert(capture);
1147 ExprKind::Loop(b, ..) => {
1148 self.loops.push(e.hir_id);
1149 self.visit_block(b);
1153 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1159 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1160 p.each_binding_or_first(&mut |_, id, _, _| {
1161 self.locals.insert(id);
1168 allow_closure: true,
1170 locals: HirIdSet::default(),
1171 captures: HirIdMap::default(),
1174 v.allow_closure.then_some(v.captures)
1177 /// Arguments of a method: the receiver and all the additional arguments.
1178 pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1180 /// Returns the method names and argument list of nested method call expressions that make up
1181 /// `expr`. method/span lists are sorted with the most recent call first.
1182 pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1183 let mut method_names = Vec::with_capacity(max_depth);
1184 let mut arg_lists = Vec::with_capacity(max_depth);
1185 let mut spans = Vec::with_capacity(max_depth);
1187 let mut current = expr;
1188 for _ in 0..max_depth {
1189 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1190 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1193 method_names.push(path.ident.name);
1194 arg_lists.push((*receiver, &**args));
1195 spans.push(path.ident.span);
1202 (method_names, arg_lists, spans)
1205 /// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
1207 /// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
1208 /// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
1209 /// containing the `Expr`s for
1210 /// `.bar()` and `.baz()`
1211 pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1212 let mut current = expr;
1213 let mut matched = Vec::with_capacity(methods.len());
1214 for method_name in methods.iter().rev() {
1215 // method chains are stored last -> first
1216 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1217 if path.ident.name.as_str() == *method_name {
1218 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1221 matched.push((receiver, args)); // build up `matched` backwards
1222 current = receiver; // go to parent expression
1230 // Reverse `matched` so that it is in the same order as `methods`.
1235 /// Returns `true` if the provided `def_id` is an entrypoint to a program.
1236 pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1239 .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1242 /// Returns `true` if the expression is in the program's `#[panic_handler]`.
1243 pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1244 let parent = cx.tcx.hir().get_parent_item(e.hir_id);
1245 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1248 /// Gets the name of the item the expression is in, if available.
1249 pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1250 let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id).def_id;
1251 match cx.tcx.hir().find_by_def_id(parent_id) {
1253 Node::Item(Item { ident, .. })
1254 | Node::TraitItem(TraitItem { ident, .. })
1255 | Node::ImplItem(ImplItem { ident, .. }),
1256 ) => Some(ident.name),
1261 pub struct ContainsName<'a, 'tcx> {
1262 pub cx: &'a LateContext<'tcx>,
1267 impl<'a, 'tcx> Visitor<'tcx> for ContainsName<'a, 'tcx> {
1268 type NestedFilter = nested_filter::OnlyBodies;
1270 fn visit_name(&mut self, name: Symbol) {
1271 if self.name == name {
1276 fn nested_visit_map(&mut self) -> Self::Map {
1281 /// Checks if an `Expr` contains a certain name.
1282 pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
1283 let mut cn = ContainsName {
1288 cn.visit_expr(expr);
1292 /// Returns `true` if `expr` contains a return expression
1293 pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
1294 for_each_expr(expr, |e| {
1295 if matches!(e.kind, hir::ExprKind::Ret(..)) {
1296 ControlFlow::Break(())
1298 ControlFlow::Continue(())
1304 /// Gets the parent node, if any.
1305 pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
1306 tcx.hir().find_parent(id)
1309 /// Gets the parent expression, if any –- this is useful to constrain a lint.
1310 pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1311 get_parent_expr_for_hir(cx, e.hir_id)
1314 /// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
1315 /// constraint lints
1316 pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
1317 match get_parent_node(cx.tcx, hir_id) {
1318 Some(Node::Expr(parent)) => Some(parent),
1323 /// Gets the enclosing block, if any.
1324 pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1325 let map = &cx.tcx.hir();
1326 let enclosing_node = map
1327 .get_enclosing_scope(hir_id)
1328 .and_then(|enclosing_id| map.find(enclosing_id));
1329 enclosing_node.and_then(|node| match node {
1330 Node::Block(block) => Some(block),
1332 kind: ItemKind::Fn(_, _, eid),
1335 | Node::ImplItem(&ImplItem {
1336 kind: ImplItemKind::Fn(_, eid),
1338 }) => match cx.tcx.hir().body(eid).value.kind {
1339 ExprKind::Block(block, _) => Some(block),
1346 /// Gets the loop or closure enclosing the given expression, if any.
1347 pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1348 cx: &LateContext<'tcx>,
1350 ) -> Option<&'tcx Expr<'tcx>> {
1351 for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
1353 Node::Expr(e) => match e.kind {
1354 ExprKind::Closure { .. } => {
1355 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1356 && subs.as_closure().kind() == ClosureKind::FnOnce
1360 let is_once = walk_to_expr_usage(cx, e, |node, id| {
1361 let Node::Expr(e) = node else {
1365 ExprKind::Call(f, _) if f.hir_id == id => Some(()),
1366 ExprKind::Call(f, args) => {
1367 let i = args.iter().position(|arg| arg.hir_id == id)?;
1368 let sig = expr_sig(cx, f)?;
1369 let predicates = sig
1371 .map_or(cx.param_env, |id| cx.tcx.param_env(id))
1373 sig.input(i).and_then(|ty| {
1374 ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
1377 ExprKind::MethodCall(_, receiver, args, _) => {
1378 let i = std::iter::once(receiver)
1380 .position(|arg| arg.hir_id == id)?;
1381 let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
1382 let ty = cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i];
1383 ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
1393 ExprKind::Loop(..) => return Some(e),
1396 Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
1403 /// Gets the parent node if it's an impl block.
1404 pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1405 match tcx.hir().parent_iter(id).next() {
1409 kind: ItemKind::Impl(imp),
1417 /// Removes blocks around an expression, only if the block contains just one expression
1418 /// and no statements. Unsafe blocks are not removed.
1422 /// * `{ x }` -> `x`
1423 /// * `{{ x }}` -> `x`
1424 /// * `{ x; }` -> `{ x; }`
1425 /// * `{ x; y }` -> `{ x; y }`
1426 /// * `{ unsafe { x } }` -> `unsafe { x }`
1427 pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1428 while let ExprKind::Block(
1432 rules: BlockCheckMode::DefaultBlock,
1443 /// Removes blocks around an expression, only if the block contains just one expression
1444 /// or just one expression statement with a semicolon. Unsafe blocks are not removed.
1448 /// * `{ x }` -> `x`
1449 /// * `{ x; }` -> `x`
1450 /// * `{{ x; }}` -> `x`
1451 /// * `{ x; y }` -> `{ x; y }`
1452 /// * `{ unsafe { x } }` -> `unsafe { x }`
1453 pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1454 while let ExprKind::Block(
1458 rules: BlockCheckMode::DefaultBlock,
1465 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1470 rules: BlockCheckMode::DefaultBlock,
1481 /// Checks if the given expression is the else clause of either an `if` or `if let` expression.
1482 pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1483 let mut iter = tcx.hir().parent_iter(expr.hir_id);
1488 kind: ExprKind::If(_, _, Some(else_expr)),
1491 )) => else_expr.hir_id == expr.hir_id,
1496 /// Checks whether the given expression is a constant integer of the given value.
1497 /// unlike `is_integer_literal`, this version does const folding
1498 pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1499 if is_integer_literal(e, value) {
1502 let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
1503 if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
1509 /// Checks whether the given expression is a constant literal of the given value.
1510 pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1511 // FIXME: use constant folding
1512 if let ExprKind::Lit(ref spanned) = expr.kind {
1513 if let LitKind::Int(v, _) = spanned.node {
1520 /// Returns `true` if the given `Expr` has been coerced before.
1522 /// Examples of coercions can be found in the Nomicon at
1523 /// <https://doc.rust-lang.org/nomicon/coercions.html>.
1525 /// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for
1526 /// more information on adjustments and coercions.
1527 pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1528 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1531 /// Returns the pre-expansion span if this comes from an expansion of the
1533 /// See also [`is_direct_expn_of`].
1535 pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
1537 if span.from_expansion() {
1538 let data = span.ctxt().outer_expn_data();
1539 let new_span = data.call_site;
1541 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
1542 if mac_name.as_str() == name {
1543 return Some(new_span);
1554 /// Returns the pre-expansion span if the span directly comes from an expansion
1555 /// of the macro `name`.
1556 /// The difference with [`is_expn_of`] is that in
1558 /// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
1559 /// # macro_rules! bar { ($e:expr) => { $e } }
1562 /// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
1563 /// from `bar!` by `is_direct_expn_of`.
1565 pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
1566 if span.from_expansion() {
1567 let data = span.ctxt().outer_expn_data();
1568 let new_span = data.call_site;
1570 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
1571 if mac_name.as_str() == name {
1572 return Some(new_span);
1580 /// Convenience function to get the return type of a function.
1581 pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
1582 let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
1583 let ret_ty = cx.tcx.fn_sig(fn_def_id).subst_identity().output();
1584 cx.tcx.erase_late_bound_regions(ret_ty)
1587 /// Convenience function to get the nth argument type of a function.
1588 pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId, nth: usize) -> Ty<'tcx> {
1589 let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
1590 let arg = cx.tcx.fn_sig(fn_def_id).subst_identity().input(nth);
1591 cx.tcx.erase_late_bound_regions(arg)
1594 /// Checks if an expression is constructing a tuple-like enum variant or struct
1595 pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1596 if let ExprKind::Call(fun, _) = expr.kind {
1597 if let ExprKind::Path(ref qp) = fun.kind {
1598 let res = cx.qpath_res(qp, fun.hir_id);
1600 def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1601 def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1609 /// Returns `true` if a pattern is refutable.
1610 // TODO: should be implemented using rustc/mir_build/thir machinery
1611 pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1612 fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1614 cx.qpath_res(qpath, id),
1615 def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
1619 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1620 i.into_iter().any(|pat| is_refutable(cx, pat))
1624 PatKind::Wild => false,
1625 PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
1626 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1627 PatKind::Lit(..) | PatKind::Range(..) => true,
1628 PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
1629 PatKind::Or(pats) => {
1630 // TODO: should be the honest check, that pats is exhaustive set
1631 are_refutable(cx, pats)
1633 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1634 PatKind::Struct(ref qpath, fields, _) => {
1635 is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1637 PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
1638 PatKind::Slice(head, middle, tail) => {
1639 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1640 rustc_ty::Slice(..) => {
1641 // [..] is the only irrefutable slice pattern.
1642 !head.is_empty() || middle.is_none() || !tail.is_empty()
1644 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1654 /// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
1655 /// the function once on the given pattern.
1656 pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1657 if let PatKind::Or(pats) = pat.kind {
1658 pats.iter().for_each(f);
1664 pub fn is_self(slf: &Param<'_>) -> bool {
1665 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1666 name.name == kw::SelfLower
1672 pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1673 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
1674 if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res {
1681 pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1682 (0..decl.inputs.len()).map(move |i| &body.params[i])
1685 /// Checks if a given expression is a match expression expanded from the `?`
1686 /// operator or the `try` macro.
1687 pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1688 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1690 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind;
1691 if ddpos.as_opt_usize().is_none();
1692 if is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk);
1693 if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
1694 if path_to_local_id(arm.body, hir_id);
1702 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1703 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1704 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1710 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1711 // desugared from a `?` operator
1712 if *source == MatchSource::TryDesugar {
1718 if arms[0].guard.is_none();
1719 if arms[1].guard.is_none();
1720 if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
1730 /// Returns `true` if the lint is allowed in the current context. This is useful for
1731 /// skipping long running code when it's unnecessary
1733 /// This function should check the lint level for the same node, that the lint will
1734 /// be emitted at. If the information is buffered to be emitted at a later point, please
1735 /// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
1736 /// expectations at the checked nodes will be fulfilled.
1737 pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1738 cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
1741 pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1742 while let PatKind::Ref(subpat, _) = pat.kind {
1748 pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
1749 Integer::from_int_ty(&tcx, ity).size().bits()
1752 #[expect(clippy::cast_possible_wrap)]
1753 /// Turn a constant int byte representation into an i128
1754 pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
1755 let amt = 128 - int_bits(tcx, ity);
1756 ((u as i128) << amt) >> amt
1759 #[expect(clippy::cast_sign_loss)]
1760 /// clip unused bytes
1761 pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
1762 let amt = 128 - int_bits(tcx, ity);
1763 ((u as u128) << amt) >> amt
1766 /// clip unused bytes
1767 pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
1768 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1769 let amt = 128 - bits;
1773 pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool {
1774 attrs.iter().any(|attr| attr.has_name(symbol))
1777 pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1778 has_attr(cx.tcx.hir().attrs(hir_id), sym::repr)
1781 pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1782 let map = &tcx.hir();
1783 let mut prev_enclosing_node = None;
1784 let mut enclosing_node = node;
1785 while Some(enclosing_node) != prev_enclosing_node {
1786 if has_attr(map.attrs(enclosing_node), symbol) {
1789 prev_enclosing_node = Some(enclosing_node);
1790 enclosing_node = map.get_parent_item(enclosing_node).into();
1796 pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
1797 any_parent_has_attr(tcx, node, sym::automatically_derived)
1800 /// Matches a function call with the given path and returns the arguments.
1805 /// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
1807 /// This function is deprecated. Use [`match_function_call_with_def_id`].
1808 pub fn match_function_call<'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 let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
1817 if match_def_path(cx, fun_def_id, path);
1825 pub fn match_function_call_with_def_id<'tcx>(
1826 cx: &LateContext<'tcx>,
1827 expr: &'tcx Expr<'_>,
1829 ) -> Option<&'tcx [Expr<'tcx>]> {
1831 if let ExprKind::Call(fun, args) = expr.kind;
1832 if let ExprKind::Path(ref qpath) = fun.kind;
1833 if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id);
1841 /// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
1844 /// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
1845 pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
1846 let search_path = cx.get_def_path(did);
1849 .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
1852 /// Checks if the given `DefId` matches the path.
1853 pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool {
1854 // We should probably move to Symbols in Clippy as well rather than interning every time.
1855 let path = cx.get_def_path(did);
1856 syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
1859 /// Checks if the given `DefId` matches the `libc` item.
1860 pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
1861 let path = cx.get_def_path(did);
1862 // libc is meant to be used as a flat list of names, but they're all actually defined in different
1863 // modules based on the target platform. Ignore everything but crate name and the item name.
1864 path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
1867 /// Returns the list of condition expressions and the list of blocks in a
1868 /// sequence of `if/else`.
1869 /// E.g., this returns `([a, b], [c, d, e])` for the expression
1870 /// `if a { c } else if b { d } else { e }`.
1871 pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1872 let mut conds = Vec::new();
1873 let mut blocks: Vec<&Block<'_>> = Vec::new();
1875 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1877 if let ExprKind::Block(block, _) = then.kind {
1880 panic!("ExprKind::If node is not an ExprKind::Block");
1883 if let Some(else_expr) = r#else {
1890 // final `else {..}`
1891 if !blocks.is_empty() {
1892 if let ExprKind::Block(block, _) = expr.kind {
1900 /// Checks if the given function kind is an async function.
1901 pub fn is_async_fn(kind: FnKind<'_>) -> bool {
1903 FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async,
1904 FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async,
1905 FnKind::Closure => false,
1909 /// Peels away all the compiler generated code surrounding the body of an async function,
1910 pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1911 if let ExprKind::Call(
1915 kind: ExprKind::Closure(&Closure { body, .. }),
1921 if let ExprKind::Block(
1926 kind: ExprKind::DropTemps(expr),
1932 ) = tcx.hir().body(body).value.kind
1940 // check if expr is calling method or function with #[must_use] attribute
1941 pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1942 let did = match expr.kind {
1943 ExprKind::Call(path, _) => if_chain! {
1944 if let ExprKind::Path(ref qpath) = path.kind;
1945 if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
1952 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1956 did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
1959 /// Checks if an expression represents the identity function
1960 /// Only examines closures and `std::convert::identity`
1961 pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1962 /// Checks if a function's body represents the identity function. Looks for bodies of the form:
1964 /// * `|x| return x`
1965 /// * `|x| { return x }`
1966 /// * `|x| { return x; }`
1967 fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1968 let id = if_chain! {
1969 if let [param] = func.params;
1970 if let PatKind::Binding(_, id, _, _) = param.pat.kind;
1978 let mut expr = func.value;
1982 ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
1983 | ExprKind::Ret(Some(e)) => expr = e,
1985 ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
1987 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
1988 if let ExprKind::Ret(Some(ret_val)) = e.kind;
1996 _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
2002 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
2003 _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
2007 /// Gets the node where an expression is either used, or it's type is unified with another branch.
2008 /// Returns both the node and the `HirId` of the closest child node.
2009 pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2010 let mut child_id = expr.hir_id;
2011 let mut iter = tcx.hir().parent_iter(child_id);
2015 Some((id, Node::Block(_))) => child_id = id,
2016 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2017 Some((_, Node::Expr(expr))) => match expr.kind {
2018 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2019 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2020 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2021 _ => break Some((Node::Expr(expr), child_id)),
2023 Some((_, node)) => break Some((node, child_id)),
2028 /// Checks if the result of an expression is used, or it's type is unified with another branch.
2029 pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2031 get_expr_use_or_unification_node(tcx, expr),
2034 kind: StmtKind::Expr(_)
2036 | StmtKind::Local(Local {
2038 kind: PatKind::Wild,
2050 /// Checks if the expression is the final expression returned from a block.
2051 pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2052 matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
2055 pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2056 if !is_no_std_crate(cx) {
2058 } else if !is_no_core_crate(cx) {
2065 pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2066 cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
2067 if let ast::AttrKind::Normal(ref normal) = attr.kind {
2068 normal.item.path == sym::no_std
2075 pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2076 cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
2077 if let ast::AttrKind::Normal(ref normal) = attr.kind {
2078 normal.item.path == sym::no_core
2085 /// Check if parent of a hir node is a trait implementation block.
2086 /// For example, `f` in
2089 /// # trait Trait { fn f(); }
2090 /// impl Trait for S {
2094 pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2095 if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) {
2096 matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
2102 /// Check if it's even possible to satisfy the `where` clause for the item.
2104 /// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
2107 /// fn foo() where i32: Iterator {
2108 /// for _ in 2i32 {}
2111 pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2112 use rustc_trait_selection::traits;
2118 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2119 traits::impossible_predicates(
2121 traits::elaborate_predicates(cx.tcx, predicates)
2122 .map(|o| o.predicate)
2123 .collect::<Vec<_>>(),
2127 /// Returns the `DefId` of the callee if the given expression is a function or method call.
2128 pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2130 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
2133 kind: ExprKind::Path(qpath),
2134 hir_id: path_hir_id,
2139 // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
2140 // deref to fn pointers, dyn Fn, impl Fn - #8850
2141 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2142 cx.typeck_results().qpath_res(qpath, *path_hir_id)
2153 /// Returns `Option<String>` where String is a textual representation of the type encapsulated in
2154 /// the slice iff the given expression is a slice of primitives (as defined in the
2155 /// `is_recursively_primitive_type` function) and `None` otherwise.
2156 pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2157 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2158 let expr_kind = expr_type.kind();
2159 let is_primitive = match expr_kind {
2160 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2161 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2162 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2163 is_recursively_primitive_type(*element_type)
2172 // if we have wrappers like Array, Slice or Tuple, print these
2173 // and get the type enclosed in the slice ref
2174 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2175 rustc_ty::Slice(..) => return Some("slice".into()),
2176 rustc_ty::Array(..) => return Some("array".into()),
2177 rustc_ty::Tuple(..) => return Some("tuple".into()),
2179 // is_recursively_primitive_type() should have taken care
2180 // of the rest and we can rely on the type that is found
2181 let refs_peeled = expr_type.peel_refs();
2182 return Some(refs_peeled.walk().last().unwrap().to_string());
2189 /// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
2190 /// `hash` must be comformed with `eq`
2191 pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
2193 Hash: Fn(&T) -> u64,
2194 Eq: Fn(&T, &T) -> bool,
2197 [a, b] if eq(a, b) => return vec![(a, b)],
2198 _ if exprs.len() <= 2 => return vec![],
2202 let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
2204 let mut map: UnhashMap<u64, Vec<&_>> =
2205 UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
2208 match map.entry(hash(expr)) {
2209 Entry::Occupied(mut o) => {
2212 match_expr_list.push((o, expr));
2215 o.get_mut().push(expr);
2217 Entry::Vacant(v) => {
2218 v.insert(vec![expr]);
2226 /// Peels off all references on the pattern. Returns the underlying pattern and the number of
2227 /// references removed.
2228 pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2229 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2230 if let PatKind::Ref(pat, _) = pat.kind {
2231 peel(pat, count + 1)
2239 /// Peels of expressions while the given closure returns `Some`.
2240 pub fn peel_hir_expr_while<'tcx>(
2241 mut expr: &'tcx Expr<'tcx>,
2242 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2243 ) -> &'tcx Expr<'tcx> {
2244 while let Some(e) = f(expr) {
2250 /// Peels off up to the given number of references on the expression. Returns the underlying
2251 /// expression and the number of references removed.
2252 pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2253 let mut remaining = count;
2254 let e = peel_hir_expr_while(expr, |e| match e.kind {
2255 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2261 (e, count - remaining)
2264 /// Peels off all unary operators of an expression. Returns the underlying expression and the number
2265 /// of operators removed.
2266 pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2267 let mut count: usize = 0;
2268 let mut curr_expr = expr;
2269 while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
2270 count = count.wrapping_add(1);
2271 curr_expr = local_expr;
2276 /// Peels off all references on the expression. Returns the underlying expression and the number of
2277 /// references removed.
2278 pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2280 let e = peel_hir_expr_while(expr, |e| match e.kind {
2281 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2290 /// Peels off all references on the type. Returns the underlying type and the number of references
2292 pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2296 TyKind::Ref(_, ref_ty) => {
2300 _ => break (ty, count),
2305 /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
2306 /// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
2307 pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2310 ExprKind::AddrOf(_, _, e) => expr = e,
2311 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2318 pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2319 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2320 if let Res::Def(_, def_id) = path.res {
2321 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2327 static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
2329 fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
2330 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2331 let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
2332 let value = map.entry(module);
2334 Entry::Occupied(entry) => f(entry.get()),
2335 Entry::Vacant(entry) => {
2336 let mut names = Vec::new();
2337 for id in tcx.hir().module_items(module) {
2338 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2339 && let item = tcx.hir().item(id)
2340 && let ItemKind::Const(ty, _body) = item.kind {
2341 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2342 // We could also check for the type name `test::TestDescAndFn`
2343 if let Res::Def(DefKind::Struct, _) = path.res {
2344 let has_test_marker = tcx
2346 .attrs(item.hir_id())
2348 .any(|a| a.has_name(sym::rustc_test_marker));
2349 if has_test_marker {
2350 names.push(item.ident.name);
2356 names.sort_unstable();
2357 f(entry.insert(names))
2362 /// Checks if the function containing the given `HirId` is a `#[test]` function
2364 /// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
2365 pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
2366 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2369 // Since you can nest functions we need to collect all until we leave
2371 .any(|(_id, node)| {
2372 if let Node::Item(item) = node {
2373 if let ItemKind::Fn(_, _, _) = item.kind {
2374 // Note that we have sorted the item names in the visitor,
2375 // so the binary_search gets the same as `contains`, but faster.
2376 return names.binary_search(&item.ident.name).is_ok();
2384 /// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
2386 /// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function
2387 pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
2388 fn is_cfg_test(attr: &Attribute) -> bool {
2389 if attr.has_name(sym::cfg)
2390 && let Some(items) = attr.meta_item_list()
2391 && let [item] = &*items
2392 && item.has_name(sym::test)
2401 .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id))
2405 /// Checks whether item either has `test` attribute applied, or
2406 /// is a module with `test` in its name.
2408 /// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
2409 pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
2410 is_in_test_function(tcx, item.hir_id())
2411 || matches!(item.kind, ItemKind::Mod(..))
2412 && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests")
2415 /// Walks the HIR tree from the given expression, up to the node where the value produced by the
2416 /// expression is consumed. Calls the function for every node encountered this way until it returns
2419 /// This allows walking through `if`, `match`, `break`, block expressions to find where the value
2420 /// produced by the expression is consumed.
2421 pub fn walk_to_expr_usage<'tcx, T>(
2422 cx: &LateContext<'tcx>,
2424 mut f: impl FnMut(Node<'tcx>, HirId) -> Option<T>,
2426 let map = cx.tcx.hir();
2427 let mut iter = map.parent_iter(e.hir_id);
2428 let mut child_id = e.hir_id;
2430 while let Some((parent_id, parent)) = iter.next() {
2431 if let Some(x) = f(parent, child_id) {
2434 let parent = match parent {
2436 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2437 child_id = parent_id;
2440 Node::Arm(a) if a.body.hir_id == child_id => {
2441 child_id = parent_id;
2447 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2448 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2450 iter = map.parent_iter(id);
2452 ExprKind::Block(..) => child_id = parent_id,
2459 /// Checks whether a given span has any comment token
2460 /// This checks for all types of comment: line "//", block "/**", doc "///" "//!"
2461 pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2462 let Ok(snippet) = sm.span_to_snippet(span) else { return false };
2463 return tokenize(&snippet).any(|token| {
2466 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2471 /// Return all the comments a given span contains
2472 /// Comments are returned wrapped with their relevant delimiters
2473 pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2474 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2475 let mut comments_buf: Vec<String> = Vec::new();
2476 let mut index: usize = 0;
2478 for token in tokenize(&snippet) {
2479 let token_range = index..(index + token.len as usize);
2480 index += token.len as usize;
2482 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => {
2483 if let Some(comment) = snippet.get(token_range) {
2484 comments_buf.push(comment.to_string());
2491 comments_buf.join("\n")
2494 pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
2495 sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
2498 macro_rules! op_utils {
2499 ($($name:ident $assign:ident)*) => {
2500 /// Binary operation traits like `LangItem::Add`
2501 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2503 /// Operator-Assign traits like `LangItem::AddAssign`
2504 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2506 /// Converts `BinOpKind::Add` to `(LangItem::Add, LangItem::AddAssign)`, for example
2507 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2509 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*