Rollup merge of #92849 - flip1995:clippyup, r=Manishearth

Clippyup

r? ```@Manishearth```
This commit is contained in:
Matthias Krüger
2022-01-14 07:47:37 +01:00
committed by GitHub
223 changed files with 3261 additions and 1687 deletions

View File

@@ -5,7 +5,6 @@
#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
use crate::{both, over};
use if_chain::if_chain;
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, *};
use rustc_span::symbol::Ident;
@@ -679,34 +678,3 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
_ => false,
}
}
/// Extract args from an assert-like macro.
///
/// Currently working with:
/// - `assert_eq!` and `assert_ne!`
/// - `debug_assert_eq!` and `debug_assert_ne!`
///
/// For example:
///
/// `debug_assert_eq!(a, b)` will return Some([a, b])
pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> {
if_chain! {
if let ExprKind::If(_, ref block, _) = expr.kind;
if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind;
then {
expr = e;
}
}
if_chain! {
if let ExprKind::Block(ref block, _) = expr.kind;
if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind;
if let ExprKind::Match(ref match_expr, _) = expr.kind;
if let ExprKind::Tup(ref tup) = match_expr.kind;
if let [a, b, ..] = tup.as_slice();
if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind);
then {
return Some([&*a, &*b]);
}
}
None
}

View File

