Rollup merge of #92849 - flip1995:clippyup, r=Manishearth
Clippyup r? ```@Manishearth```
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<'_>,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
539
clippy_utils/src/macros.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
@@ -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"];
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user