#![feature(box_patterns)]
-#![feature(in_band_lifetimes)]
+#![feature(control_flow_enum)]
#![feature(let_else)]
+#![feature(once_cell)]
#![feature(rustc_private)]
-#![feature(control_flow_enum)]
#![recursion_limit = "512"]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
pub mod eager_or_lazy;
pub mod higher;
mod hir_utils;
+pub mod macros;
pub mod msrvs;
pub mod numeric_literal;
pub mod paths;
use std::collections::hash_map::Entry;
use std::hash::BuildHasherDefault;
+use std::lazy::SyncOnceCell;
+use std::sync::{Mutex, MutexGuard};
use if_chain::if_chain;
use rustc_ast::ast::{self, Attribute, LitKind};
+use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unhash::UnhashMap;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID};
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
-use rustc_hir::intravisit::{walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
use rustc_hir::{
- def, Arm, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl,
- ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local,
- MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem,
- TraitItemKind, TraitRef, TyKind, UnOp,
+ def, lang_items, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr,
+ ExprKind, FnDecl, ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem,
+ Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
+ Target, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
};
use rustc_lint::{LateContext, Level, Lint, LintContext};
-use rustc_middle::hir::exports::Export;
-use rustc_middle::hir::map::Map;
use rustc_middle::hir::place::PlaceBase;
use rustc_middle::ty as rustc_ty;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
extract_msrv_attr!(@EarlyContext);
};
(@$context:ident$(, $call:tt)?) => {
- fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) {
+ fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
use $crate::get_unique_inner_attr;
match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") {
Some(msrv_attr) => {
/// instead.
///
/// Examples:
-/// ```ignore
+/// ```
/// let abc = 1;
/// // ^ output
/// let def = abc;
-/// dbg!(def)
+/// dbg!(def);
/// // ^^^ input
///
/// // or...
/// let abc = 1;
/// let def = abc + 2;
/// // ^^^^^^^ output
-/// dbg!(def)
+/// dbg!(def);
/// // ^^^ input
/// ```
pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
/// ```
pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
let parent_id = cx.tcx.hir().get_parent_item(id);
- match cx.tcx.hir().get(parent_id) {
+ match cx.tcx.hir().get_by_def_id(parent_id) {
Node::Item(&Item {
kind: ItemKind::Const(..) | ItemKind::Static(..),
..
}
/// Checks if the first type parameter is a lang item.
-pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> {
+pub fn is_ty_param_lang_item<'tcx>(
+ cx: &LateContext<'_>,
+ qpath: &QPath<'tcx>,
+ item: LangItem,
+) -> Option<&'tcx hir::Ty<'tcx>> {
let ty = get_qpath_generic_tys(qpath).next()?;
if let TyKind::Path(qpath) = &ty.kind {
}
/// Checks if the first type parameter is a diagnostic item.
-pub fn is_ty_param_diagnostic_item(
+pub fn is_ty_param_diagnostic_item<'tcx>(
cx: &LateContext<'_>,
qpath: &QPath<'tcx>,
item: Symbol,
}
}
-pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> {
+pub fn get_qpath_generics<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> {
match path {
QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args),
QPath::TypeRelative(_, s) => s.args,
}
}
-pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
+pub fn get_qpath_generic_tys<'tcx>(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
get_qpath_generics(path)
.map_or([].as_ref(), |a| a.args)
.iter()
}
};
}
- fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export> {
- tcx.item_children(def_id)
+ fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option<Res> {
+ match tcx.def_kind(def_id) {
+ DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
+ .module_children(def_id)
+ .iter()
+ .find(|item| item.ident.name.as_str() == name)
+ .map(|child| child.res.expect_non_local()),
+ DefKind::Impl => tcx
+ .associated_item_def_ids(def_id)
+ .iter()
+ .copied()
+ .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name)
+ .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)),
+ _ => None,
+ }
+ }
+ fn find_primitive(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
+ if let Some(&(index, Target::Impl)) = lang_items::ITEM_REFS.get(&Symbol::intern(name)) {
+ tcx.lang_items().items()[index]
+ } else {
+ None
+ }
+ }
+ fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
+ tcx.crates(())
.iter()
- .find(|item| item.ident.name.as_str() == name)
+ .find(|&&num| tcx.crate_name(num).as_str() == name)
+ .map(CrateNum::as_def_id)
}
- let (krate, first, path) = match *path {
- [krate, first, ref path @ ..] => (krate, first, path),
+ let (base, first, path) = match *path {
+ [base, first, ref path @ ..] => (base, first, path),
[primitive] => {
return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
},
_ => return Res::Err,
};
let tcx = cx.tcx;
- let crates = tcx.crates(());
- let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate));
- let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first));
+ let first = try_res!(
+ find_primitive(tcx, base)
+ .or_else(|| find_crate(tcx, base))
+ .and_then(|id| item_child_by_name(tcx, id, first))
+ );
+
let last = path
.iter()
.copied()
- // `get_def_path` seems to generate these empty segments for extern blocks.
- // We can just ignore them.
- .filter(|segment| !segment.is_empty())
// for each segment, find the child item
- .try_fold(first, |item, segment| {
- let def_id = item.res.def_id();
+ .try_fold(first, |res, segment| {
+ let def_id = res.def_id();
if let Some(item) = item_child_by_name(tcx, def_id, segment) {
Some(item)
- } else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
+ } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
// it is not a child item so check inherent impl items
tcx.inherent_impls(def_id)
.iter()
None
}
});
- try_res!(last).res.expect_non_local()
+ try_res!(last).expect_non_local()
}
/// Convenience function to get the `DefId` of a trait by path.
/// }
/// }
/// ```
-pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx TraitRef<'tcx>> {
+pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
// Get the implemented trait for the current function
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
if_chain! {
- if parent_impl != hir::CRATE_HIR_ID;
- if let hir::Node::Item(item) = cx.tcx.hir().get(parent_impl);
+ if parent_impl != CRATE_DEF_ID;
+ if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl);
if let hir::ItemKind::Impl(impl_) = &item.kind;
then { return impl_.of_trait.as_ref(); }
}
(result, root)
}
+/// Gets the mutability of the custom deref adjustment, if any.
+pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
+ cx.typeck_results()
+ .expr_adjustments(e)
+ .iter()
+ .find_map(|a| match a.kind {
+ Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
+ Adjust::Deref(None) => None,
+ _ => Some(None),
+ })
+ .and_then(|x| x)
+}
+
/// Checks if two expressions can be mutably borrowed simultaneously
/// and they aren't dependent on borrowing same thing twice
pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
if !eq_expr_value(cx, r1, r2) {
return true;
}
+ if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
+ return false;
+ }
+
for (x1, x2) in s1.iter().zip(s2.iter()) {
+ if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
+ return false;
+ }
+
match (&x1.kind, &x2.kind) {
(ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
if i1 != i2 {
false
}
+/// Return true if the expr is equal to `Default::default` when evaluated.
+pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
+ if_chain! {
+ if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
+ if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
+ if is_diag_trait_item(cx, repl_def_id, sym::Default)
+ || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
+ then {
+ true
+ }
+ else {
+ false
+ }
+ }
+}
+
/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
/// It doesn't cover all cases, for example indirect function calls (some of std
/// functions are supported) but it is the best we have.
_ => false,
},
ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
- ExprKind::Repeat(x, y) => if_chain! {
- if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(y.body).value.kind;
+ ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
+ if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
if let LitKind::Int(v, _) = const_lit.node;
if v <= 32 && is_default_equivalent(cx, x);
then {
false
}
},
- ExprKind::Call(repl_func, _) => if_chain! {
- if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
- if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
- if is_diag_trait_item(cx, repl_def_id, sym::Default)
- || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
- then {
- true
- }
- else {
- false
- }
- },
+ ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func),
ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone),
ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
_ => false,
///
/// Note that this check is not recursive, so passing the `if` expression will always return true
/// even though sub-expressions might return false.
-pub fn can_move_expr_to_closure_no_visit(
+pub fn can_move_expr_to_closure_no_visit<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
loop_ids: &[HirId],
| ExprKind::Continue(_)
| ExprKind::Ret(_)
| ExprKind::Yield(..)
- | ExprKind::InlineAsm(_)
- | ExprKind::LlvmInlineAsm(_) => false,
+ | ExprKind::InlineAsm(_) => false,
// Accessing a field of a local value can only be done if the type isn't
// partially moved.
ExprKind::Field(
/// Note as this will walk up to parent expressions until the capture can be determined it should
/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
/// function argument (other than a receiver).
-pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
+pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
let mut capture = CaptureKind::Ref(Mutability::Not);
pat.each_binding_or_first(&mut |_, id, span, _| match cx
/// Checks if the expression can be moved into a closure as is. This will return a list of captures
/// if so, otherwise, `None`.
-pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
+pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
struct V<'cx, 'tcx> {
cx: &'cx LateContext<'tcx>,
// Stack of potential break targets contained in the expression.
/// mutable reference.
captures: HirIdMap<CaptureKind>,
}
- impl Visitor<'tcx> for V<'_, 'tcx> {
- type Map = ErasedMap<'tcx>;
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::None
- }
-
+ impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
if !self.allow_closure {
return;
};
if !self.locals.contains(&local_id) {
let capture = match capture.info.capture_kind {
- UpvarCapture::ByValue(_) => CaptureKind::Value,
- UpvarCapture::ByRef(borrow) => match borrow.kind {
+ UpvarCapture::ByValue => CaptureKind::Value,
+ UpvarCapture::ByRef(kind) => match kind {
BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
CaptureKind::Ref(Mutability::Mut)
let mut current = expr;
for _ in 0..max_depth {
- if let ExprKind::MethodCall(path, span, args, _) = ¤t.kind {
+ if let ExprKind::MethodCall(path, args, _) = ¤t.kind {
if args.iter().any(|e| e.span.from_expansion()) {
break;
}
method_names.push(path.ident.name);
arg_lists.push(&**args);
- spans.push(*span);
+ spans.push(path.ident.span);
current = &args[0];
} else {
break;
let mut matched = Vec::with_capacity(methods.len());
for method_name in methods.iter().rev() {
// method chains are stored last -> first
- if let ExprKind::MethodCall(path, _, args, _) = current.kind {
+ if let ExprKind::MethodCall(path, args, _) = current.kind {
if path.ident.name.as_str() == *method_name {
if args.iter().any(|e| e.span.from_expansion()) {
return None;
/// Returns `true` if the expression is in the program's `#[panic_handler]`.
pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
let parent = cx.tcx.hir().get_parent_item(e.hir_id);
- let def_id = cx.tcx.hir().local_def_id(parent).to_def_id();
- Some(def_id) == cx.tcx.lang_items().panic_impl()
+ Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
}
/// Gets the name of the item the expression is in, if available.
pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
- match cx.tcx.hir().find(parent_id) {
+ match cx.tcx.hir().find_by_def_id(parent_id) {
Some(
Node::Item(Item { ident, .. })
| Node::TraitItem(TraitItem { ident, .. })
}
impl<'tcx> Visitor<'tcx> for ContainsName {
- type Map = Map<'tcx>;
-
fn visit_name(&mut self, _: Span, name: Symbol) {
if self.name == name {
self.result = true;
}
}
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::None
- }
}
/// Checks if an `Expr` contains a certain name.
found
}
-/// Finds calls of the specified macros in a function body.
-pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
- let mut result = Vec::new();
- expr_visitor_no_bodies(|expr| {
- if names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
- result.push(expr.span);
- }
- true
- })
- .visit_expr(&body.value);
- result
-}
-
/// Extends the span to the beginning of the spans line, incl. whitespaces.
///
-/// ```rust,ignore
+/// ```rust
/// let x = ();
/// // ^^
/// // will be converted to
}
/// Gets the loop or closure enclosing the given expression, if any.
-pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+pub fn get_enclosing_loop_or_closure<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
match node {
Node::Expr(
cx.typeck_results().adjustments().get(e.hir_id).is_some()
}
-/// Returns the pre-expansion span if is this comes from an expansion of the
+/// Returns the pre-expansion span if this comes from an expansion of the
/// macro `name`.
/// See also [`is_direct_expn_of`].
#[must_use]
/// of the macro `name`.
/// The difference with [`is_expn_of`] is that in
/// ```rust
-/// # macro_rules! foo { ($e:tt) => { $e } }; macro_rules! bar { ($e:expr) => { $e } }
+/// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
+/// # macro_rules! bar { ($e:expr) => { $e } }
/// foo!(bar!(42));
/// ```
/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
return true;
}
prev_enclosing_node = Some(enclosing_node);
- enclosing_node = map.get_parent_item(enclosing_node);
+ enclosing_node = map.local_def_id_to_hir_id(map.get_parent_item(enclosing_node));
}
false
path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
}
-pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
- if let ExprKind::Call(func, [arg]) = expr.kind {
- expr_path_res(cx, func)
- .opt_def_id()
- .map_or(false, |id| match_panic_def_id(cx, id))
- .then(|| arg)
- } else {
- None
- }
-}
-
-pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
- match_any_def_paths(
- cx,
- did,
- &[
- &paths::BEGIN_PANIC,
- &paths::PANIC_ANY,
- &paths::PANICKING_PANIC,
- &paths::PANICKING_PANIC_FMT,
- &paths::PANICKING_PANIC_STR,
- ],
- )
- .is_some()
-}
-
/// Returns the list of condition expressions and the list of blocks in a
/// sequence of `if/else`.
/// E.g., this returns `([a, b], [c, d, e])` for the expression
}
/// Peels away all the compiler generated code surrounding the body of an async function,
-pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
+pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
if let ExprKind::Call(
_,
&[
None
}
},
- ExprKind::MethodCall(_, _, _, _) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
+ ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
_ => None,
};
}
/// Gets the node where an expression is either used, or it's type is unified with another branch.
-pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
+pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
let mut child_id = expr.hir_id;
let mut iter = tcx.hir().parent_iter(child_id);
loop {
/// Check if parent of a hir node is a trait implementation block.
/// For example, `f` in
-/// ```rust,ignore
+/// ```rust
+/// # struct S;
+/// # trait Trait { fn f(); }
/// impl Trait for S {
/// fn f() {}
/// }
.predicates_of(did)
.predicates
.iter()
- .filter_map(|(p, _)| if p.is_global(cx.tcx) { Some(*p) } else { None });
+ .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
traits::impossible_predicates(
cx.tcx,
traits::elaborate_predicates(cx.tcx, predicates)
if is_primitive {
// if we have wrappers like Array, Slice or Tuple, print these
// and get the type enclosed in the slice ref
- match expr_type.peel_refs().walk(cx.tcx).nth(1).unwrap().expect_ty().kind() {
+ match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
rustc_ty::Slice(..) => return Some("slice".into()),
rustc_ty::Array(..) => return Some("array".into()),
rustc_ty::Tuple(..) => return Some("tuple".into()),
// is_recursively_primitive_type() should have taken care
// of the rest and we can rely on the type that is found
let refs_peeled = expr_type.peel_refs();
- return Some(refs_peeled.walk(cx.tcx).last().unwrap().to_string());
+ return Some(refs_peeled.walk().last().unwrap().to_string());
},
}
}
/// Peels off all references on the pattern. Returns the underlying pattern and the number of
/// references removed.
-pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
- fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
+pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
+ fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
if let PatKind::Ref(pat, _) = pat.kind {
peel(pat, count + 1)
} else {
/// Peels off up to the given number of references on the expression. Returns the underlying
/// expression and the number of references removed.
-pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
+pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
let mut remaining = count;
let e = peel_hir_expr_while(expr, |e| match e.kind {
ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
/// Peels off all references on the expression. Returns the underlying expression and the number of
/// references removed.
-pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
+pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
let mut count = 0;
let e = peel_hir_expr_while(expr, |e| match e.kind {
ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
false
}
-struct VisitConstTestStruct<'tcx> {
+struct TestItemNamesVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
names: Vec<Symbol>,
- found: bool,
}
-impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'hir> {
+
+impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> {
fn visit_item(&mut self, item: &Item<'_>) {
if let ItemKind::Const(ty, _body) = item.kind {
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
// We could also check for the type name `test::TestDescAndFn`
- // and the `#[rustc_test_marker]` attribute?
if let Res::Def(DefKind::Struct, _) = path.res {
let has_test_marker = self
.tcx
.attrs(item.hir_id())
.iter()
.any(|a| a.has_name(sym::rustc_test_marker));
- if has_test_marker && self.names.contains(&item.ident.name) {
- self.found = true;
+ if has_test_marker {
+ self.names.push(item.ident.name);
}
}
}
fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {}
}
+static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = SyncOnceCell::new();
+
+fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
+ let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
+ let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
+ match map.entry(module) {
+ Entry::Occupied(entry) => f(entry.get()),
+ Entry::Vacant(entry) => {
+ let mut visitor = TestItemNamesVisitor { tcx, names: Vec::new() };
+ tcx.hir().visit_item_likes_in_module(module, &mut visitor);
+ visitor.names.sort_unstable();
+ f(&*entry.insert(visitor.names))
+ },
+ }
+}
+
/// Checks if the function containing the given `HirId` is a `#[test]` function
///
/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`.
pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
- let names: Vec<_> = tcx
- .hir()
- .parent_iter(id)
- // Since you can nest functions we need to collect all until we leave
- // function scope
- .filter_map(|(_id, node)| {
- if let Node::Item(item) = node {
- if let ItemKind::Fn(_, _, _) = item.kind {
- return Some(item.ident.name);
+ with_test_item_names(tcx, tcx.parent_module(id), |names| {
+ tcx.hir()
+ .parent_iter(id)
+ // Since you can nest functions we need to collect all until we leave
+ // function scope
+ .any(|(_id, node)| {
+ if let Node::Item(item) = node {
+ if let ItemKind::Fn(_, _, _) = item.kind {
+ // Note that we have sorted the item names in the visitor,
+ // so the binary_search gets the same as `contains`, but faster.
+ return names.binary_search(&item.ident.name).is_ok();
+ }
}
- }
- None
- })
- .collect();
- let parent_mod = tcx.parent_module(id);
- let mut vis = VisitConstTestStruct {
- tcx,
- names,
- found: false,
- };
- tcx.hir().visit_item_likes_in_module(parent_mod, &mut vis);
- vis.found
+ false
+ })
+ })
}
/// Checks whether item either has `test` attribute applied, or