@@ -223,7 +223,7 @@ pub fn constant_simple<'tcx>(
constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
}
pub fn constant_full_int(
pub fn constant_full_int<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,

View File

@@ -198,7 +198,7 @@ pub fn span_lint_hir_and_then(
/// |
/// = note: `-D fold-any` implied by `-D warnings`
/// ```
#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))]
#[cfg_attr(feature = "internal", allow(clippy::collapsible_span_lint_calls))]
pub fn span_lint_and_sugg<'a, T: LintContext>(
cx: &'a T,
lint: &'static Lint,

View File

@@ -45,7 +45,12 @@ impl ops::BitOrAssign for EagernessSuggestion {
}
/// Determine the eagerness of the given function call.
fn fn_eagerness(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, args: &'tcx [Expr<'_>]) -> EagernessSuggestion {
fn fn_eagerness<'tcx>(
cx: &LateContext<'tcx>,
fn_id: DefId,
name: Symbol,
args: &'tcx [Expr<'_>],
) -> EagernessSuggestion {
use EagernessSuggestion::{Eager, Lazy, NoChange};
let name = name.as_str();
@@ -92,7 +97,7 @@ fn fn_eagerness(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, args: &'tcx
}
#[allow(clippy::too_many_lines)]
fn expr_eagerness(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
struct V<'cx, 'tcx> {
cx: &'cx LateContext<'tcx>,
eagerness: EagernessSuggestion,
@@ -225,11 +230,11 @@ fn expr_eagerness(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggest
}
/// Whether the given expression should be changed to evaluate eagerly
pub fn switch_to_eager_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
pub fn switch_to_eager_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
expr_eagerness(cx, expr) == EagernessSuggestion::Eager
}
/// Whether the given expression should be changed to evaluate lazily
pub fn switch_to_lazy_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
pub fn switch_to_lazy_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
expr_eagerness(cx, expr) == EagernessSuggestion::Lazy
}

View File

@@ -3,15 +3,13 @@
#![deny(clippy::missing_docs_in_private_items)]
use crate::ty::is_type_diagnostic_item;
use crate::{is_expn_of, last_path_segment, match_def_path, paths};
use crate::{is_expn_of, match_def_path, paths};
use if_chain::if_chain;
use rustc_ast::ast::{self, LitKind};
use rustc_hir as hir;
use rustc_hir::{
Arm, Block, BorrowKind, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StmtKind, UnOp,
};
use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath};
use rustc_lint::LateContext;
use rustc_span::{sym, symbol, ExpnKind, Span, Symbol};
use rustc_span::{sym, symbol, Span};
/// The essential nodes of a desugared for loop as well as the entire span:
/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
@@ -428,293 +426,6 @@ pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
}
}
/// Extract args from an assert-like macro.
/// Currently working with:
/// - `assert!`, `assert_eq!` and `assert_ne!`
/// - `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!`
/// For example:
/// `assert!(expr)` will return `Some([expr])`
/// `debug_assert_eq!(a, b)` will return `Some([a, b])`
pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx Expr<'tcx>>> {
/// Try to match the AST for a pattern that contains a match, for example when two args are
/// compared
fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> {
if_chain! {
if let ExprKind::Match(headerexpr, _, _) = &matchblock_expr.kind;
if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind;
if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind;
if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind;
then {
return Some(vec![lhs, rhs]);
}
}
None
}
if let ExprKind::Block(block, _) = e.kind {
if block.stmts.len() == 1 {
if let StmtKind::Semi(matchexpr) = block.stmts.get(0)?.kind {
// macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`)
if_chain! {
if let Some(If { cond, .. }) = If::hir(matchexpr);
if let ExprKind::Unary(UnOp::Not, condition) = cond.kind;
then {
return Some(vec![condition]);
}
}
// debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
if_chain! {
if let ExprKind::Block(matchblock,_) = matchexpr.kind;
if let Some(matchblock_expr) = matchblock.expr;
then {
return ast_matchblock(matchblock_expr);
}
}
}
} else if let Some(matchblock_expr) = block.expr {
// macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
return ast_matchblock(matchblock_expr);
}
}
None
}
/// A parsed `format!` expansion
pub struct FormatExpn<'tcx> {
/// Span of `format!(..)`
pub call_site: Span,
/// Inner `format_args!` expansion
pub format_args: FormatArgsExpn<'tcx>,
}
impl FormatExpn<'tcx> {
/// Parses an expanded `format!` invocation
pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
if_chain! {
if let ExprKind::Block(block, _) = expr.kind;
if let [stmt] = block.stmts;
if let StmtKind::Local(local) = stmt.kind;
if let Some(init) = local.init;
if let ExprKind::Call(_, [format_args]) = init.kind;
let expn_data = expr.span.ctxt().outer_expn_data();
if let ExpnKind::Macro(_, sym::format) = expn_data.kind;
if let Some(format_args) = FormatArgsExpn::parse(format_args);
then {
Some(FormatExpn {
call_site: expn_data.call_site,
format_args,
})
} else {
None
}
}
}
}
/// A parsed `format_args!` expansion
pub struct FormatArgsExpn<'tcx> {
/// Span of the first argument, the format string
pub format_string_span: Span,
/// Values passed after the format string
pub value_args: Vec<&'tcx Expr<'tcx>>,
/// String literal expressions which represent the format string split by "{}"
pub format_string_parts: &'tcx [Expr<'tcx>],
/// Symbols corresponding to [`Self::format_string_parts`]
pub format_string_symbols: Vec<Symbol>,
/// Expressions like `ArgumentV1::new(arg0, Debug::fmt)`
pub args: &'tcx [Expr<'tcx>],
/// The final argument passed to `Arguments::new_v1_formatted`, if applicable
pub fmt_expr: Option<&'tcx Expr<'tcx>>,
}
impl FormatArgsExpn<'tcx> {
/// Parses an expanded `format_args!` or `format_args_nl!` invocation
pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
if_chain! {
if let ExpnKind::Macro(_, name) = expr.span.ctxt().outer_expn_data().kind;
let name = name.as_str();
if name.ends_with("format_args") || name.ends_with("format_args_nl");
if let ExprKind::Call(_, args) = expr.kind;
if let Some((strs_ref, args, fmt_expr)) = match args {
// Arguments::new_v1
[strs_ref, args] => Some((strs_ref, args, None)),
// Arguments::new_v1_formatted
[strs_ref, args, fmt_expr, _unsafe_arg] => Some((strs_ref, args, Some(fmt_expr))),
_ => None,
};
if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind;
if let ExprKind::Array(format_string_parts) = strs_arr.kind;
if let Some(format_string_symbols) = format_string_parts
.iter()
.map(|e| {
if let ExprKind::Lit(lit) = &e.kind {
if let LitKind::Str(symbol, _style) = lit.node {
return Some(symbol);
}
}
None
})
.collect();
if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args.kind;
if let ExprKind::Match(args, [arm], _) = args.kind;
if let ExprKind::Tup(value_args) = args.kind;
if let Some(value_args) = value_args
.iter()
.map(|e| match e.kind {
ExprKind::AddrOf(_, _, e) => Some(e),
_ => None,
})
.collect();
if let ExprKind::Array(args) = arm.body.kind;
then {
Some(FormatArgsExpn {
format_string_span: strs_ref.span,
value_args,
format_string_parts,
format_string_symbols,
args,
fmt_expr,
})
} else {
None
}
}
}
/// Returns a vector of `FormatArgsArg`.
pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
if let Some(expr) = self.fmt_expr {
if_chain! {
if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
if let ExprKind::Array(exprs) = expr.kind;
then {
exprs.iter().map(|fmt| {
if_chain! {
// struct `core::fmt::rt::v1::Argument`
if let ExprKind::Struct(_, fields, _) = fmt.kind;
if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position);
if let ExprKind::Lit(lit) = &position_field.expr.kind;
if let LitKind::Int(position, _) = lit.node;
if let Ok(i) = usize::try_from(position);
let arg = &self.args[i];
if let ExprKind::Call(_, [arg_name, _]) = arg.kind;
if let ExprKind::Field(_, j) = arg_name.kind;
if let Ok(j) = j.name.as_str().parse::<usize>();
then {
Some(FormatArgsArg { value: self.value_args[j], arg, fmt: Some(fmt) })
} else {
None
}
}
}).collect()
} else {
None
}
}
} else {
Some(
self.value_args
.iter()
.zip(self.args.iter())
.map(|(value, arg)| FormatArgsArg { value, arg, fmt: None })
.collect(),
)
}
}
}
/// Type representing a `FormatArgsExpn`'s format arguments
pub struct FormatArgsArg<'tcx> {
/// An element of `value_args` according to `position`
pub value: &'tcx Expr<'tcx>,
/// An element of `args` according to `position`
pub arg: &'tcx Expr<'tcx>,
/// An element of `fmt_expn`
pub fmt: Option<&'tcx Expr<'tcx>>,
}
impl<'tcx> FormatArgsArg<'tcx> {
/// Returns true if any formatting parameters are used that would have an effect on strings,
/// like `{:+2}` instead of just `{}`.
pub fn has_string_formatting(&self) -> bool {
self.fmt.map_or(false, |fmt| {
// `!` because these conditions check that `self` is unformatted.
!if_chain! {
// struct `core::fmt::rt::v1::Argument`
if let ExprKind::Struct(_, fields, _) = fmt.kind;
if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
// struct `core::fmt::rt::v1::FormatSpec`
if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind;
let mut precision_found = false;
let mut width_found = false;
if subfields.iter().all(|field| {
match field.ident.name {
sym::precision => {
precision_found = true;
if let ExprKind::Path(ref precision_path) = field.expr.kind {
last_path_segment(precision_path).ident.name == sym::Implied
} else {
false
}
}
sym::width => {
width_found = true;
if let ExprKind::Path(ref width_qpath) = field.expr.kind {
last_path_segment(width_qpath).ident.name == sym::Implied
} else {
false
}
}
_ => true,
}
});
if precision_found && width_found;
then { true } else { false }
}
})
}
/// Returns true if the argument is formatted using `Display::fmt`.
pub fn is_display(&self) -> bool {
if_chain! {
if let ExprKind::Call(_, [_, format_field]) = self.arg.kind;
if let ExprKind::Path(QPath::Resolved(_, path)) = format_field.kind;
if let [.., t, _] = path.segments;
if t.ident.name == sym::Display;
then { true } else { false }
}
}
}
/// A parsed `panic!` expansion
pub struct PanicExpn<'tcx> {
/// Span of `panic!(..)`
pub call_site: Span,
/// Inner `format_args!` expansion
pub format_args: FormatArgsExpn<'tcx>,
}
impl PanicExpn<'tcx> {
/// Parses an expanded `panic!` invocation
pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
if_chain! {
if let ExprKind::Call(_, [format_args]) = expr.kind;
let expn_data = expr.span.ctxt().outer_expn_data();
if let Some(format_args) = FormatArgsExpn::parse(format_args);
then {
Some(PanicExpn {
call_site: expn_data.call_site,
format_args,
})
} else {
None
}
}
}
}
/// A parsed `Vec` initialization expression
#[derive(Clone, Copy)]
pub enum VecInitKind {

View File

@@ -6,9 +6,9 @@ use rustc_data_structures::fx::FxHasher;
use rustc_hir::def::Res;
use rustc_hir::HirIdMap;
use rustc_hir::{
BinOpKind, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId,
ArrayLen, BinOpKind, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId,
InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt,
StmtKind, Ty, TyKind, TypeBinding, ArrayLen
StmtKind, Ty, TyKind, TypeBinding,
};
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::LateContext;
@@ -171,11 +171,11 @@ impl HirEqInterExpr<'_, '_, '_> {
}
pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool {
match (left, right) {
(ArrayLen::Infer(..), ArrayLen::Infer(..)) => true,
(ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body),
(_, _) => false,
}
match (left, right) {
(ArrayLen::Infer(..), ArrayLen::Infer(..)) => true,
(ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body),
(_, _) => false,
}
}
pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
@@ -396,12 +396,10 @@ impl HirEqInterExpr<'_, '_, '_> {
}
#[allow(clippy::similar_names)]
fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
match (&left.kind, &right.kind) {
(&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec),
(&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => {
self.eq_ty(lt, rt) && self.eq_array_length(ll, rl)
},
(&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl),
(&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty)
},
@@ -853,6 +851,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
pub fn hash_path(&mut self, path: &Path<'_>) {
match path.res {
// constant hash since equality is dependant on inter-expression context
// e.g. The expressions `if let Some(x) = foo() {}` and `if let Some(y) = foo() {}` are considered equal
// even though the binding names are different and they have different `HirId`s.
Res::Local(_) => 1_usize.hash(&mut self.s),
_ => {
for seg in path.segments {
@@ -963,7 +963,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
pub fn hash_array_length(&mut self, length: ArrayLen) {
match length {
ArrayLen::Infer(..) => {}
ArrayLen::Infer(..) => {},
ArrayLen::Body(anon_const) => self.hash_body(anon_const.body),
}
}

View File

@@ -1,6 +1,5 @@
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(in_band_lifetimes)]
#![feature(let_else)]
#![feature(once_cell)]
#![feature(rustc_private)]
@@ -44,6 +43,7 @@ pub mod diagnostics;
pub mod eager_or_lazy;
pub mod higher;
mod hir_utils;
pub mod macros;
pub mod msrvs;
pub mod numeric_literal;
pub mod paths;
@@ -70,16 +70,16 @@ 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};
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
use rustc_hir::intravisit::{walk_expr, ErasedMap, FnKind, NestedVisitorMap, 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, ArrayLen
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::map::Map;
@@ -126,7 +126,7 @@ macro_rules! extract_msrv_attr {
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) => {
@@ -146,13 +146,6 @@ macro_rules! extract_msrv_attr {
};
}
/// Returns `true` if the span comes from a macro expansion, no matter if from a
/// macro by example or from a procedural macro
#[must_use]
pub fn in_macro(span: Span) -> bool {
span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
}
/// Returns `true` if the two spans come from differing expansions (i.e., one is
/// from a macro and one isn't).
#[must_use]
@@ -282,7 +275,11 @@ pub fn is_wild(pat: &Pat<'_>) -> bool {
}
/// 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 {
@@ -298,7 +295,7 @@ pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: La
}
/// 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,
@@ -375,7 +372,7 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
}
}
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,
@@ -383,7 +380,7 @@ pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>>
}
}
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()
@@ -522,7 +519,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
}
};
}
fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<Res> {
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)
@@ -538,18 +535,34 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
_ => 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(|&&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()
@@ -628,6 +641,19 @@ fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'
(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 {
@@ -636,7 +662,15 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -
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 {
@@ -710,8 +744,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
_ => false,
},
ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
ExprKind::Repeat(x, len) => if_chain! {
if let ArrayLen::Body(len) = len;
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);
@@ -760,7 +793,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
///
/// 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],
@@ -835,7 +868,7 @@ impl std::ops::BitOrAssign for CaptureKind {
/// 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
@@ -935,7 +968,7 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind
/// 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.
@@ -948,7 +981,7 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) ->
/// mutable reference.
captures: HirIdMap<CaptureKind>,
}
impl Visitor<'tcx> for V<'_, 'tcx> {
impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
type Map = ErasedMap<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
@@ -1146,19 +1179,6 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
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
@@ -1218,7 +1238,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
}
/// 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(
@@ -1687,32 +1707,6 @@ pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
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
@@ -1752,7 +1746,7 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool {
}
/// 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(
_,
&[
@@ -1856,7 +1850,7 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
}
/// 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 {
@@ -2062,8 +2056,8 @@ where
/// 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 {
@@ -2086,7 +2080,7 @@ pub fn peel_hir_expr_while<'tcx>(
/// 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 => {
@@ -2100,7 +2094,7 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>,
/// 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) => {
@@ -2183,7 +2177,7 @@ impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> {
static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = SyncOnceCell::new();
fn with_test_item_names(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
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) {

539
clippy_utils/src/macros.rs Normal file
View File

@@ -0,0 +1,539 @@
#![allow(clippy::similar_names)] // `expr` and `expn`
use crate::visitors::expr_visitor_no_bodies;
use arrayvec::ArrayVec;
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::intravisit::Visitor;
use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
use rustc_lint::LateContext;
use rustc_span::def_id::DefId;
use rustc_span::hygiene::{MacroKind, SyntaxContext};
use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol};
use std::ops::ControlFlow;
/// A macro call, like `vec![1, 2, 3]`.
///
/// Use `tcx.item_name(macro_call.def_id)` to get the macro name.
/// Even better is to check if it is a diagnostic item.
///
/// This structure is similar to `ExpnData` but it precludes desugaring expansions.
#[derive(Debug)]
pub struct MacroCall {
/// Macro `DefId`
pub def_id: DefId,
/// Kind of macro
pub kind: MacroKind,
/// The expansion produced by the macro call
pub expn: ExpnId,
/// Span of the macro call site
pub span: Span,
}
impl MacroCall {
pub fn is_local(&self) -> bool {
span_is_local(self.span)
}
}
/// Returns an iterator of expansions that created the given span
pub fn expn_backtrace(mut span: Span) -> impl Iterator<Item = (ExpnId, ExpnData)> {
std::iter::from_fn(move || {
let ctxt = span.ctxt();
if ctxt == SyntaxContext::root() {
return None;
}
let expn = ctxt.outer_expn();
let data = expn.expn_data();
span = data.call_site;
Some((expn, data))
})
}
/// Checks whether the span is from the root expansion or a locally defined macro
pub fn span_is_local(span: Span) -> bool {
!span.from_expansion() || expn_is_local(span.ctxt().outer_expn())
}
/// Checks whether the expansion is the root expansion or a locally defined macro
pub fn expn_is_local(expn: ExpnId) -> bool {
if expn == ExpnId::root() {
return true;
}
let data = expn.expn_data();
let backtrace = expn_backtrace(data.call_site);
std::iter::once((expn, data))
.chain(backtrace)
.find_map(|(_, data)| data.macro_def_id)
.map_or(true, DefId::is_local)
}
/// Returns an iterator of macro expansions that created the given span.
/// Note that desugaring expansions are skipped.
pub fn macro_backtrace(span: Span) -> impl Iterator<Item = MacroCall> {
expn_backtrace(span).filter_map(|(expn, data)| match data {
ExpnData {
kind: ExpnKind::Macro(kind, _),
macro_def_id: Some(def_id),
call_site: span,
..
} => Some(MacroCall {
def_id,
kind,
expn,
span,
}),
_ => None,
})
}
/// If the macro backtrace of `span` has a macro call at the root expansion
/// (i.e. not a nested macro call), returns `Some` with the `MacroCall`
pub fn root_macro_call(span: Span) -> Option<MacroCall> {
macro_backtrace(span).last()
}
/// Like [`root_macro_call`], but only returns `Some` if `node` is the "first node"
/// produced by the macro call, as in [`first_node_in_macro`].
pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) -> Option<MacroCall> {
if first_node_in_macro(cx, node) != Some(ExpnId::root()) {
return None;
}
root_macro_call(node.span())
}
/// Like [`macro_backtrace`], but only returns macro calls where `node` is the "first node" of the
/// macro call, as in [`first_node_in_macro`].
pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) -> impl Iterator<Item = MacroCall> {
let span = node.span();
first_node_in_macro(cx, node)
.into_iter()
.flat_map(move |expn| macro_backtrace(span).take_while(move |macro_call| macro_call.expn != expn))
}
/// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the
/// macro call site (i.e. the parent of the macro expansion). This generally means that `node`
/// is the outermost node of an entire macro expansion, but there are some caveats noted below.
/// This is useful for finding macro calls while visiting the HIR without processing the macro call
/// at every node within its expansion.
///
/// If you already have immediate access to the parent node, it is simpler to
/// just check the context of that span directly (e.g. `parent.span.from_expansion()`).
///
/// If a macro call is in statement position, it expands to one or more statements.
/// In that case, each statement *and* their immediate descendants will all yield `Some`
/// with the `ExpnId` of the containing block.
///
/// A node may be the "first node" of multiple macro calls in a macro backtrace.
/// The expansion of the outermost macro call site is returned in such cases.
pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<ExpnId> {
// get the macro expansion or return `None` if not found
// `macro_backtrace` importantly ignores desugaring expansions
let expn = macro_backtrace(node.span()).next()?.expn;
// get the parent node, possibly skipping over a statement
// if the parent is not found, it is sensible to return `Some(root)`
let hir = cx.tcx.hir();
let mut parent_iter = hir.parent_iter(node.hir_id());
let (parent_id, _) = match parent_iter.next() {
None => return Some(ExpnId::root()),
Some((_, Node::Stmt(_))) => match parent_iter.next() {
None => return Some(ExpnId::root()),
Some(next) => next,
},
Some(next) => next,
};
// get the macro expansion of the parent node
let parent_span = hir.span(parent_id);
let Some(parent_macro_call) = macro_backtrace(parent_span).next() else {
// the parent node is not in a macro
return Some(ExpnId::root());
};
if parent_macro_call.expn.is_descendant_of(expn) {
// `node` is input to a macro call
return None;
}
Some(parent_macro_call.expn)
}
/* Specific Macro Utils */
/// Is `def_id` of `std::panic`, `core::panic` or any inner implementation macros
pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool {
let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
matches!(
name.as_str(),
"core_panic_macro"
| "std_panic_macro"
| "core_panic_2015_macro"
| "std_panic_2015_macro"
| "core_panic_2021_macro"
)
}
pub enum PanicExpn<'a> {
/// No arguments - `panic!()`
Empty,
/// A string literal or any `&str` - `panic!("message")` or `panic!(message)`
Str(&'a Expr<'a>),
/// A single argument that implements `Display` - `panic!("{}", object)`
Display(&'a Expr<'a>),
/// Anything else - `panic!("error {}: {}", a, b)`
Format(FormatArgsExpn<'a>),
}
impl<'a> PanicExpn<'a> {
pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) {
return None;
}
let ExprKind::Call(callee, [arg]) = &expr.kind else { return None };
let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
let result = match path.segments.last().unwrap().ident.as_str() {
"panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
"panic" | "panic_str" => Self::Str(arg),
"panic_display" => {
let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None };
Self::Display(e)
},
"panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
_ => return None,
};
Some(result)
}
}
/// Finds the arguments of an `assert!` or `debug_assert!` macro call within the macro expansion
pub fn find_assert_args<'a>(
cx: &LateContext<'_>,
expr: &'a Expr<'a>,
expn: ExpnId,
) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p))
}
/// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
/// expansion
pub fn find_assert_eq_args<'a>(
cx: &LateContext<'_>,
expr: &'a Expr<'a>,
expn: ExpnId,
) -> Option<(&'a Expr<'a>, &'a Expr<'a>, PanicExpn<'a>)> {
find_assert_args_inner(cx, expr, expn).map(|([a, b], p)| (a, b, p))
}
fn find_assert_args_inner<'a, const N: usize>(
cx: &LateContext<'_>,
expr: &'a Expr<'a>,
expn: ExpnId,
) -> Option<([&'a Expr<'a>; N], PanicExpn<'a>)> {
let macro_id = expn.expn_data().macro_def_id?;
let (expr, expn) = match cx.tcx.item_name(macro_id).as_str().strip_prefix("debug_") {
None => (expr, expn),
Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
};
let mut args = ArrayVec::new();
let mut panic_expn = None;
expr_visitor_no_bodies(|e| {
if args.is_full() {
if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
panic_expn = PanicExpn::parse(cx, e);
}
panic_expn.is_none()
} else if is_assert_arg(cx, e, expn) {
args.push(e);
false
} else {
true
}
})
.visit_expr(expr);
let args = args.into_inner().ok()?;
// if no `panic!(..)` is found, use `PanicExpn::Empty`
// to indicate that the default assertion message is used
let panic_expn = panic_expn.unwrap_or(PanicExpn::Empty);
Some((args, panic_expn))
}
fn find_assert_within_debug_assert<'a>(
cx: &LateContext<'_>,
expr: &'a Expr<'a>,
expn: ExpnId,
assert_name: Symbol,
) -> Option<(&'a Expr<'a>, ExpnId)> {
let mut found = None;
expr_visitor_no_bodies(|e| {
if found.is_some() || !e.span.from_expansion() {
return false;
}
let e_expn = e.span.ctxt().outer_expn();
if e_expn == expn {
return true;
}
if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) {
found = Some((e, e_expn));
}
false
})
.visit_expr(expr);
found
}
fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool {
if !expr.span.from_expansion() {
return true;
}
let result = macro_backtrace(expr.span).try_for_each(|macro_call| {
if macro_call.expn == assert_expn {
ControlFlow::Break(false)
} else {
match cx.tcx.item_name(macro_call.def_id) {
// `cfg!(debug_assertions)` in `debug_assert!`
sym::cfg => ControlFlow::CONTINUE,
// assert!(other_macro!(..))
_ => ControlFlow::Break(true),
}
}
});
match result {
ControlFlow::Break(is_assert_arg) => is_assert_arg,
ControlFlow::Continue(()) => true,
}
}
/// A parsed `format_args!` expansion
pub struct FormatArgsExpn<'tcx> {
/// Span of the first argument, the format string
pub format_string_span: Span,
/// The format string split by formatted args like `{..}`
pub format_string_parts: Vec<Symbol>,
/// Values passed after the format string
pub value_args: Vec<&'tcx Expr<'tcx>>,
/// Each element is a `value_args` index and a formatting trait (e.g. `sym::Debug`)
pub formatters: Vec<(usize, Symbol)>,
/// List of `fmt::v1::Argument { .. }` expressions. If this is empty,
/// then `formatters` represents the format args (`{..}`).
/// If this is non-empty, it represents the format args, and the `position`
/// parameters within the struct expressions are indexes of `formatters`.
pub specs: Vec<&'tcx Expr<'tcx>>,
}
impl<'tcx> FormatArgsExpn<'tcx> {
/// Parses an expanded `format_args!` or `format_args_nl!` invocation
pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
macro_backtrace(expr.span).find(|macro_call| {
matches!(
cx.tcx.item_name(macro_call.def_id),
sym::const_format_args | sym::format_args | sym::format_args_nl
)
})?;
let mut format_string_span: Option<Span> = None;
let mut format_string_parts: Vec<Symbol> = Vec::new();
let mut value_args: Vec<&Expr<'_>> = Vec::new();
let mut formatters: Vec<(usize, Symbol)> = Vec::new();
let mut specs: Vec<&Expr<'_>> = Vec::new();
expr_visitor_no_bodies(|e| {
// if we're still inside of the macro definition...
if e.span.ctxt() == expr.span.ctxt() {
// ArgumnetV1::new(<value>, <format_trait>::fmt)
if_chain! {
if let ExprKind::Call(callee, [val, fmt_path]) = e.kind;
if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind;
if seg.ident.name == sym::new;
if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
if path.segments.last().unwrap().ident.name == sym::ArgumentV1;
if let ExprKind::Path(QPath::Resolved(_, path)) = fmt_path.kind;
if let [.., fmt_trait, _fmt] = path.segments;
then {
let val_idx = if_chain! {
if val.span.ctxt() == expr.span.ctxt();
if let ExprKind::Field(_, field) = val.kind;
if let Ok(idx) = field.name.as_str().parse();
then {
// tuple index
idx
} else {
// assume the value expression is passed directly
formatters.len()
}
};
formatters.push((val_idx, fmt_trait.ident.name));
}
}
if let ExprKind::Struct(QPath::Resolved(_, path), ..) = e.kind {
if path.segments.last().unwrap().ident.name == sym::Argument {
specs.push(e);
}
}
// walk through the macro expansion
return true;
}
// assume that the first expr with a differing context represents
// (and has the span of) the format string
if format_string_span.is_none() {
format_string_span = Some(e.span);
let span = e.span;
// walk the expr and collect string literals which are format string parts
expr_visitor_no_bodies(|e| {
if e.span.ctxt() != span.ctxt() {
// defensive check, probably doesn't happen
return false;
}
if let ExprKind::Lit(lit) = &e.kind {
if let LitKind::Str(symbol, _s) = lit.node {
format_string_parts.push(symbol);
}
}
true
})
.visit_expr(e);
} else {
// assume that any further exprs with a differing context are value args
value_args.push(e);
}
// don't walk anything not from the macro expansion (e.a. inputs)
false
})
.visit_expr(expr);
Some(FormatArgsExpn {
format_string_span: format_string_span?,
format_string_parts,
value_args,
formatters,
specs,
})
}
/// Finds a nested call to `format_args!` within a `format!`-like macro call
pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
let mut format_args = None;
expr_visitor_no_bodies(|e| {
if format_args.is_some() {
return false;
}
let e_ctxt = e.span.ctxt();
if e_ctxt == expr.span.ctxt() {
return true;
}
if e_ctxt.outer_expn().is_descendant_of(expn_id) {
format_args = FormatArgsExpn::parse(cx, e);
}
false
})
.visit_expr(expr);
format_args
}
/// Returns a vector of `FormatArgsArg`.
pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
if self.specs.is_empty() {
let args = std::iter::zip(&self.value_args, &self.formatters)
.map(|(value, &(_, format_trait))| FormatArgsArg {
value,
format_trait,
spec: None,
})
.collect();
return Some(args);
}
self.specs
.iter()
.map(|spec| {
if_chain! {
// struct `core::fmt::rt::v1::Argument`
if let ExprKind::Struct(_, fields, _) = spec.kind;
if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position);
if let ExprKind::Lit(lit) = &position_field.expr.kind;
if let LitKind::Int(position, _) = lit.node;
if let Ok(i) = usize::try_from(position);
if let Some(&(j, format_trait)) = self.formatters.get(i);
then {
Some(FormatArgsArg { value: self.value_args[j], format_trait, spec: Some(spec) })
} else {
None
}
}
})
.collect()
}
/// Span of all inputs
pub fn inputs_span(&self) -> Span {
match *self.value_args {
[] => self.format_string_span,
[.., last] => self.format_string_span.to(last.span),
}
}
}
/// Type representing a `FormatArgsExpn`'s format arguments
pub struct FormatArgsArg<'tcx> {
/// An element of `value_args` according to `position`
pub value: &'tcx Expr<'tcx>,
/// An element of `args` according to `position`
pub format_trait: Symbol,
/// An element of `specs`
pub spec: Option<&'tcx Expr<'tcx>>,
}
impl<'tcx> FormatArgsArg<'tcx> {
/// Returns true if any formatting parameters are used that would have an effect on strings,
/// like `{:+2}` instead of just `{}`.
pub fn has_string_formatting(&self) -> bool {
self.spec.map_or(false, |spec| {
// `!` because these conditions check that `self` is unformatted.
!if_chain! {
// struct `core::fmt::rt::v1::Argument`
if let ExprKind::Struct(_, fields, _) = spec.kind;
if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
// struct `core::fmt::rt::v1::FormatSpec`
if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind;
if subfields.iter().all(|field| match field.ident.name {
sym::precision | sym::width => match field.expr.kind {
ExprKind::Path(QPath::Resolved(_, path)) => {
path.segments.last().unwrap().ident.name == sym::Implied
}
_ => false,
}
_ => true,
});
then { true } else { false }
}
})
}
}
/// A node with a `HirId` and a `Span`
pub trait HirNode {
fn hir_id(&self) -> HirId;
fn span(&self) -> Span;
}
macro_rules! impl_hir_node {
($($t:ident),*) => {
$(impl HirNode for hir::$t<'_> {
fn hir_id(&self) -> HirId {
self.hir_id
}
fn span(&self) -> Span {
self.span
}
})*
};
}
impl_hir_node!(Expr, Pat);
impl HirNode for hir::Item<'_> {
fn hir_id(&self) -> HirId {
self.hir_id()
}
fn span(&self) -> Span {
self.span
}
}

