diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2022-01-14 07:47:37 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-01-14 07:47:37 +0100 |
| commit | ccfee3d53b0d7b4855ad17702ca309ccf5eff3ac (patch) | |
| tree | 8576972fff0f11aac9434b9047522c45fc8cb51c /src/tools/clippy/clippy_utils | |
| parent | 1794b24bf9a899a5517ff1eb3667e7b03ae48d86 (diff) | |
| parent | b83c77cb7cc7f616df80cc7bd3e2a5f1eb9c0a9f (diff) | |
| download | rust-ccfee3d53b0d7b4855ad17702ca309ccf5eff3ac.tar.gz rust-ccfee3d53b0d7b4855ad17702ca309ccf5eff3ac.zip | |
Rollup merge of #92849 - flip1995:clippyup, r=Manishearth
Clippyup r? ```@Manishearth```
Diffstat (limited to 'src/tools/clippy/clippy_utils')
| -rw-r--r-- | src/tools/clippy/clippy_utils/Cargo.toml | 6 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/ast_utils.rs | 32 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/consts.rs | 2 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/diagnostics.rs | 2 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/eager_or_lazy.rs | 13 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/higher.rs | 295 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/hir_utils.rs | 24 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/lib.rs | 148 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/macros.rs | 539 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/msrvs.rs | 3 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/paths.rs | 44 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs | 25 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/source.rs | 2 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/sugg.rs | 4 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/ty.rs | 4 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/visitors.rs | 8 |
16 files changed, 692 insertions, 459 deletions
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 0ba0b59ed13..afff6491aba 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,17 +1,17 @@ [package] name = "clippy_utils" -version = "0.1.59" +version = "0.1.60" edition = "2021" publish = false [dependencies] +arrayvec = { version = "0.7", default-features = false } if_chain = "1.0" rustc-semver = "1.1" [features] deny-warnings = [] -internal-lints = [] -metadata-collector-lint = [] +internal = [] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 8400bfbbd99..3d3180521ab 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -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 -} diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 5c024612f8e..34c5af848a6 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -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<'_>, diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index d47b002ad7a..ca222c3d669 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -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, diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 61e529a6079..c3936ec95d4 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -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 } diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index c764c35d444..160a51740cd 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -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 { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index ac2b1a0259e..5a08a411dd1 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -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), } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 90f5baffd22..57183b58b2a 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -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) { diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs new file mode 100644 index 00000000000..b7a242cf90a --- /dev/null +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -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 + } +} diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 0cec7d6a5e4..a5b409ad96b 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -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 } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 6171823abbb..288c56e9fd7 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -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"]; diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 836558b07cb..8bdc9a9ea16 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -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>, diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index d928317259d..dbad607c58e 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -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, diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 92662c59226..87bc8232dde 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -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 diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 6d191d4a59b..72317447159 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -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 { diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 4bfd3c64b9c..b60cd4736f3 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -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, |
