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::SimplifiedTypeGen::{
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_semver::RustcVersion;
109 use rustc_session::Session;
110 use rustc_span::hygiene::{ExpnKind, MacroKind};
111 use rustc_span::source_map::SourceMap;
113 use rustc_span::symbol::{kw, Ident, Symbol};
114 use rustc_span::Span;
115 use rustc_target::abi::Integer;
117 use crate::consts::{constant, Constant};
118 use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
119 use crate::visitors::for_each_expr;
121 pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
122 if let Ok(version) = RustcVersion::parse(msrv) {
123 return Some(version);
124 } else if let Some(sess) = sess {
125 if let Some(span) = span {
126 sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
132 pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
133 msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
137 macro_rules! extract_msrv_attr {
138 ($context:ident) => {
139 fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
140 let sess = rustc_lint::LintContext::sess(cx);
141 match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
143 if let Some(msrv) = msrv_attr.value_str() {
144 self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
146 sess.span_err(msrv_attr.span, "bad clippy attribute");
155 /// If the given expression is a local binding, find the initializer expression.
156 /// If that initializer expression is another local binding, find its initializer again.
157 /// This process repeats as long as possible (but usually no more than once). Initializer
158 /// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
171 /// let def = abc + 2;
172 /// // ^^^^^^^ output
176 pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
177 while let Some(init) = path_to_local(expr)
178 .and_then(|id| find_binding_init(cx, id))
179 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
186 /// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
187 /// By only considering immutable bindings, we guarantee that the returned expression represents the
188 /// value of the binding wherever it is referenced.
190 /// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
191 /// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
192 /// canonical binding `HirId`.
193 pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
194 let hir = cx.tcx.hir();
196 if let Some(Node::Pat(pat)) = hir.find(hir_id);
197 if matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..));
198 let parent = hir.get_parent_node(hir_id);
199 if let Some(Node::Local(local)) = hir.find(parent);
207 /// Returns `true` if the given `NodeId` is inside a constant context
212 /// if in_constant(cx, expr.hir_id) {
216 pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
217 let parent_id = cx.tcx.hir().get_parent_item(id).def_id;
218 match cx.tcx.hir().get_by_def_id(parent_id) {
220 kind: ItemKind::Const(..) | ItemKind::Static(..),
223 | Node::TraitItem(&TraitItem {
224 kind: TraitItemKind::Const(..),
227 | Node::ImplItem(&ImplItem {
228 kind: ImplItemKind::Const(..),
231 | Node::AnonConst(_) => true,
233 kind: ItemKind::Fn(ref sig, ..),
236 | Node::ImplItem(&ImplItem {
237 kind: ImplItemKind::Fn(ref sig, _),
239 }) => sig.header.constness == Constness::Const,
244 /// Checks if a `Res` refers to a constructor of a `LangItem`
245 /// For example, use this to check whether a function call or a pattern is `Some(..)`.
246 pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
247 if let Res::Def(DefKind::Ctor(..), id) = res
248 && let Some(lang_id) = cx.tcx.lang_items().get(lang_item)
249 && let Some(id) = cx.tcx.opt_parent(id)
257 pub fn is_res_diagnostic_ctor(cx: &LateContext<'_>, res: Res, diag_item: Symbol) -> bool {
258 if let Res::Def(DefKind::Ctor(..), id) = res
259 && let Some(id) = cx.tcx.opt_parent(id)
261 cx.tcx.is_diagnostic_item(diag_item, id)
267 /// Checks if a `QPath` resolves to a constructor of a diagnostic item.
268 pub fn is_diagnostic_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, diagnostic_item: Symbol) -> bool {
269 if let QPath::Resolved(_, path) = qpath {
270 if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
271 return cx.tcx.is_diagnostic_item(diagnostic_item, cx.tcx.parent(ctor_id));
277 /// Checks if the `DefId` matches the given diagnostic item or it's constructor.
278 pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
279 let did = match cx.tcx.def_kind(did) {
280 DefKind::Ctor(..) => cx.tcx.parent(did),
281 // Constructors for types in external crates seem to have `DefKind::Variant`
282 DefKind::Variant => match cx.tcx.opt_parent(did) {
283 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
289 cx.tcx.is_diagnostic_item(item, did)
292 /// Checks if the `DefId` matches the given `LangItem` or it's constructor.
293 pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
294 let did = match cx.tcx.def_kind(did) {
295 DefKind::Ctor(..) => cx.tcx.parent(did),
296 // Constructors for types in external crates seem to have `DefKind::Variant`
297 DefKind::Variant => match cx.tcx.opt_parent(did) {
298 Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
304 cx.tcx.lang_items().get(item) == Some(did)
307 pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
317 ) | ExprKind::Tup([])
321 /// Checks if given pattern is a wildcard (`_`)
322 pub fn is_wild(pat: &Pat<'_>) -> bool {
323 matches!(pat.kind, PatKind::Wild)
326 /// Checks if the method call given in `expr` belongs to the given trait.
327 /// This is a deprecated function, consider using [`is_trait_method`].
328 pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
329 let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
330 let trt_id = cx.tcx.trait_of_item(def_id);
331 trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
334 /// Checks if a method is defined in an impl of a diagnostic item
335 pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
336 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
337 if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
338 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
344 /// Checks if a method is in a diagnostic item trait
345 pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
346 if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
347 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
352 /// Checks if the method call given in `expr` belongs to the given trait.
353 pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
355 .type_dependent_def_id(expr.hir_id)
356 .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
359 /// Checks if the given expression is a path referring an item on the trait
360 /// that is marked with the given diagnostic item.
362 /// For checking method call expressions instead of path expressions, use
363 /// [`is_trait_method`].
365 /// For example, this can be used to find if an expression like `u64::default`
366 /// refers to an item of the trait `Default`, which is associated with the
367 /// `diag_item` of `sym::Default`.
368 pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
369 if let hir::ExprKind::Path(ref qpath) = expr.kind {
370 cx.qpath_res(qpath, expr.hir_id)
372 .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
378 pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
380 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
381 QPath::TypeRelative(_, seg) => seg,
382 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
386 pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
387 last_path_segment(qpath)
389 .map_or(&[][..], |a| a.args)
391 .filter_map(|a| match a {
392 hir::GenericArg::Type(ty) => Some(*ty),
397 /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
398 /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
399 /// `QPath::Resolved.1.res.opt_def_id()`.
401 /// Matches a `QPath` against a slice of segment string literals.
403 /// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
404 /// `rustc_hir::QPath`.
408 /// match_qpath(path, &["std", "rt", "begin_unwind"])
410 pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
412 QPath::Resolved(_, path) => match_path(path, segments),
413 QPath::TypeRelative(ty, segment) => match ty.kind {
414 TyKind::Path(ref inner_path) => {
415 if let [prefix @ .., end] = segments {
416 if match_qpath(inner_path, prefix) {
417 return segment.ident.name.as_str() == *end;
424 QPath::LangItem(..) => false,
428 /// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
430 /// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
431 pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
432 path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
435 /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
436 /// it matches the given lang item.
437 pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
438 path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id))
441 /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
442 /// it matches the given diagnostic item.
443 pub fn is_path_diagnostic_item<'tcx>(
444 cx: &LateContext<'_>,
445 maybe_path: &impl MaybePath<'tcx>,
448 path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
451 /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
452 /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
453 /// `QPath::Resolved.1.res.opt_def_id()`.
455 /// Matches a `Path` against a slice of segment string literals.
457 /// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
458 /// `rustc_hir::Path`.
463 /// if match_path(&trait_ref.path, &paths::HASH) {
464 /// // This is the `std::hash::Hash` trait.
467 /// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
468 /// // This is a `rustc_middle::lint::Lint`.
471 pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
475 .zip(segments.iter().rev())
476 .all(|(a, b)| a.ident.name.as_str() == *b)
479 /// If the expression is a path to a local, returns the canonical `HirId` of the local.
480 pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
481 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
482 if let Res::Local(id) = path.res {
489 /// Returns true if the expression is a path to a local with the specified `HirId`.
490 /// Use this function to see if an expression matches a function argument or a match binding.
491 pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
492 path_to_local(expr) == Some(id)
495 pub trait MaybePath<'hir> {
496 fn hir_id(&self) -> HirId;
497 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
500 macro_rules! maybe_path {
501 ($ty:ident, $kind:ident) => {
502 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
503 fn hir_id(&self) -> HirId {
506 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
508 hir::$kind::Path(qpath) => Some(qpath),
515 maybe_path!(Expr, ExprKind);
516 maybe_path!(Pat, PatKind);
517 maybe_path!(Ty, TyKind);
519 /// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
520 pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
521 match maybe_path.qpath_opt() {
523 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
527 /// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
528 pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
529 path_res(cx, maybe_path).opt_def_id()
532 fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
533 let ty = match name {
534 "bool" => BoolSimplifiedType,
535 "char" => CharSimplifiedType,
536 "str" => StrSimplifiedType,
537 "array" => ArraySimplifiedType,
538 "slice" => SliceSimplifiedType,
539 // FIXME: rustdoc documents these two using just `pointer`.
541 // Maybe this is something we should do here too.
542 "const_ptr" => PtrSimplifiedType(Mutability::Not),
543 "mut_ptr" => PtrSimplifiedType(Mutability::Mut),
544 "isize" => IntSimplifiedType(IntTy::Isize),
545 "i8" => IntSimplifiedType(IntTy::I8),
546 "i16" => IntSimplifiedType(IntTy::I16),
547 "i32" => IntSimplifiedType(IntTy::I32),
548 "i64" => IntSimplifiedType(IntTy::I64),
549 "i128" => IntSimplifiedType(IntTy::I128),
550 "usize" => UintSimplifiedType(UintTy::Usize),
551 "u8" => UintSimplifiedType(UintTy::U8),
552 "u16" => UintSimplifiedType(UintTy::U16),
553 "u32" => UintSimplifiedType(UintTy::U32),
554 "u64" => UintSimplifiedType(UintTy::U64),
555 "u128" => UintSimplifiedType(UintTy::U128),
556 "f32" => FloatSimplifiedType(FloatTy::F32),
557 "f64" => FloatSimplifiedType(FloatTy::F64),
558 _ => return [].iter().copied(),
561 tcx.incoherent_impls(ty).iter().copied()
564 fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
565 match tcx.def_kind(def_id) {
566 DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
567 .module_children(def_id)
569 .filter(|item| item.ident.name == name)
570 .map(|child| child.res.expect_non_local())
573 .associated_item_def_ids(def_id)
576 .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
577 .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
583 fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
587 let item_kind = match hir.find_by_def_id(local_id) {
588 Some(Node::Crate(r#mod)) => {
589 root_mod = ItemKind::Mod(r#mod);
592 Some(Node::Item(item)) => &item.kind,
593 _ => return Vec::new(),
596 let res = |ident: Ident, owner_id: OwnerId| {
597 if ident.name == name {
598 let def_id = owner_id.to_def_id();
599 Some(Res::Def(tcx.def_kind(def_id), def_id))
606 ItemKind::Mod(r#mod) => r#mod
609 .filter_map(|&item_id| res(hir.item(item_id).ident, item_id.owner_id))
611 ItemKind::Impl(r#impl) => r#impl
614 .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
616 ItemKind::Trait(.., trait_item_refs) => trait_item_refs
618 .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
624 fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
625 if let Some(local_id) = def_id.as_local() {
626 local_item_children_by_name(tcx, local_id, name)
628 non_local_item_children_by_name(tcx, def_id, name)
632 /// Resolves a def path like `std::vec::Vec`.
634 /// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
635 /// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
637 /// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec`
638 /// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
640 /// This function is expensive and should be used sparingly.
641 pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> {
642 fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator<Item = DefId> + '_ {
646 .filter(move |&num| tcx.crate_name(num) == name)
647 .map(CrateNum::as_def_id)
652 let (base, mut path) = match *path {
654 return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
656 [base, ref path @ ..] => (base, path),
657 _ => return Vec::new(),
660 let base_sym = Symbol::intern(base);
662 let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
663 Some(LOCAL_CRATE.as_def_id())
668 let starts = find_primitive_impls(tcx, base)
669 .chain(find_crates(tcx, base_sym))
671 .map(|id| Res::Def(tcx.def_kind(id), id));
673 let mut resolutions: Vec<Res> = starts.collect();
675 while let [segment, rest @ ..] = path {
677 let segment = Symbol::intern(segment);
679 resolutions = resolutions
681 .filter_map(|res| res.opt_def_id())
683 // When the current def_id is e.g. `struct S`, check the impl items in
685 let inherent_impl_children = tcx
686 .inherent_impls(def_id)
688 .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
690 let direct_children = item_children_by_name(tcx, def_id, segment);
692 inherent_impl_children.chain(direct_children)
700 /// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`].
701 pub fn def_path_def_ids(cx: &LateContext<'_>, path: &[&str]) -> impl Iterator<Item = DefId> {
702 def_path_res(cx, path).into_iter().filter_map(|res| res.opt_def_id())
705 /// Convenience function to get the `DefId` of a trait by path.
706 /// It could be a trait or trait alias.
708 /// This function is expensive and should be used sparingly.
709 pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
710 def_path_res(cx, path).into_iter().find_map(|res| match res {
711 Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
716 /// Gets the `hir::TraitRef` of the trait the given method is implemented for.
718 /// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
721 /// struct Point(isize, isize);
723 /// impl std::ops::Add for Point {
724 /// type Output = Self;
726 /// fn add(self, other: Self) -> Self {
731 pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
732 // Get the implemented trait for the current function
733 let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
734 let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
736 if parent_impl != hir::CRATE_OWNER_ID;
737 if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl.def_id);
738 if let hir::ItemKind::Impl(impl_) = &item.kind;
740 return impl_.of_trait.as_ref();
746 /// This method will return tuple of projection stack and root of the expression,
747 /// used in `can_mut_borrow_both`.
749 /// For example, if `e` represents the `v[0].a.b[x]`
750 /// this method will return a tuple, composed of a `Vec`
751 /// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
752 /// and an `Expr` for root of them, `v`
753 fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
754 let mut result = vec![];
757 ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
768 /// Gets the mutability of the custom deref adjustment, if any.
769 pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
773 .find_map(|a| match a.kind {
774 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
775 Adjust::Deref(None) => None,
781 /// Checks if two expressions can be mutably borrowed simultaneously
782 /// and they aren't dependent on borrowing same thing twice
783 pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
784 let (s1, r1) = projection_stack(e1);
785 let (s2, r2) = projection_stack(e2);
786 if !eq_expr_value(cx, r1, r2) {
789 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
793 for (x1, x2) in s1.iter().zip(s2.iter()) {
794 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
798 match (&x1.kind, &x2.kind) {
799 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
804 (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
805 if !eq_expr_value(cx, i1, i2) {
815 /// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
816 /// constructor from the std library
817 fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
818 let std_types_symbols = &[
829 if let QPath::TypeRelative(_, method) = path {
830 if method.ident.name == sym::new {
831 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
832 if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
833 return std_types_symbols.iter().any(|&symbol| {
834 cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
843 /// Return true if the expr is equal to `Default::default` when evaluated.
844 pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
846 if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
847 if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
848 if is_diag_trait_item(cx, repl_def_id, sym::Default)
849 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
850 then { true } else { false }
854 /// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
855 /// It doesn't cover all cases, for example indirect function calls (some of std
856 /// functions are supported) but it is the best we have.
857 pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
859 ExprKind::Lit(lit) => match lit.node {
860 LitKind::Bool(false) | LitKind::Int(0, _) => true,
861 LitKind::Str(s, _) => s.is_empty(),
864 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
865 ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
866 if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
867 if let LitKind::Int(v, _) = const_lit.node;
868 if v <= 32 && is_default_equivalent(cx, x);
876 ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func),
877 ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg),
878 ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
879 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
884 fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
885 if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind &&
886 seg.ident.name == sym::from
889 ExprKind::Lit(hir::Lit {
890 node: LitKind::Str(ref sym, _),
892 }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
893 ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
894 ExprKind::Repeat(_, ArrayLen::Body(len)) => {
895 if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind &&
896 let LitKind::Int(v, _) = const_lit.node
898 return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
907 /// Checks if the top level expression can be moved into a closure as is.
908 /// Currently checks for:
909 /// * Break/Continue outside the given loop HIR ids.
910 /// * Yield/Return statements.
911 /// * Inline assembly.
912 /// * Usages of a field of a local where the type of the local can be partially moved.
914 /// For example, given the following function:
917 /// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
918 /// for item in iter {
929 /// When called on the expression `item.0` this will return false unless the local `item` is in the
930 /// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
931 /// isn't always safe to move into a closure when only a single field is needed.
933 /// When called on the `continue` expression this will return false unless the outer loop expression
934 /// is in the `loop_ids` set.
936 /// Note that this check is not recursive, so passing the `if` expression will always return true
937 /// even though sub-expressions might return false.
938 pub fn can_move_expr_to_closure_no_visit<'tcx>(
939 cx: &LateContext<'tcx>,
940 expr: &'tcx Expr<'_>,
942 ignore_locals: &HirIdSet,
945 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
946 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
947 if loop_ids.contains(&id) =>
952 | ExprKind::Continue(_)
954 | ExprKind::Yield(..)
955 | ExprKind::InlineAsm(_) => false,
956 // Accessing a field of a local value can only be done if the type isn't
962 ExprKind::Path(QPath::Resolved(
965 res: Res::Local(local_id),
972 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
973 // TODO: check if the local has been partially moved. Assume it has for now.
980 /// How a local is captured by a closure
981 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
982 pub enum CaptureKind {
987 pub fn is_imm_ref(self) -> bool {
988 self == Self::Ref(Mutability::Not)
991 impl std::ops::BitOr for CaptureKind {
993 fn bitor(self, rhs: Self) -> Self::Output {
995 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
996 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
997 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
998 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
1002 impl std::ops::BitOrAssign for CaptureKind {
1003 fn bitor_assign(&mut self, rhs: Self) {
1004 *self = *self | rhs;
1008 /// Given an expression referencing a local, determines how it would be captured in a closure.
1009 /// Note as this will walk up to parent expressions until the capture can be determined it should
1010 /// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
1011 /// function argument (other than a receiver).
1012 pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
1013 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
1014 let mut capture = CaptureKind::Ref(Mutability::Not);
1015 pat.each_binding_or_first(&mut |_, id, span, _| match cx
1017 .extract_binding_mode(cx.sess(), id, span)
1020 BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
1021 capture = CaptureKind::Value;
1023 BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
1024 capture = CaptureKind::Ref(Mutability::Mut);
1031 debug_assert!(matches!(
1033 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
1036 let mut child_id = e.hir_id;
1037 let mut capture = CaptureKind::Value;
1038 let mut capture_expr_ty = e;
1040 for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
1043 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
1051 .map_or(&[][..], |x| &**x)
1053 if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
1054 *adjust.last().map_or(target, |a| a.target).kind()
1056 return CaptureKind::Ref(mutability);
1061 Node::Expr(e) => match e.kind {
1062 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
1063 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
1064 ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
1065 return CaptureKind::Ref(Mutability::Mut);
1067 ExprKind::Field(..) => {
1068 if capture == CaptureKind::Value {
1069 capture_expr_ty = e;
1072 ExprKind::Let(let_expr) => {
1073 let mutability = match pat_capture_kind(cx, let_expr.pat) {
1074 CaptureKind::Value => Mutability::Not,
1075 CaptureKind::Ref(m) => m,
1077 return CaptureKind::Ref(mutability);
1079 ExprKind::Match(_, arms, _) => {
1080 let mut mutability = Mutability::Not;
1081 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
1083 CaptureKind::Value => break,
1084 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
1085 CaptureKind::Ref(Mutability::Not) => (),
1088 return CaptureKind::Ref(mutability);
1092 Node::Local(l) => match pat_capture_kind(cx, l.pat) {
1093 CaptureKind::Value => break,
1094 capture @ CaptureKind::Ref(_) => return capture,
1099 child_id = parent_id;
1102 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
1103 // Copy types are never automatically captured by value.
1104 CaptureKind::Ref(Mutability::Not)
1110 /// Checks if the expression can be moved into a closure as is. This will return a list of captures
1111 /// if so, otherwise, `None`.
1112 pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
1113 struct V<'cx, 'tcx> {
1114 cx: &'cx LateContext<'tcx>,
1115 // Stack of potential break targets contained in the expression.
1117 /// Local variables created in the expression. These don't need to be captured.
1119 /// Whether this expression can be turned into a closure.
1120 allow_closure: bool,
1121 /// Locals which need to be captured, and whether they need to be by value, reference, or
1122 /// mutable reference.
1123 captures: HirIdMap<CaptureKind>,
1125 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
1126 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
1127 if !self.allow_closure {
1132 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
1133 if !self.locals.contains(&l) {
1134 let cap = capture_local_usage(self.cx, e);
1135 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
1138 ExprKind::Closure { .. } => {
1139 let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id);
1140 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
1141 let local_id = match capture.place.base {
1142 PlaceBase::Local(id) => id,
1143 PlaceBase::Upvar(var) => var.var_path.hir_id,
1146 if !self.locals.contains(&local_id) {
1147 let capture = match capture.info.capture_kind {
1148 UpvarCapture::ByValue => CaptureKind::Value,
1149 UpvarCapture::ByRef(kind) => match kind {
1150 BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
1151 BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
1152 CaptureKind::Ref(Mutability::Mut)
1158 .and_modify(|e| *e |= capture)
1159 .or_insert(capture);
1163 ExprKind::Loop(b, ..) => {
1164 self.loops.push(e.hir_id);
1165 self.visit_block(b);
1169 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
1175 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1176 p.each_binding_or_first(&mut |_, id, _, _| {
1177 self.locals.insert(id);
1184 allow_closure: true,
1186 locals: HirIdSet::default(),
1187 captures: HirIdMap::default(),
1190 v.allow_closure.then_some(v.captures)
1193 /// Arguments of a method: the receiver and all the additional arguments.
1194 pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
1196 /// Returns the method names and argument list of nested method call expressions that make up
1197 /// `expr`. method/span lists are sorted with the most recent call first.
1198 pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
1199 let mut method_names = Vec::with_capacity(max_depth);
1200 let mut arg_lists = Vec::with_capacity(max_depth);
1201 let mut spans = Vec::with_capacity(max_depth);
1203 let mut current = expr;
1204 for _ in 0..max_depth {
1205 if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
1206 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1209 method_names.push(path.ident.name);
1210 arg_lists.push((*receiver, &**args));
1211 spans.push(path.ident.span);
1218 (method_names, arg_lists, spans)
1221 /// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
1223 /// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
1224 /// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
1225 /// containing the `Expr`s for
1226 /// `.bar()` and `.baz()`
1227 pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
1228 let mut current = expr;
1229 let mut matched = Vec::with_capacity(methods.len());
1230 for method_name in methods.iter().rev() {
1231 // method chains are stored last -> first
1232 if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
1233 if path.ident.name.as_str() == *method_name {
1234 if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
1237 matched.push((receiver, args)); // build up `matched` backwards
1238 current = receiver; // go to parent expression
1246 // Reverse `matched` so that it is in the same order as `methods`.
1251 /// Returns `true` if the provided `def_id` is an entrypoint to a program.
1252 pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1255 .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
1258 /// Returns `true` if the expression is in the program's `#[panic_handler]`.
1259 pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1260 let parent = cx.tcx.hir().get_parent_item(e.hir_id);
1261 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
1264 /// Gets the name of the item the expression is in, if available.
1265 pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1266 let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id).def_id;
1267 match cx.tcx.hir().find_by_def_id(parent_id) {
1269 Node::Item(Item { ident, .. })
1270 | Node::TraitItem(TraitItem { ident, .. })
1271 | Node::ImplItem(ImplItem { ident, .. }),
1272 ) => Some(ident.name),
1277 pub struct ContainsName {
1282 impl<'tcx> Visitor<'tcx> for ContainsName {
1283 fn visit_name(&mut self, name: Symbol) {
1284 if self.name == name {
1290 /// Checks if an `Expr` contains a certain name.
1291 pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
1292 let mut cn = ContainsName { name, result: false };
1293 cn.visit_expr(expr);
1297 /// Returns `true` if `expr` contains a return expression
1298 pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
1299 for_each_expr(expr, |e| {
1300 if matches!(e.kind, hir::ExprKind::Ret(..)) {
1301 ControlFlow::Break(())
1303 ControlFlow::Continue(())
1309 /// Gets the parent node, if any.
1310 pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
1311 tcx.hir().parent_iter(id).next().map(|(_, node)| node)
1314 /// Gets the parent expression, if any –- this is useful to constrain a lint.
1315 pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
1316 get_parent_expr_for_hir(cx, e.hir_id)
1319 /// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
1320 /// constraint lints
1321 pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
1322 match get_parent_node(cx.tcx, hir_id) {
1323 Some(Node::Expr(parent)) => Some(parent),
1328 pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1329 let map = &cx.tcx.hir();
1330 let enclosing_node = map
1331 .get_enclosing_scope(hir_id)
1332 .and_then(|enclosing_id| map.find(enclosing_id));
1333 enclosing_node.and_then(|node| match node {
1334 Node::Block(block) => Some(block),
1336 kind: ItemKind::Fn(_, _, eid),
1339 | Node::ImplItem(&ImplItem {
1340 kind: ImplItemKind::Fn(_, eid),
1342 }) => match cx.tcx.hir().body(eid).value.kind {
1343 ExprKind::Block(block, _) => Some(block),
1350 /// Gets the loop or closure enclosing the given expression, if any.
1351 pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1352 cx: &LateContext<'tcx>,
1354 ) -> Option<&'tcx Expr<'tcx>> {
1355 for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
1357 Node::Expr(e) => match e.kind {
1358 ExprKind::Closure { .. } => {
1359 if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
1360 && subs.as_closure().kind() == ClosureKind::FnOnce
1364 let is_once = walk_to_expr_usage(cx, e, |node, id| {
1365 let Node::Expr(e) = node else {
1369 ExprKind::Call(f, _) if f.hir_id == id => Some(()),
1370 ExprKind::Call(f, args) => {
1371 let i = args.iter().position(|arg| arg.hir_id == id)?;
1372 let sig = expr_sig(cx, f)?;
1373 let predicates = sig
1375 .map_or(cx.param_env, |id| cx.tcx.param_env(id))
1377 sig.input(i).and_then(|ty| {
1378 ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
1381 ExprKind::MethodCall(_, receiver, args, _) => {
1382 let i = std::iter::once(receiver)
1384 .position(|arg| arg.hir_id == id)?;
1385 let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
1386 let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
1387 ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
1397 ExprKind::Loop(..) => return Some(e),
1400 Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
1407 /// Gets the parent node if it's an impl block.
1408 pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
1409 match tcx.hir().parent_iter(id).next() {
1413 kind: ItemKind::Impl(imp),
1421 /// Removes blocks around an expression, only if the block contains just one expression
1422 /// and no statements. Unsafe blocks are not removed.
1426 /// * `{ x }` -> `x`
1427 /// * `{{ x }}` -> `x`
1428 /// * `{ x; }` -> `{ x; }`
1429 /// * `{ x; y }` -> `{ x; y }`
1430 /// * `{ unsafe { x } }` -> `unsafe { x }`
1431 pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1432 while let ExprKind::Block(
1436 rules: BlockCheckMode::DefaultBlock,
1447 /// Removes blocks around an expression, only if the block contains just one expression
1448 /// or just one expression statement with a semicolon. Unsafe blocks are not removed.
1452 /// * `{ x }` -> `x`
1453 /// * `{ x; }` -> `x`
1454 /// * `{{ x; }}` -> `x`
1455 /// * `{ x; y }` -> `{ x; y }`
1456 /// * `{ unsafe { x } }` -> `unsafe { x }`
1457 pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1458 while let ExprKind::Block(
1462 rules: BlockCheckMode::DefaultBlock,
1469 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1474 rules: BlockCheckMode::DefaultBlock,
1485 /// Checks if the given expression is the else clause of either an `if` or `if let` expression.
1486 pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1487 let mut iter = tcx.hir().parent_iter(expr.hir_id);
1492 kind: ExprKind::If(_, _, Some(else_expr)),
1495 )) => else_expr.hir_id == expr.hir_id,
1500 /// Checks whether the given expression is a constant integer of the given value.
1501 /// unlike `is_integer_literal`, this version does const folding
1502 pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1503 if is_integer_literal(e, value) {
1506 let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
1507 if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
1513 /// Checks whether the given expression is a constant literal of the given value.
1514 pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1515 // FIXME: use constant folding
1516 if let ExprKind::Lit(ref spanned) = expr.kind {
1517 if let LitKind::Int(v, _) = spanned.node {
1524 /// Returns `true` if the given `Expr` has been coerced before.
1526 /// Examples of coercions can be found in the Nomicon at
1527 /// <https://doc.rust-lang.org/nomicon/coercions.html>.
1529 /// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for
1530 /// more information on adjustments and coercions.
1531 pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1532 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1535 /// Returns the pre-expansion span if this comes from an expansion of the
1537 /// See also [`is_direct_expn_of`].
1539 pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
1541 if span.from_expansion() {
1542 let data = span.ctxt().outer_expn_data();
1543 let new_span = data.call_site;
1545 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
1546 if mac_name.as_str() == name {
1547 return Some(new_span);
1558 /// Returns the pre-expansion span if the span directly comes from an expansion
1559 /// of the macro `name`.
1560 /// The difference with [`is_expn_of`] is that in
1562 /// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
1563 /// # macro_rules! bar { ($e:expr) => { $e } }
1566 /// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
1567 /// from `bar!` by `is_direct_expn_of`.
1569 pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
1570 if span.from_expansion() {
1571 let data = span.ctxt().outer_expn_data();
1572 let new_span = data.call_site;
1574 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
1575 if mac_name.as_str() == name {
1576 return Some(new_span);
1584 /// Convenience function to get the return type of a function.
1585 pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
1586 let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
1587 let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
1588 cx.tcx.erase_late_bound_regions(ret_ty)
1591 /// Convenience function to get the nth argument type of a function.
1592 pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId, nth: usize) -> Ty<'tcx> {
1593 let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
1594 let arg = cx.tcx.fn_sig(fn_def_id).input(nth);
1595 cx.tcx.erase_late_bound_regions(arg)
1598 /// Checks if an expression is constructing a tuple-like enum variant or struct
1599 pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1600 if let ExprKind::Call(fun, _) = expr.kind {
1601 if let ExprKind::Path(ref qp) = fun.kind {
1602 let res = cx.qpath_res(qp, fun.hir_id);
1604 def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1605 def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1613 /// Returns `true` if a pattern is refutable.
1614 // TODO: should be implemented using rustc/mir_build/thir machinery
1615 pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1616 fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1618 cx.qpath_res(qpath, id),
1619 def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
1623 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1624 i.into_iter().any(|pat| is_refutable(cx, pat))
1628 PatKind::Wild => false,
1629 PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
1630 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
1631 PatKind::Lit(..) | PatKind::Range(..) => true,
1632 PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
1633 PatKind::Or(pats) => {
1634 // TODO: should be the honest check, that pats is exhaustive set
1635 are_refutable(cx, pats)
1637 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
1638 PatKind::Struct(ref qpath, fields, _) => {
1639 is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
1641 PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
1642 PatKind::Slice(head, middle, tail) => {
1643 match &cx.typeck_results().node_type(pat.hir_id).kind() {
1644 rustc_ty::Slice(..) => {
1645 // [..] is the only irrefutable slice pattern.
1646 !head.is_empty() || middle.is_none() || !tail.is_empty()
1648 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
1658 /// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
1659 /// the function once on the given pattern.
1660 pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1661 if let PatKind::Or(pats) = pat.kind {
1662 pats.iter().for_each(f);
1668 pub fn is_self(slf: &Param<'_>) -> bool {
1669 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1670 name.name == kw::SelfLower
1676 pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
1677 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
1678 if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res {
1685 pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1686 (0..decl.inputs.len()).map(move |i| &body.params[i])
1689 /// Checks if a given expression is a match expression expanded from the `?`
1690 /// operator or the `try` macro.
1691 pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1692 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1694 if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind;
1695 if ddpos.as_opt_usize().is_none();
1696 if is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk);
1697 if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
1698 if path_to_local_id(arm.body, hir_id);
1706 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1707 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
1708 is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
1714 if let ExprKind::Match(_, arms, ref source) = expr.kind {
1715 // desugared from a `?` operator
1716 if *source == MatchSource::TryDesugar {
1722 if arms[0].guard.is_none();
1723 if arms[1].guard.is_none();
1724 if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
1734 /// Returns `true` if the lint is allowed in the current context. This is useful for
1735 /// skipping long running code when it's unnecessary
1737 /// This function should check the lint level for the same node, that the lint will
1738 /// be emitted at. If the information is buffered to be emitted at a later point, please
1739 /// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
1740 /// expectations at the checked nodes will be fulfilled.
1741 pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
1742 cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
1745 pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1746 while let PatKind::Ref(subpat, _) = pat.kind {
1752 pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
1753 Integer::from_int_ty(&tcx, ity).size().bits()
1756 #[expect(clippy::cast_possible_wrap)]
1757 /// Turn a constant int byte representation into an i128
1758 pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
1759 let amt = 128 - int_bits(tcx, ity);
1760 ((u as i128) << amt) >> amt
1763 #[expect(clippy::cast_sign_loss)]
1764 /// clip unused bytes
1765 pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
1766 let amt = 128 - int_bits(tcx, ity);
1767 ((u as u128) << amt) >> amt
1770 /// clip unused bytes
1771 pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
1772 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1773 let amt = 128 - bits;
1777 pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool {
1778 attrs.iter().any(|attr| attr.has_name(symbol))
1781 pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1782 has_attr(cx.tcx.hir().attrs(hir_id), sym::repr)
1785 pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
1786 let map = &tcx.hir();
1787 let mut prev_enclosing_node = None;
1788 let mut enclosing_node = node;
1789 while Some(enclosing_node) != prev_enclosing_node {
1790 if has_attr(map.attrs(enclosing_node), symbol) {
1793 prev_enclosing_node = Some(enclosing_node);
1794 enclosing_node = map.get_parent_item(enclosing_node).into();
1800 pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
1801 any_parent_has_attr(tcx, node, sym::automatically_derived)
1804 /// Matches a function call with the given path and returns the arguments.
1809 /// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
1811 /// This function is deprecated. Use [`match_function_call_with_def_id`].
1812 pub fn match_function_call<'tcx>(
1813 cx: &LateContext<'tcx>,
1814 expr: &'tcx Expr<'_>,
1816 ) -> Option<&'tcx [Expr<'tcx>]> {
1818 if let ExprKind::Call(fun, args) = expr.kind;
1819 if let ExprKind::Path(ref qpath) = fun.kind;
1820 if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
1821 if match_def_path(cx, fun_def_id, path);
1829 pub fn match_function_call_with_def_id<'tcx>(
1830 cx: &LateContext<'tcx>,
1831 expr: &'tcx Expr<'_>,
1833 ) -> Option<&'tcx [Expr<'tcx>]> {
1835 if let ExprKind::Call(fun, args) = expr.kind;
1836 if let ExprKind::Path(ref qpath) = fun.kind;
1837 if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id);
1845 /// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
1848 /// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
1849 pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
1850 let search_path = cx.get_def_path(did);
1853 .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
1856 /// Checks if the given `DefId` matches the path.
1857 pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool {
1858 // We should probably move to Symbols in Clippy as well rather than interning every time.
1859 let path = cx.get_def_path(did);
1860 syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
1863 /// Checks if the given `DefId` matches the `libc` item.
1864 pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
1865 let path = cx.get_def_path(did);
1866 // libc is meant to be used as a flat list of names, but they're all actually defined in different
1867 // modules based on the target platform. Ignore everything but crate name and the item name.
1868 path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
1871 /// Returns the list of condition expressions and the list of blocks in a
1872 /// sequence of `if/else`.
1873 /// E.g., this returns `([a, b], [c, d, e])` for the expression
1874 /// `if a { c } else if b { d } else { e }`.
1875 pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1876 let mut conds = Vec::new();
1877 let mut blocks: Vec<&Block<'_>> = Vec::new();
1879 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
1881 if let ExprKind::Block(block, _) = then.kind {
1884 panic!("ExprKind::If node is not an ExprKind::Block");
1887 if let Some(else_expr) = r#else {
1894 // final `else {..}`
1895 if !blocks.is_empty() {
1896 if let ExprKind::Block(block, _) = expr.kind {
1904 /// Checks if the given function kind is an async function.
1905 pub fn is_async_fn(kind: FnKind<'_>) -> bool {
1907 FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async,
1908 FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async,
1909 FnKind::Closure => false,
1913 /// Peels away all the compiler generated code surrounding the body of an async function,
1914 pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
1915 if let ExprKind::Call(
1919 kind: ExprKind::Closure(&Closure { body, .. }),
1925 if let ExprKind::Block(
1930 kind: ExprKind::DropTemps(expr),
1936 ) = tcx.hir().body(body).value.kind
1944 // check if expr is calling method or function with #[must_use] attribute
1945 pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1946 let did = match expr.kind {
1947 ExprKind::Call(path, _) => if_chain! {
1948 if let ExprKind::Path(ref qpath) = path.kind;
1949 if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
1956 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1960 did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
1963 /// Checks if an expression represents the identity function
1964 /// Only examines closures and `std::convert::identity`
1965 pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1966 /// Checks if a function's body represents the identity function. Looks for bodies of the form:
1968 /// * `|x| return x`
1969 /// * `|x| { return x }`
1970 /// * `|x| { return x; }`
1971 fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1972 let id = if_chain! {
1973 if let [param] = func.params;
1974 if let PatKind::Binding(_, id, _, _) = param.pat.kind;
1982 let mut expr = func.value;
1986 ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
1987 | ExprKind::Ret(Some(e)) => expr = e,
1989 ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
1991 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
1992 if let ExprKind::Ret(Some(ret_val)) = e.kind;
2000 _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
2006 ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
2007 _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
2011 /// Gets the node where an expression is either used, or it's type is unified with another branch.
2012 /// Returns both the node and the `HirId` of the closest child node.
2013 pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
2014 let mut child_id = expr.hir_id;
2015 let mut iter = tcx.hir().parent_iter(child_id);
2019 Some((id, Node::Block(_))) => child_id = id,
2020 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
2021 Some((_, Node::Expr(expr))) => match expr.kind {
2022 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
2023 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
2024 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
2025 _ => break Some((Node::Expr(expr), child_id)),
2027 Some((_, node)) => break Some((node, child_id)),
2032 /// Checks if the result of an expression is used, or it's type is unified with another branch.
2033 pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2035 get_expr_use_or_unification_node(tcx, expr),
2038 kind: StmtKind::Expr(_)
2040 | StmtKind::Local(Local {
2042 kind: PatKind::Wild,
2054 /// Checks if the expression is the final expression returned from a block.
2055 pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
2056 matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
2059 pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
2060 if !is_no_std_crate(cx) {
2062 } else if !is_no_core_crate(cx) {
2069 pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
2070 cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
2071 if let ast::AttrKind::Normal(ref normal) = attr.kind {
2072 normal.item.path == sym::no_std
2079 pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
2080 cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
2081 if let ast::AttrKind::Normal(ref normal) = attr.kind {
2082 normal.item.path == sym::no_core
2089 /// Check if parent of a hir node is a trait implementation block.
2090 /// For example, `f` in
2093 /// # trait Trait { fn f(); }
2094 /// impl Trait for S {
2098 pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
2099 if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
2100 matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
2106 /// Check if it's even possible to satisfy the `where` clause for the item.
2108 /// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
2111 /// fn foo() where i32: Iterator {
2112 /// for _ in 2i32 {}
2115 pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
2116 use rustc_trait_selection::traits;
2122 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
2123 traits::impossible_predicates(
2125 traits::elaborate_predicates(cx.tcx, predicates)
2126 .map(|o| o.predicate)
2127 .collect::<Vec<_>>(),
2131 /// Returns the `DefId` of the callee if the given expression is a function or method call.
2132 pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
2134 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
2137 kind: ExprKind::Path(qpath),
2138 hir_id: path_hir_id,
2143 // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
2144 // deref to fn pointers, dyn Fn, impl Fn - #8850
2145 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
2146 cx.typeck_results().qpath_res(qpath, *path_hir_id)
2157 /// Returns `Option<String>` where String is a textual representation of the type encapsulated in
2158 /// the slice iff the given expression is a slice of primitives (as defined in the
2159 /// `is_recursively_primitive_type` function) and `None` otherwise.
2160 pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
2161 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
2162 let expr_kind = expr_type.kind();
2163 let is_primitive = match expr_kind {
2164 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
2165 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
2166 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
2167 is_recursively_primitive_type(*element_type)
2176 // if we have wrappers like Array, Slice or Tuple, print these
2177 // and get the type enclosed in the slice ref
2178 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
2179 rustc_ty::Slice(..) => return Some("slice".into()),
2180 rustc_ty::Array(..) => return Some("array".into()),
2181 rustc_ty::Tuple(..) => return Some("tuple".into()),
2183 // is_recursively_primitive_type() should have taken care
2184 // of the rest and we can rely on the type that is found
2185 let refs_peeled = expr_type.peel_refs();
2186 return Some(refs_peeled.walk().last().unwrap().to_string());
2193 /// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
2194 /// `hash` must be comformed with `eq`
2195 pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
2197 Hash: Fn(&T) -> u64,
2198 Eq: Fn(&T, &T) -> bool,
2201 [a, b] if eq(a, b) => return vec![(a, b)],
2202 _ if exprs.len() <= 2 => return vec![],
2206 let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
2208 let mut map: UnhashMap<u64, Vec<&_>> =
2209 UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
2212 match map.entry(hash(expr)) {
2213 Entry::Occupied(mut o) => {
2216 match_expr_list.push((o, expr));
2219 o.get_mut().push(expr);
2221 Entry::Vacant(v) => {
2222 v.insert(vec![expr]);
2230 /// Peels off all references on the pattern. Returns the underlying pattern and the number of
2231 /// references removed.
2232 pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2233 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
2234 if let PatKind::Ref(pat, _) = pat.kind {
2235 peel(pat, count + 1)
2243 /// Peels of expressions while the given closure returns `Some`.
2244 pub fn peel_hir_expr_while<'tcx>(
2245 mut expr: &'tcx Expr<'tcx>,
2246 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2247 ) -> &'tcx Expr<'tcx> {
2248 while let Some(e) = f(expr) {
2254 /// Peels off up to the given number of references on the expression. Returns the underlying
2255 /// expression and the number of references removed.
2256 pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
2257 let mut remaining = count;
2258 let e = peel_hir_expr_while(expr, |e| match e.kind {
2259 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
2265 (e, count - remaining)
2268 /// Peels off all references on the expression. Returns the underlying expression and the number of
2269 /// references removed.
2270 pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
2272 let e = peel_hir_expr_while(expr, |e| match e.kind {
2273 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
2282 /// Peels off all references on the type. Returns the underlying type and the number of references
2284 pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
2288 TyKind::Rptr(_, ref_ty) => {
2292 _ => break (ty, count),
2297 /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
2298 /// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
2299 pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2302 ExprKind::AddrOf(_, _, e) => expr = e,
2303 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2310 pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2311 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2312 if let Res::Def(_, def_id) = path.res {
2313 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
2319 static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
2321 fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
2322 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2323 let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
2324 let value = map.entry(module);
2326 Entry::Occupied(entry) => f(entry.get()),
2327 Entry::Vacant(entry) => {
2328 let mut names = Vec::new();
2329 for id in tcx.hir().module_items(module) {
2330 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
2331 && let item = tcx.hir().item(id)
2332 && let ItemKind::Const(ty, _body) = item.kind {
2333 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2334 // We could also check for the type name `test::TestDescAndFn`
2335 if let Res::Def(DefKind::Struct, _) = path.res {
2336 let has_test_marker = tcx
2338 .attrs(item.hir_id())
2340 .any(|a| a.has_name(sym::rustc_test_marker));
2341 if has_test_marker {
2342 names.push(item.ident.name);
2348 names.sort_unstable();
2349 f(entry.insert(names))
2354 /// Checks if the function containing the given `HirId` is a `#[test]` function
2356 /// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
2357 pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
2358 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2361 // Since you can nest functions we need to collect all until we leave
2363 .any(|(_id, node)| {
2364 if let Node::Item(item) = node {
2365 if let ItemKind::Fn(_, _, _) = item.kind {
2366 // Note that we have sorted the item names in the visitor,
2367 // so the binary_search gets the same as `contains`, but faster.
2368 return names.binary_search(&item.ident.name).is_ok();
2376 /// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
2378 /// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function
2379 pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
2380 fn is_cfg_test(attr: &Attribute) -> bool {
2381 if attr.has_name(sym::cfg)
2382 && let Some(items) = attr.meta_item_list()
2383 && let [item] = &*items
2384 && item.has_name(sym::test)
2393 .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id))
2397 /// Checks whether item either has `test` attribute applied, or
2398 /// is a module with `test` in its name.
2400 /// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
2401 pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
2402 is_in_test_function(tcx, item.hir_id())
2403 || matches!(item.kind, ItemKind::Mod(..))
2404 && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests")
2407 /// Walks the HIR tree from the given expression, up to the node where the value produced by the
2408 /// expression is consumed. Calls the function for every node encountered this way until it returns
2411 /// This allows walking through `if`, `match`, `break`, block expressions to find where the value
2412 /// produced by the expression is consumed.
2413 pub fn walk_to_expr_usage<'tcx, T>(
2414 cx: &LateContext<'tcx>,
2416 mut f: impl FnMut(Node<'tcx>, HirId) -> Option<T>,
2418 let map = cx.tcx.hir();
2419 let mut iter = map.parent_iter(e.hir_id);
2420 let mut child_id = e.hir_id;
2422 while let Some((parent_id, parent)) = iter.next() {
2423 if let Some(x) = f(parent, child_id) {
2426 let parent = match parent {
2428 Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
2429 child_id = parent_id;
2432 Node::Arm(a) if a.body.hir_id == child_id => {
2433 child_id = parent_id;
2439 ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
2440 ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
2442 iter = map.parent_iter(id);
2444 ExprKind::Block(..) => child_id = parent_id,
2451 /// Checks whether a given span has any comment token
2452 /// This checks for all types of comment: line "//", block "/**", doc "///" "//!"
2453 pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
2454 let Ok(snippet) = sm.span_to_snippet(span) else { return false };
2455 return tokenize(&snippet).any(|token| {
2458 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
2463 /// Return all the comments a given span contains
2464 /// Comments are returned wrapped with their relevant delimiters
2465 pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
2466 let snippet = sm.span_to_snippet(span).unwrap_or_default();
2467 let mut comments_buf: Vec<String> = Vec::new();
2468 let mut index: usize = 0;
2470 for token in tokenize(&snippet) {
2471 let token_range = index..(index + token.len as usize);
2472 index += token.len as usize;
2474 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => {
2475 if let Some(comment) = snippet.get(token_range) {
2476 comments_buf.push(comment.to_string());
2483 comments_buf.join("\n")
2486 macro_rules! op_utils {
2487 ($($name:ident $assign:ident)*) => {
2488 /// Binary operation traits like `LangItem::Add`
2489 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2491 /// Operator-Assign traits like `LangItem::AddAssign`
2492 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2494 /// Converts `BinOpKind::Add` to `(LangItem::Add, LangItem::AddAssign)`, for example
2495 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2497 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*