View File

@@ -12,8 +12,9 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,53,0 { OR_PATTERNS }
1,53,0 { OR_PATTERNS, MANUAL_BITS }
1,52,0 { STR_SPLIT_ONCE }
1,51,0 { BORROW_AS_PTR }
1,50,0 { BOOL_THEN }
1,47,0 { TAU }
1,46,0 { CONST_IF_MATCH }

View File

@@ -5,16 +5,16 @@
//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
#[cfg(feature = "metadata-collector-lint")]
#[cfg(feature = "internal")]
pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
#[cfg(feature = "metadata-collector-lint")]
#[cfg(feature = "internal")]
pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
["rustc_lint_defs", "Applicability", "Unspecified"],
["rustc_lint_defs", "Applicability", "HasPlaceholders"],
["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
["rustc_lint_defs", "Applicability", "MachineApplicable"],
];
#[cfg(feature = "metadata-collector-lint")]
#[cfg(feature = "internal")]
pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
@@ -25,7 +25,6 @@ pub const ASSERT_MACRO: [&str; 4] = ["core", "macros", "builtin", "assert"];
pub const ASSERT_NE_MACRO: [&str; 3] = ["core", "macros", "assert_ne"];
pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
/// Preferably use the diagnostic item `sym::Borrow` where possible
pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
pub const BORROW_MUT_TRAIT: [&str; 3] = ["core", "borrow", "BorrowMut"];
@@ -46,7 +45,7 @@ pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
pub const EPRINT_MACRO: [&str; 3] = ["std", "macros", "eprint"];
@@ -64,13 +63,17 @@ pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "From
pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"];
pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
@@ -82,11 +85,11 @@ pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
#[cfg(feature = "internal")]
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
@@ -106,10 +109,6 @@ pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
pub(super) const PANICKING_PANIC: [&str; 3] = ["core", "panicking", "panic"];
pub(super) const PANICKING_PANIC_FMT: [&str; 3] = ["core", "panicking", "panic_fmt"];
pub(super) const PANICKING_PANIC_STR: [&str; 3] = ["core", "panicking", "panic_str"];
pub(super) const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
pub const PARKING_LOT_RAWMUTEX: [&str; 3] = ["parking_lot", "raw_mutex", "RawMutex"];
pub const PARKING_LOT_RAWRWLOCK: [&str; 3] = ["parking_lot", "raw_rwlock", "RawRwLock"];
pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"];
@@ -118,6 +117,7 @@ pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWri
pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
@@ -180,20 +180,24 @@ pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"];
#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"];
pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];

View File

@@ -19,7 +19,7 @@ use std::borrow::Cow;
type McfResult = Result<(), (Span, Cow<'static, str>)>;
pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
let def_id = body.source.def_id();
let mut current = def_id;
loop {
@@ -85,7 +85,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&Ru
Ok(())
}
fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
for arg in ty.walk(tcx) {
let ty = match arg.unpack() {
GenericArgKind::Type(ty) => ty,
@@ -133,7 +133,13 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
Ok(())
}
fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
fn check_rvalue<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
def_id: DefId,
rvalue: &Rvalue<'tcx>,
span: Span,
) -> McfResult {
match rvalue {
Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
@@ -210,7 +216,12 @@ fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rv
}
}
fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
fn check_statement<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
def_id: DefId,
statement: &Statement<'tcx>,
) -> McfResult {
let span = statement.source_info.span;
match &statement.kind {
StatementKind::Assign(box (place, rval)) => {
@@ -239,7 +250,7 @@ fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statemen
}
}
fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
match operand {
Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
Operand::Constant(c) => match c.check_static_ptr(tcx) {
@@ -249,7 +260,7 @@ fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &
}
}
fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
let mut cursor = place.projection.as_ref();
while let [ref proj_base @ .., elem] = *cursor {
cursor = proj_base;
@@ -274,7 +285,7 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t
Ok(())
}
fn check_terminator(
fn check_terminator<'a, 'tcx>(
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
terminator: &Terminator<'tcx>,

View File

@@ -281,7 +281,7 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>(
/// correctly get a snippet of `vec![]`.
///
/// This will also return whether or not the snippet is a macro call.
pub fn snippet_with_context(
pub fn snippet_with_context<'a>(
cx: &LateContext<'_>,
span: Span,
outer: SyntaxContext,

View File

@@ -461,7 +461,7 @@ impl Neg for Sugg<'_> {
}
}
impl Not for Sugg<'a> {
impl<'a> Not for Sugg<'a> {
type Output = Sugg<'a>;
fn not(self) -> Sugg<'a> {
use AssocOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual};
@@ -846,7 +846,7 @@ struct DerefDelegate<'a, 'tcx> {
applicability: Applicability,
}
impl DerefDelegate<'_, 'tcx> {
impl<'tcx> DerefDelegate<'_, 'tcx> {
/// build final suggestion:
/// - create the ending part of suggestion
/// - concatenate starting and ending parts

View File

@@ -25,7 +25,7 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
}
/// Checks whether a type can be partially moved.
pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
if has_drop(cx, ty) || is_copy(cx, ty) {
return false;
}
@@ -366,7 +366,7 @@ pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
/// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
/// otherwise returns `false`
pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
match (&a.kind(), &b.kind()) {
(&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => {
if did_a != did_b {

View File

@@ -173,7 +173,7 @@ pub trait Visitable<'tcx> {
}
macro_rules! visitable_ref {
($t:ident, $f:ident) => {
impl Visitable<'tcx> for &'tcx $t<'tcx> {
impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
visitor.$f(self);
}
@@ -217,7 +217,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
}
/// Checks if the given local is used.
pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
let mut is_used = false;
let mut visitor = expr_visitor(cx, |expr| {
if !is_used {
@@ -231,7 +231,7 @@ pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id
}
/// Checks if the given expression is a constant.
pub fn is_const_evaluatable(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
struct V<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
is_const: bool,
@@ -321,7 +321,7 @@ pub fn is_const_evaluatable(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
}
/// Checks if the given expression performs an unsafe operation outside of an unsafe block.
pub fn is_expr_unsafe(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
struct V<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
is_unsafe: bool,