From 1788cfd95d343dc59c2555ac895acb26cd5d3811 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 26 Sep 2021 17:38:05 +0100 Subject: [PATCH 01/72] Remove NullOp::Box --- clippy_utils/src/qualify_min_const_fn.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index e6d8ba3f02eb0..789418c743ff8 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -193,7 +193,6 @@ fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rv } }, Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => Ok(()), - Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); if ty.is_integral() || ty.is_bool() { From d5cbae90f9e3f323f18d9234b3e55b98c52fed52 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 23 Dec 2021 10:02:05 +0100 Subject: [PATCH 02/72] fix clippy --- clippy_lints/src/trailing_empty_array.rs | 1 + clippy_lints/src/utils/author.rs | 11 +++++-- clippy_lints/src/utils/inspector.rs | 9 ++++-- clippy_utils/src/hir_utils.rs | 39 ++++++++++++++++-------- clippy_utils/src/lib.rs | 7 +++-- tests/ui/author/repeat.stdout | 3 +- 6 files changed, 50 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs index 47c0a84cd4630..af36f7267004d 100644 --- a/clippy_lints/src/trailing_empty_array.rs +++ b/clippy_lints/src/trailing_empty_array.rs @@ -59,6 +59,7 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx if let ItemKind::Struct(data, _) = &item.kind; if let Some(last_field) = data.fields().last(); if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind; + if let rustc_hir::ArrayLen::Body(length) = length; // Then check if that that array zero-sized let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index c1b811c217440..9b06ca4e82493 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -6,7 +6,7 @@ use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_ast::LitIntType; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::{ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind}; +use rustc_hir::{ArrayLen, ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::{Ident, Symbol}; @@ -567,7 +567,14 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, value, length); kind!("Repeat({value}, {length})"); self.expr(value); - self.body(field!(length.body)); + match length.value { + ArrayLen::Infer(..) => out!("if let ArrayLen::Infer(..) = length;"), + ArrayLen::Body(anon_const) => { + bind!(self, anon_const); + out!("if let ArrayLen::Body({anon_const}) = {length};"); + self.body(field!(anon_const.body)); + } + } }, ExprKind::Err => kind!("Err"), ExprKind::DropTemps(expr) => { diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index abf4826a06917..c96766e56784f 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -334,12 +334,17 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}anon_const:", ind); print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); }, - hir::ExprKind::Repeat(val, ref anon_const) => { + hir::ExprKind::Repeat(val, length) => { println!("{}Repeat", ind); println!("{}value:", ind); print_expr(cx, val, indent + 1); println!("{}repeat count:", ind); - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); + match length { + hir::ArrayLen::Infer(_, _) => println!("{}repeat count: _", ind), + hir::ArrayLen::Body(anon_const) => { + print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1) + } + } }, hir::ExprKind::Err => { println!("{}Err", ind); diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index ad50759effaf8..ac2b1a0259e3e 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -8,7 +8,7 @@ use rustc_hir::HirIdMap; use rustc_hir::{ 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, + StmtKind, Ty, TyKind, TypeBinding, ArrayLen }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; @@ -170,6 +170,14 @@ 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, + } + } + pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool { let cx = self.inner.cx; let eval_const = |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value); @@ -194,8 +202,8 @@ impl HirEqInterExpr<'_, '_, '_> { } let is_eq = match ( - &reduce_exprkind(self.inner.cx, &left.kind), - &reduce_exprkind(self.inner.cx, &right.kind), + reduce_exprkind(self.inner.cx, &left.kind), + reduce_exprkind(self.inner.cx, &right.kind), ) { (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => { lb == rb && l_mut == r_mut && self.eq_expr(le, re) @@ -232,7 +240,7 @@ impl HirEqInterExpr<'_, '_, '_> { }, (&ExprKind::Index(la, li), &ExprKind::Index(ra, ri)) => self.eq_expr(la, ra) && self.eq_expr(li, ri), (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => { - self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r)) + self.eq_expr(lc, rc) && self.eq_expr(lt, rt) && both(le, re, |l, r| self.eq_expr(l, r)) }, (&ExprKind::Let(l), &ExprKind::Let(r)) => { self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) @@ -253,8 +261,8 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => { self.inner.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) }, - (&ExprKind::Repeat(le, ref ll_id), &ExprKind::Repeat(re, ref rl_id)) => { - self.eq_expr(le, re) && self.eq_body(ll_id.body, rl_id.body) + (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => { + self.eq_expr(le, re) && self.eq_array_length(ll, rl) }, (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)), (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r), @@ -391,8 +399,8 @@ impl HirEqInterExpr<'_, '_, '_> { 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, ref ll_id), &TyKind::Array(rt, ref rl_id)) => { - self.eq_ty(lt, rt) && self.eq_body(ll_id.body, rl_id.body) + (&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) @@ -714,9 +722,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { ExprKind::ConstBlock(ref l_id) => { self.hash_body(l_id.body); }, - ExprKind::Repeat(e, ref l_id) => { + ExprKind::Repeat(e, len) => { self.hash_expr(e); - self.hash_body(l_id.body); + self.hash_array_length(len); }, ExprKind::Ret(ref e) => { if let Some(e) = *e { @@ -906,9 +914,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { TyKind::Slice(ty) => { self.hash_ty(ty); }, - TyKind::Array(ty, anon_const) => { + &TyKind::Array(ty, len) => { self.hash_ty(ty); - self.hash_body(anon_const.body); + self.hash_array_length(len); }, TyKind::Ptr(ref mut_ty) => { self.hash_ty(mut_ty.ty); @@ -953,6 +961,13 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } + pub fn hash_array_length(&mut self, length: ArrayLen) { + match length { + ArrayLen::Infer(..) => {} + ArrayLen::Body(anon_const) => self.hash_body(anon_const.body), + } + } + pub fn hash_body(&mut self, body_id: BodyId) { // swap out TypeckResults when hashing a body let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id)); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7e054a54c3c02..be8558f36ca76 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -75,7 +75,7 @@ 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, + TraitItemKind, TraitRef, TyKind, UnOp, ArrayLen }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -675,8 +675,9 @@ 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, y) => if_chain! { - if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(y.body).value.kind; + ExprKind::Repeat(x, len) => if_chain! { + if let ArrayLen::Body(len) = len; + if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind; if let LitKind::Int(v, _) = const_lit.node; if v <= 32 && is_default_equivalent(cx, x); then { diff --git a/tests/ui/author/repeat.stdout b/tests/ui/author/repeat.stdout index f16350e4b5e6d..471bbce4f4185 100644 --- a/tests/ui/author/repeat.stdout +++ b/tests/ui/author/repeat.stdout @@ -2,7 +2,8 @@ if_chain! { if let ExprKind::Repeat(value, length) = expr.kind; if let ExprKind::Lit(ref lit) = value.kind; if let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node; - let expr1 = &cx.tcx.hir().body(length.body).value; + if let ArrayLen::Body(anon_const) = length; + let expr1 = &cx.tcx.hir().body(anon_const.body).value; if let ExprKind::Lit(ref lit1) = expr1.kind; if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node; then { From dfd32544faaba6a54238533b32ecf5cb7145cb35 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 28 Dec 2021 16:19:23 +0100 Subject: [PATCH 03/72] Update pulldown-cmark version in clippy --- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/doc.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 0661c2803864c..7d2a3e4f639c3 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -13,7 +13,7 @@ cargo_metadata = "0.14" clippy_utils = { path = "../clippy_utils" } if_chain = "1.0" itertools = "0.10" -pulldown-cmark = { version = "0.8", default-features = false } +pulldown-cmark = { version = "0.9", default-features = false } quine-mc_cluskey = "0.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 3650e4f91a001..7c2717733578b 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -542,16 +542,16 @@ fn check_doc<'a, Events: Iterator, Range in_link = Some(url), End(Link(..)) => in_link = None, - Start(Heading(_) | Paragraph | Item) => { - if let Start(Heading(_)) = event { + Start(Heading(_, _, _) | Paragraph | Item) => { + if let Start(Heading(_, _, _)) = event { in_heading = true; } ticks_unbalanced = false; let (_, span) = get_current_span(spans, range.start); paragraph_span = first_line_of_span(cx, span); }, - End(Heading(_) | Paragraph | Item) => { - if let End(Heading(_)) = event { + End(Heading(_, _, _) | Paragraph | Item) => { + if let End(Heading(_, _, _)) = event { in_heading = false; } if ticks_unbalanced { From eddf80698fb27165893c7ebfccd2d3228a959d81 Mon Sep 17 00:00:00 2001 From: ThePuzzlemaker Date: Tue, 28 Dec 2021 19:38:23 -0600 Subject: [PATCH 04/72] Suggest `return`ing tail expressions in async fn's --- .../rustc_typeck/src/check/fn_ctxt/suggestions.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 6c7d3a0c9c0bb..e8a0cc946b5e1 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -9,7 +9,7 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; use rustc_hir::{Expr, ExprKind, ItemKind, Node, Path, QPath, Stmt, StmtKind, TyKind}; -use rustc_infer::infer; +use rustc_infer::infer::{self, TyCtxtInferExt}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Binder, Ty}; use rustc_span::symbol::{kw, sym}; @@ -608,6 +608,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); let ty = self.normalize_associated_types_in(expr.span, ty); + let ty = match self.tcx.asyncness(fn_id.owner) { + hir::IsAsync::Async => self.tcx.infer_ctxt().enter(|infcx| { + infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| { + span_bug!( + fn_decl.output.span(), + "failed to get output type of async function" + ) + }) + }), + hir::IsAsync::NotAsync => ty, + }; if self.can_coerce(found, ty) { err.multipart_suggestion( "you might have meant to return this value", From ab7c4464920531559c4da0a9e9b8972ae2443ed1 Mon Sep 17 00:00:00 2001 From: ThePuzzlemaker Date: Tue, 28 Dec 2021 19:39:33 -0600 Subject: [PATCH 05/72] Add regression test for #92308 This amends off of an existing test introduced in #81769, if you think I should make a separate test I will. --- .../return/tail-expr-as-potential-return.rs | 22 +++++++++++++++++++ .../tail-expr-as-potential-return.stderr | 22 +++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/test/ui/return/tail-expr-as-potential-return.rs b/src/test/ui/return/tail-expr-as-potential-return.rs index 83266abfa06e6..2c3610fb24d4e 100644 --- a/src/test/ui/return/tail-expr-as-potential-return.rs +++ b/src/test/ui/return/tail-expr-as-potential-return.rs @@ -1,3 +1,16 @@ +// > Suggest `return`ing tail expressions that match return type +// > +// > Some newcomers are confused by the behavior of tail expressions, +// > interpreting that "leaving out the `;` makes it the return value". +// > To help them go in the right direction, suggest using `return` instead +// > when applicable. +// (original commit description for this test) +// +// This test was amended to also serve as a regression test for #92308, where +// this suggestion would not trigger with async functions. +// +// edition:2018 + fn main() { let _ = foo(true); } @@ -5,6 +18,15 @@ fn main() { fn foo(x: bool) -> Result { if x { Err(42) //~ ERROR mismatched types + //| HELP you might have meant to return this value + } + Ok(42.0) +} + +async fn bar(x: bool) -> Result { + if x { + Err(42) //~ ERROR mismatched types + //| HELP you might have meant to return this value } Ok(42.0) } diff --git a/src/test/ui/return/tail-expr-as-potential-return.stderr b/src/test/ui/return/tail-expr-as-potential-return.stderr index 87ef18878d6f1..dec1cbc4624ef 100644 --- a/src/test/ui/return/tail-expr-as-potential-return.stderr +++ b/src/test/ui/return/tail-expr-as-potential-return.stderr @@ -1,9 +1,10 @@ error[E0308]: mismatched types - --> $DIR/tail-expr-as-potential-return.rs:7:9 + --> $DIR/tail-expr-as-potential-return.rs:28:9 | LL | / if x { LL | | Err(42) | | ^^^^^^^ expected `()`, found enum `Result` +LL | | //| HELP you might have meant to return this value LL | | } | |_____- expected this to be `()` | @@ -14,6 +15,23 @@ help: you might have meant to return this value LL | return Err(42); | ++++++ + -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/tail-expr-as-potential-return.rs:20:9 + | +LL | / if x { +LL | | Err(42) + | | ^^^^^^^ expected `()`, found enum `Result` +LL | | //| HELP you might have meant to return this value +LL | | } + | |_____- expected this to be `()` + | + = note: expected unit type `()` + found enum `Result<_, {integer}>` +help: you might have meant to return this value + | +LL | return Err(42); + | ++++++ + + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. From 828ddbe414d77d61292a72ffb9ade85cd6e72cda Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Wed, 29 Dec 2021 08:52:40 -0800 Subject: [PATCH 06/72] fix [`redundant_closure`] fp with `Arc` --- clippy_lints/src/eta_reduction.rs | 5 +++++ tests/ui/eta.fixed | 11 +++++++++++ tests/ui/eta.rs | 11 +++++++++++ 3 files changed, 27 insertions(+) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 5a4b424710440..b22515a39079a 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id}; use if_chain::if_chain; @@ -12,6 +13,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -113,6 +115,9 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { // A type param function ref like `T::f` is not 'static, however // it is if cast like `T::f as fn()`. This seems like a rustc bug. if !substs.types().any(|t| matches!(t.kind(), ty::Param(_))); + let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs(); + if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc); + if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc); then { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 1de79667f55fb..f938f71068844 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -248,3 +248,14 @@ mod type_param_bound { take(X::fun as fn()); } } + +// #8073 Don't replace closure with `Arc` or `Rc` +fn arc_fp() { + let rc = std::rc::Rc::new(|| 7); + let arc = std::sync::Arc::new(|n| n + 1); + let ref_arc = &std::sync::Arc::new(|_| 5); + + true.then(|| rc()); + (0..5).map(|n| arc(n)); + Some(4).map(|n| ref_arc(n)); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 86abd347baa78..075bbc74922f9 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -248,3 +248,14 @@ mod type_param_bound { take(X::fun as fn()); } } + +// #8073 Don't replace closure with `Arc` or `Rc` +fn arc_fp() { + let rc = std::rc::Rc::new(|| 7); + let arc = std::sync::Arc::new(|n| n + 1); + let ref_arc = &std::sync::Arc::new(|_| 5); + + true.then(|| rc()); + (0..5).map(|n| arc(n)); + Some(4).map(|n| ref_arc(n)); +} From 97ab44ca974544fdf5ef598b04d3794af513e4bf Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 30 Dec 2021 15:10:43 +0100 Subject: [PATCH 07/72] Merge commit '0eff589afc83e21a03a168497bbab6b4dfbb4ef6' into clippyup --- .github/workflows/clippy.yml | 4 + .github/workflows/clippy_bors.yml | 4 + CHANGELOG.md | 123 ++++++++- CONTRIBUTING.md | 8 +- README.md | 10 +- clippy_lints/src/approx_const.rs | 2 +- clippy_lints/src/attrs.rs | 6 +- clippy_lints/src/casts/unnecessary_cast.rs | 5 +- clippy_lints/src/enum_variants.rs | 128 ++++----- clippy_lints/src/float_literal.rs | 2 +- clippy_lints/src/floating_point_arithmetic.rs | 34 ++- clippy_lints/src/identity_op.rs | 15 +- clippy_lints/src/implicit_saturating_sub.rs | 6 +- clippy_lints/src/init_numbered_fields.rs | 80 ++++++ clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_style.rs | 1 + clippy_lints/src/lib.rs | 8 +- clippy_lints/src/loops/manual_memcpy.rs | 29 +- clippy_lints/src/macro_use.rs | 6 +- clippy_lints/src/match_str_case_mismatch.rs | 6 +- clippy_lints/src/methods/iter_skip_next.rs | 32 ++- clippy_lints/src/methods/mod.rs | 2 +- .../src/methods/unwrap_or_else_default.rs | 5 +- clippy_lints/src/methods/utils.rs | 2 +- clippy_lints/src/needless_bool.rs | 12 +- clippy_lints/src/neg_multiply.rs | 25 +- clippy_lints/src/non_expressive_names.rs | 8 +- clippy_lints/src/ranges.rs | 8 +- clippy_lints/src/return_self_not_must_use.rs | 11 +- clippy_lints/src/returns.rs | 14 +- clippy_lints/src/shadow.rs | 6 +- clippy_lints/src/tabs_in_doc_comments.rs | 2 +- .../src/undocumented_unsafe_blocks.rs | 6 +- clippy_lints/src/unsafe_removed_from_name.rs | 2 +- clippy_lints/src/unwrap.rs | 2 +- clippy_lints/src/upper_case_acronyms.rs | 2 +- clippy_lints/src/utils/internal_lints.rs | 20 +- clippy_utils/src/consts.rs | 10 +- clippy_utils/src/lib.rs | 123 +++++---- clippy_utils/src/str_utils.rs | 101 ++++++- clippy_utils/src/sugg.rs | 251 ++++++++++++------ rust-toolchain | 2 +- tests/ui/crashes/ice-7868.stderr | 2 +- tests/ui/enum_variants.rs | 6 + tests/ui/enum_variants.stderr | 38 ++- tests/ui/floating_point_rad.fixed | 5 + tests/ui/floating_point_rad.rs | 5 + tests/ui/floating_point_rad.stderr | 28 +- tests/ui/identity_op.rs | 15 ++ tests/ui/identity_op.stderr | 36 ++- tests/ui/iter_skip_next.fixed | 15 ++ tests/ui/iter_skip_next.rs | 15 ++ tests/ui/iter_skip_next.stderr | 28 +- tests/ui/iter_skip_next_unfixable.rs | 19 ++ tests/ui/iter_skip_next_unfixable.stderr | 39 +++ .../manual_memcpy/with_loop_counters.stderr | 2 +- tests/ui/needless_bool/fixable.fixed | 9 + tests/ui/needless_bool/fixable.rs | 33 +++ tests/ui/needless_bool/fixable.stderr | 86 +++++- tests/ui/needless_return.fixed | 15 +- tests/ui/needless_return.rs | 11 + tests/ui/needless_return.stderr | 58 ++-- tests/ui/neg_multiply.fixed | 45 ++++ tests/ui/neg_multiply.rs | 12 +- tests/ui/neg_multiply.stderr | 38 ++- tests/ui/numbered_fields.fixed | 33 +++ tests/ui/numbered_fields.rs | 41 +++ tests/ui/numbered_fields.stderr | 26 ++ tests/ui/return_self_not_must_use.rs | 27 +- tests/ui/shadow.rs | 2 + tests/ui/shadow.stderr | 18 +- tests/ui/short_circuit_statement.fixed | 2 +- tests/ui/short_circuit_statement.stderr | 2 +- tests/ui/undocumented_unsafe_blocks.rs | 38 +-- tests/ui/undocumented_unsafe_blocks.stderr | 30 +-- tests/ui/unwrap_or_else_default.fixed | 5 +- tests/ui/unwrap_or_else_default.rs | 3 + tests/ui/unwrap_or_else_default.stderr | 18 +- 80 files changed, 1493 insertions(+), 439 deletions(-) create mode 100644 clippy_lints/src/init_numbered_fields.rs create mode 100644 tests/ui/iter_skip_next_unfixable.rs create mode 100644 tests/ui/iter_skip_next_unfixable.stderr create mode 100644 tests/ui/neg_multiply.fixed create mode 100644 tests/ui/numbered_fields.fixed create mode 100644 tests/ui/numbered_fields.rs create mode 100644 tests/ui/numbered_fields.stderr diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 0339de77f3cec..3d8c39408a924 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -58,6 +58,10 @@ jobs: run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint working-directory: clippy_lints + - name: Test clippy_utils + run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + working-directory: clippy_utils + - name: Test rustc_tools_util run: cargo test --features deny-warnings working-directory: rustc_tools_util diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 1f4d666c7a92c..8b644aa28176b 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -121,6 +121,10 @@ jobs: run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint working-directory: clippy_lints + - name: Test clippy_utils + run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + working-directory: clippy_utils + - name: Test rustc_tools_util run: cargo test --features deny-warnings working-directory: rustc_tools_util diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b5279cda6ea3..27bac4718b6c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,127 @@ document. ## Unreleased / In Rust Nightly -[b7f3f7f...master](https://github.com/rust-lang/rust-clippy/compare/b7f3f7f...master) +[e181011...master](https://github.com/rust-lang/rust-clippy/compare/e181011...master) + +## Rust 1.58 (beta) + +Current beta, release 2022-01-13 + +[00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011) + +### New lints + +* [`transmute_num_to_bytes`] + [#7805](https://github.com/rust-lang/rust-clippy/pull/7805) +* [`match_str_case_mismatch`] + [#7806](https://github.com/rust-lang/rust-clippy/pull/7806) +* [`format_in_format_args`], [`to_string_in_format_args`] + [#7743](https://github.com/rust-lang/rust-clippy/pull/7743) +* [`uninit_vec`] + [#7682](https://github.com/rust-lang/rust-clippy/pull/7682) +* [`fn_to_numeric_cast_any`] + [#7705](https://github.com/rust-lang/rust-clippy/pull/7705) +* [`undocumented_unsafe_blocks`] + [#7748](https://github.com/rust-lang/rust-clippy/pull/7748) +* [`trailing_empty_array`] + [#7838](https://github.com/rust-lang/rust-clippy/pull/7838) +* [`string_slice`] + [#7878](https://github.com/rust-lang/rust-clippy/pull/7878) + +### Moves or deprecations of lints + +* Move [`non_send_fields_in_send_ty`] to `suspicious` + [#7874](https://github.com/rust-lang/rust-clippy/pull/7874) +* Move [`non_ascii_literal`] to `restriction` + [#7907](https://github.com/rust-lang/rust-clippy/pull/7907) + +### Changes that expand what code existing lints cover + +* [`question_mark`] now covers `Result` + [#7840](https://github.com/rust-lang/rust-clippy/pull/7840) +* Make [`useless_format`] recognize bare `format!("")` + [#7801](https://github.com/rust-lang/rust-clippy/pull/7801) +* Lint on underscored variables with no side effects in [`no_effect`] + [#7775](https://github.com/rust-lang/rust-clippy/pull/7775) +* Expand [`match_ref_pats`] to check for multiple reference patterns + [#7800](https://github.com/rust-lang/rust-clippy/pull/7800) + +### False positive fixes + +* Fix false positive of [`implicit_saturating_sub`] with `else` clause + [#7832](https://github.com/rust-lang/rust-clippy/pull/7832) +* Fix [`question_mark`] when there is call in conditional predicate + [#7860](https://github.com/rust-lang/rust-clippy/pull/7860) +* [`mut_mut`] no longer lints when type is defined in external macros + [#7795](https://github.com/rust-lang/rust-clippy/pull/7795) +* Avoid [`eq_op`] in test functions + [#7811](https://github.com/rust-lang/rust-clippy/pull/7811) +* [`cast_possible_truncation`] no longer lints when cast is coming from `signum` + method call [#7850](https://github.com/rust-lang/rust-clippy/pull/7850) +* [`match_str_case_mismatch`] no longer lints on uncased characters + [#7865](https://github.com/rust-lang/rust-clippy/pull/7865) +* [`ptr_arg`] no longer lints references to type aliases + [#7890](https://github.com/rust-lang/rust-clippy/pull/7890) +* [`missing_safety_doc`] now also accepts "implementation safety" headers + [#7856](https://github.com/rust-lang/rust-clippy/pull/7856) +* [`missing_safety_doc`] no longer lints if any parent has `#[doc(hidden)]` + attribute [#7849](https://github.com/rust-lang/rust-clippy/pull/7849) +* [`if_not_else`] now ignores else-if statements + [#7895](https://github.com/rust-lang/rust-clippy/pull/7895) +* Avoid linting [`cast_possible_truncation`] on bit-reducing operations + [#7819](https://github.com/rust-lang/rust-clippy/pull/7819) +* Avoid linting [`field_reassign_with_default`] when `Drop` and `Copy` are + involved [#7794](https://github.com/rust-lang/rust-clippy/pull/7794) +* [`unnecessary_sort_by`] now checks if argument implements `Ord` trait + [#7824](https://github.com/rust-lang/rust-clippy/pull/7824) +* Fix false positive in [`match_overlapping_arm`] + [#7847](https://github.com/rust-lang/rust-clippy/pull/7847) +* Prevent [`needless_lifetimes`] false positive in `async` function definition + [#7901](https://github.com/rust-lang/rust-clippy/pull/7901) + +### Suggestion fixes/improvements + +* Keep an initial `::` when [`doc_markdown`] suggests to use ticks + [#7916](https://github.com/rust-lang/rust-clippy/pull/7916) +* Add a machine applicable suggestion for the [`doc_markdown`] missing backticks + lint [#7904](https://github.com/rust-lang/rust-clippy/pull/7904) +* [`equatable_if_let`] no longer expands macros in the suggestion + [#7788](https://github.com/rust-lang/rust-clippy/pull/7788) +* Make [`shadow_reuse`] suggestion less verbose + [#7782](https://github.com/rust-lang/rust-clippy/pull/7782) + +### ICE fixes + +* Fix ICE in [`enum_variant_names`] + [#7873](https://github.com/rust-lang/rust-clippy/pull/7873) +* Fix ICE in [`undocumented_unsafe_blocks`] + [#7891](https://github.com/rust-lang/rust-clippy/pull/7891) + +### Documentation improvements + +* Fixed naive doc formatting for `#[must_use]` lints ([`must_use_unit`], + [`double_must_use`], [`must_use_candidate`], [`let_underscore_must_use`]) + [#7827](https://github.com/rust-lang/rust-clippy/pull/7827) +* Fix typo in example for [`match_result_ok`] + [#7815](https://github.com/rust-lang/rust-clippy/pull/7815) + +### Others + +* Allow giving reasons for [`disallowed_types`] + [#7791](https://github.com/rust-lang/rust-clippy/pull/7791) +* Fix [`manual_assert`] and [`match_wild_err_arm`] for `#![no_std]` and Rust + 2021. [#7851](https://github.com/rust-lang/rust-clippy/pull/7851) +* Fix regression in [`semicolon_if_nothing_returned`] on macros containing while + loops [#7789](https://github.com/rust-lang/rust-clippy/pull/7789) +* Added a new configuration `literal-suffix-style` to enforce a certain style + writing [`unseparated_literal_suffix`] + [#7726](https://github.com/rust-lang/rust-clippy/pull/7726) ## Rust 1.57 -Current beta, release 2021-12-02 +Current stable, released 2021-12-02 -[7bfc26e...b7f3f7f](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...b7f3f7f) +[7bfc26e...00e31fa](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...00e31fa) ### New Lints @@ -161,7 +275,7 @@ Current beta, release 2021-12-02 ## Rust 1.56 -Current stable, released 2021-10-21 +Released 2021-10-21 [74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e) @@ -2912,6 +3026,7 @@ Released 2018-09-13 [`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter [`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string [`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display +[`init_numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#init_numbered_fields [`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always [`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax [`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 97ff31b4bc5a3..fc663de8f792d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -118,7 +118,7 @@ which `IntelliJ Rust` will be able to understand. Run `cargo dev setup intellij --repo-path ` where `` is a path to the rustc repo you just cloned. The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to -Clippys `Cargo.toml`s and should allow `IntelliJ Rust` to understand most of the types that Clippy uses. +Clippy's `Cargo.toml`s and should allow `IntelliJ Rust` to understand most of the types that Clippy uses. Just make sure to remove the dependencies again before finally making a pull request! [rustc_repo]: https://github.com/rust-lang/rust/ @@ -126,8 +126,8 @@ Just make sure to remove the dependencies again before finally making a pull req ### Rust Analyzer As of [#6869][6869], [`rust-analyzer`][ra_homepage] can understand that Clippy uses compiler-internals -using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippys `Cargo.toml.` -You will required a `nightly` toolchain with the `rustc-dev` component installed. +using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippy's `Cargo.toml.` +You will require a `nightly` toolchain with the `rustc-dev` component installed. Make sure that in the `rust-analyzer` configuration, you set ``` { "rust-analyzer.rustcSource": "discover" } @@ -228,7 +228,7 @@ about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree]. ### Patching git-subtree to work with big repos -Currently there's a bug in `git-subtree` that prevents it from working properly +Currently, there's a bug in `git-subtree` that prevents it from working properly with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stale. Before continuing with the following steps, we need to manually apply that fix to our local copy of `git-subtree`. diff --git a/README.md b/README.md index 73b0167d363f6..1bbd89e7822e8 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ line. (You can swap `clippy::all` with the specific lint category you are target ## Configuration Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable = -value` mapping eg. +value` mapping e.g. ```toml avoid-breaking-exported-api = false @@ -155,6 +155,10 @@ cognitive-complexity-threshold = 30 See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which lints can be configured and the meaning of the variables. +Note that configuration changes will not apply for code that has already been compiled and cached under `./target/`; +for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure that +any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch. + To deactivate the “for further information visit *lint-link*” message you can define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. @@ -193,7 +197,7 @@ And to warn on `lint_name`, run cargo clippy -- -W clippy::lint_name ``` -This also works with lint groups. For example you +This also works with lint groups. For example, you can run Clippy with warnings for all lints enabled: ```terminal cargo clippy -- -W clippy::pedantic @@ -228,7 +232,7 @@ fn main() { You can also omit the patch version when specifying the MSRV, so `msrv = 1.30` is equivalent to `msrv = 1.30.0`. -Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly. +Note: `custom_inner_attributes` is an unstable feature, so it has to be enabled explicitly. Lints that recognize this configuration option can be found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv) diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 12435eefbc4ee..5061c9d1eaf6f 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -87,7 +87,7 @@ impl ApproxConstant { let s = s.as_str(); if s.parse::().is_ok() { for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { - if is_approx_const(constant, &s, min_digits) + if is_approx_const(constant, s, min_digits) && msrv.as_ref().map_or(true, |msrv| meets_msrv(self.msrv.as_ref(), msrv)) { span_lint_and_help( diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 489945b513da4..0629674307ba7 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -310,8 +310,10 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { || is_word(lint, sym::deprecated) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) - || extract_clippy_lint(lint).map_or(false, |s| s.as_str() == "wildcard_imports") - || extract_clippy_lint(lint).map_or(false, |s| s.as_str() == "enum_glob_use") + || extract_clippy_lint(lint) + .map_or(false, |s| s.as_str() == "wildcard_imports") + || extract_clippy_lint(lint) + .map_or(false, |s| s.as_str() == "enum_glob_use") { return; } diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 9ed359922fd4d..1915d990c126c 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -49,8 +49,9 @@ pub(super) fn check( if cast_from.kind() == cast_to.kind() => { if let Some(src) = snippet_opt(cx, lit.span) { - let num_lit = NumericLiteral::from_lit_kind(&src, &lit.node).unwrap(); - lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { + lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + } } }, _ => { diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 689ac6184bffb..4f89e56743068 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::is_present_in_source; -use clippy_utils::str_utils::{self, count_match_end, count_match_start}; -use rustc_hir::{EnumDef, Item, ItemKind}; +use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start}; +use rustc_hir::{EnumDef, Item, ItemKind, Variant}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -18,6 +18,12 @@ declare_clippy_lint! { /// Enumeration variant names should specify their variant, /// not repeat the enumeration name. /// + /// ### Limitations + /// Characters with no casing will be considered when comparing prefixes/suffixes + /// This applies to numbers and non-ascii characters without casing + /// e.g. `Foo1` and `Foo2` is considered to have different prefixes + /// (the prefixes are `Foo1` and `Foo2` respectively), as also `Bar螃`, `Bar蟹` + /// /// ### Example /// ```rust /// enum Cake { @@ -120,72 +126,73 @@ impl_lint_pass!(EnumVariantNames => [ MODULE_INCEPTION ]); -fn check_variant( - cx: &LateContext<'_>, - threshold: u64, - def: &EnumDef<'_>, - item_name: &str, - item_name_chars: usize, - span: Span, -) { +fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) { + let name = variant.ident.name.as_str(); + let item_name_chars = item_name.chars().count(); + + if count_match_start(item_name, name).char_count == item_name_chars + && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase()) + && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric()) + { + span_lint( + cx, + ENUM_VARIANT_NAMES, + variant.span, + "variant name starts with the enum's name", + ); + } +} + +fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) { + let name = variant.ident.name.as_str(); + let item_name_chars = item_name.chars().count(); + + if count_match_end(item_name, name).char_count == item_name_chars { + span_lint( + cx, + ENUM_VARIANT_NAMES, + variant.span, + "variant name ends with the enum's name", + ); + } +} + +fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_name: &str, span: Span) { if (def.variants.len() as u64) < threshold { return; } + + let first = &def.variants[0].ident.name.as_str(); + let mut pre = camel_case_split(first); + let mut post = pre.clone(); + post.reverse(); for var in def.variants { - let name = var.ident.name.as_str(); - if count_match_start(item_name, &name).char_count == item_name_chars - && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase()) - && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric()) - { - span_lint( - cx, - ENUM_VARIANT_NAMES, - var.span, - "variant name starts with the enum's name", - ); - } - if count_match_end(item_name, &name).char_count == item_name_chars { - span_lint( - cx, - ENUM_VARIANT_NAMES, - var.span, - "variant name ends with the enum's name", - ); - } - } - let first = def.variants[0].ident.name.as_str(); - let mut pre = &first[..str_utils::camel_case_until(&*first).byte_index]; - let mut post = &first[str_utils::camel_case_start(&*first).byte_index..]; - for var in def.variants { + check_enum_start(cx, item_name, var); + check_enum_end(cx, item_name, var); let name = var.ident.name.as_str(); - let pre_match = count_match_start(pre, &name).byte_count; - pre = &pre[..pre_match]; - let pre_camel = str_utils::camel_case_until(pre).byte_index; - pre = &pre[..pre_camel]; - while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() { - if next.is_numeric() { - return; - } - if next.is_lowercase() { - let last = pre.len() - last.len_utf8(); - let last_camel = str_utils::camel_case_until(&pre[..last]); - pre = &pre[..last_camel.byte_index]; - } else { - break; - } - } + let variant_split = camel_case_split(name); - let post_match = count_match_end(post, &name); - let post_end = post.len() - post_match.byte_count; - post = &post[post_end..]; - let post_camel = str_utils::camel_case_start(post); - post = &post[post_camel.byte_index..]; + pre = pre + .iter() + .zip(variant_split.iter()) + .take_while(|(a, b)| a == b) + .map(|e| *e.0) + .collect(); + post = post + .iter() + .zip(variant_split.iter().rev()) + .take_while(|(a, b)| a == b) + .map(|e| *e.0) + .collect(); } let (what, value) = match (pre.is_empty(), post.is_empty()) { (true, true) => return, - (false, _) => ("pre", pre), - (true, false) => ("post", post), + (false, _) => ("pre", pre.join("")), + (true, false) => { + post.reverse(); + ("post", post.join("")) + }, }; span_lint_and_help( cx, @@ -233,8 +240,7 @@ impl LateLintPass<'_> for EnumVariantNames { #[allow(clippy::similar_names)] fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { let item_name = item.ident.name.as_str(); - let item_name_chars = item_name.chars().count(); - let item_camel = to_camel_case(&item_name); + let item_camel = to_camel_case(item_name); if !item.span.from_expansion() && is_present_in_source(cx, item.span) { if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() { // constants don't have surrounding modules @@ -283,7 +289,7 @@ impl LateLintPass<'_> for EnumVariantNames { } if let ItemKind::Enum(ref def, _) = item.kind { if !(self.avoid_breaking_exported_api && cx.access_levels.is_exported(item.def_id)) { - check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span); + check_variant(cx, self.threshold, def, item_name, item.span); } } self.modules.push((item.ident.name, item_camel)); diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 6903073fbcd85..7a4397a7b7467 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { // If its within the 2 decimal digits of being out of precision we // check if the parsed representation is the same as the string // since we'll need the truncated string anyway. - let digits = count_digits(&sym_str); + let digits = count_digits(sym_str); let max = max_digits(fty); let type_suffix = match lit_float_ty { LitFloatType::Suffixed(ast::FloatTy::F32) => Some("f32"), diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index a70e58c9c3a5e..6dcbaf68dfdbc 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -356,7 +356,7 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if eq_expr_value(cx, lmul_lhs, lmul_rhs); if eq_expr_value(cx, rmul_lhs, rmul_rhs); then { - return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, ".."), Sugg::hir(cx, rmul_lhs, ".."))); + return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, "..").maybe_par(), Sugg::hir(cx, rmul_lhs, ".."))); } } @@ -379,7 +379,7 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), rargs_1); if Int(2) == lvalue && Int(2) == rvalue; then { - return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, ".."), Sugg::hir(cx, rargs_0, ".."))); + return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, "..").maybe_par(), Sugg::hir(cx, rargs_0, ".."))); } } } @@ -654,26 +654,52 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && (F32(180_f32) == lvalue || F64(180_f64) == lvalue) { + let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); + if_chain! { + if let ExprKind::Lit(ref literal) = mul_lhs.kind; + if let ast::LitKind::Float(ref value, float_type) = literal.node; + if float_type == ast::LitFloatType::Unsuffixed; + then { + if value.as_str().ends_with('.') { + proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); + } else { + proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); + } + } + } span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS, expr.span, "conversion to degrees can be done more accurately", "consider using", - format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..")), + proposal, Applicability::MachineApplicable, ); } else if (F32(180_f32) == rvalue || F64(180_f64) == rvalue) && (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue) { + let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..")); + if_chain! { + if let ExprKind::Lit(ref literal) = mul_lhs.kind; + if let ast::LitKind::Float(ref value, float_type) = literal.node; + if float_type == ast::LitFloatType::Unsuffixed; + then { + if value.as_str().ends_with('.') { + proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, "..")); + } else { + proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, "..")); + } + } + } span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS, expr.span, "conversion to radians can be done more accurately", "consider using", - format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..")), + proposal, Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index b4e7bbc767135..f824f20ca40a0 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -61,15 +61,18 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { } fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { - // `1 << 0` is a common pattern in bit manipulation code - cmp.node == BinOpKind::Shl - && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0)) - && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)) + // This lint applies to integers + !cx.typeck_results().expr_ty(left).peel_refs().is_integral() + || !cx.typeck_results().expr_ty(right).peel_refs().is_integral() + // `1 << 0` is a common pattern in bit manipulation code + || (cmp.node == BinOpKind::Shl + && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0)) + && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))) } fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { - if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e) { - let check = match *cx.typeck_results().expr_ty(e).kind() { + if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) { + let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), ty::Uint(uty) => clip(cx.tcx, !0, uty), _ => return, diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 26a196aab5972..6515975fbffdc 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if let LitKind::Int(0, _) = cond_lit.node { if cx.typeck_results().expr_ty(cond_left).is_signed() { } else { - print_lint_and_sugg(cx, &var_name, expr); + print_lint_and_sugg(cx, var_name, expr); }; } }, @@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok()); if int_ids.any(|int_id| int_id == impl_id); then { - print_lint_and_sugg(cx, &var_name, expr) + print_lint_and_sugg(cx, var_name, expr) } } }, @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok()); if int_ids.any(|int_id| int_id == impl_id); then { - print_lint_and_sugg(cx, &var_name, expr) + print_lint_and_sugg(cx, var_name, expr) } } }, diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs new file mode 100644 index 0000000000000..5fe6725b581dc --- /dev/null +++ b/clippy_lints/src/init_numbered_fields.rs @@ -0,0 +1,80 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use std::borrow::Cow; +use std::cmp::Reverse; +use std::collections::BinaryHeap; + +declare_clippy_lint! { + /// ### What it does + /// Checks for tuple structs initialized with field syntax. + /// It will however not lint if a base initializer is present. + /// The lint will also ignore code in macros. + /// + /// ### Why is this bad? + /// This may be confusing to the uninitiated and adds no + /// benefit as opposed to tuple initializers + /// + /// ### Example + /// ```rust + /// struct TupleStruct(u8, u16); + /// + /// let _ = TupleStruct { + /// 0: 1, + /// 1: 23, + /// }; + /// + /// // should be written as + /// let base = TupleStruct(1, 23); + /// + /// // This is OK however + /// let _ = TupleStruct { 0: 42, ..base }; + /// ``` + #[clippy::version = "1.59.0"] + pub INIT_NUMBERED_FIELDS, + style, + "numbered fields in tuple struct initializer" +} + +declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]); + +impl<'tcx> LateLintPass<'tcx> for NumberedFields { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Struct(path, fields, None) = e.kind { + if !fields.is_empty() + && !in_macro(e.span) + && fields + .iter() + .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) + { + let expr_spans = fields + .iter() + .map(|f| (Reverse(f.ident.as_str().parse::().unwrap()), f.expr.span)) + .collect::>(); + let mut appl = Applicability::MachineApplicable; + let snippet = format!( + "{}({})", + snippet_with_applicability(cx, path.span(), "..", &mut appl), + expr_spans + .into_iter_sorted() + .map(|(_, span)| snippet_with_applicability(cx, span, "..", &mut appl)) + .intersperse(Cow::Borrowed(", ")) + .collect::() + ); + span_lint_and_sugg( + cx, + INIT_NUMBERED_FIELDS, + e.span, + "used a field initializer for a tuple struct", + "try this instead", + snippet, + appl, + ); + } + } + } +} diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 09cd0d22d8b0f..20e6220ec7d3a 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -441,7 +441,7 @@ fn is_empty_string(expr: &Expr<'_>) -> bool { if let ExprKind::Lit(ref lit) = expr.kind { if let LitKind::Str(lit, _) = lit.node { let lit = lit.as_str(); - return lit == ""; + return lit.is_empty(); } } false diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 3d3999d4cc0d8..944411087e951 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -81,6 +81,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(infinite_iter::INFINITE_ITER), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), + LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), LintId::of(int_plus_one::INT_PLUS_ONE), LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 766c5ba1bcb0f..002122793f3b6 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -178,6 +178,7 @@ store.register_lints(&[ inherent_impl::MULTIPLE_INHERENT_IMPL, inherent_to_string::INHERENT_TO_STRING, inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY, + init_numbered_fields::INIT_NUMBERED_FIELDS, inline_fn_without_body::INLINE_FN_WITHOUT_BODY, int_plus_one::INT_PLUS_ONE, integer_division::INTEGER_DIVISION, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index ea87e7e7a7368..1a0b869d40adb 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -29,6 +29,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::RESULT_UNIT_ERR), LintId::of(inherent_to_string::INHERENT_TO_STRING), + LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(len_zero::COMPARISON_TO_EMPTY), LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(len_zero::LEN_ZERO), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d1c7956a7a5c8..d4687a1e28797 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,13 +1,15 @@ // error-pattern:cargo-clippy +#![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] +#![feature(control_flow_enum)] #![feature(drain_filter)] #![feature(in_band_lifetimes)] +#![feature(iter_intersperse)] +#![feature(let_else)] #![feature(once_cell)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] -#![feature(control_flow_enum)] -#![feature(let_else)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)] @@ -242,6 +244,7 @@ mod indexing_slicing; mod infinite_iter; mod inherent_impl; mod inherent_to_string; +mod init_numbered_fields; mod inline_fn_without_body; mod int_plus_one; mod integer_division; @@ -854,6 +857,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); + store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 6cda926853438..c62fa5e998bd4 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -12,6 +12,7 @@ use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Pat, PatKind, StmtKind} use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::sym; +use std::fmt::Display; use std::iter::Iterator; /// Checks for for loops that sequentially copy items from one slice-like @@ -108,7 +109,7 @@ fn build_manual_memcpy_suggestion<'tcx>( src: &IndexExpr<'_>, ) -> String { fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> { - if offset.as_str() == "0" { + if offset.to_string() == "0" { sugg::EMPTY.into() } else { offset @@ -123,7 +124,7 @@ fn build_manual_memcpy_suggestion<'tcx>( if let Some(arg) = len_args.get(0); if path_to_local(arg) == path_to_local(base); then { - if sugg.as_str() == end_str { + if sugg.to_string() == end_str { sugg::EMPTY.into() } else { sugg @@ -147,7 +148,7 @@ fn build_manual_memcpy_suggestion<'tcx>( print_offset(apply_offset(&start_str, &idx_expr.idx_offset)).into_sugg(), print_limit( end, - end_str.as_str(), + end_str.to_string().as_str(), idx_expr.base, apply_offset(&end_str, &idx_expr.idx_offset), ) @@ -159,7 +160,7 @@ fn build_manual_memcpy_suggestion<'tcx>( print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)).into_sugg(), print_limit( end, - end_str.as_str(), + end_str.to_string().as_str(), idx_expr.base, apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str, ) @@ -202,15 +203,13 @@ fn build_manual_memcpy_suggestion<'tcx>( #[derive(Clone)] struct MinifyingSugg<'a>(Sugg<'a>); -impl<'a> MinifyingSugg<'a> { - fn as_str(&self) -> &str { - // HACK: Don't sync to Clippy! Required because something with the `or_patterns` feature - // changed and this would now require parentheses. - match &self.0 { - Sugg::NonParen(s) | Sugg::MaybeParen(s) | Sugg::BinOp(_, s) => s.as_ref(), - } +impl Display for MinifyingSugg<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) } +} +impl<'a> MinifyingSugg<'a> { fn into_sugg(self) -> Sugg<'a> { self.0 } @@ -225,7 +224,7 @@ impl<'a> From> for MinifyingSugg<'a> { impl std::ops::Add for &MinifyingSugg<'static> { type Output = MinifyingSugg<'static>; fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { - match (self.as_str(), rhs.as_str()) { + match (self.to_string().as_str(), rhs.to_string().as_str()) { ("0", _) => rhs.clone(), (_, "0") => self.clone(), (_, _) => (&self.0 + &rhs.0).into(), @@ -236,7 +235,7 @@ impl std::ops::Add for &MinifyingSugg<'static> { impl std::ops::Sub for &MinifyingSugg<'static> { type Output = MinifyingSugg<'static>; fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { - match (self.as_str(), rhs.as_str()) { + match (self.to_string().as_str(), rhs.to_string().as_str()) { (_, "0") => self.clone(), ("0", _) => (-rhs.0.clone()).into(), (x, y) if x == y => sugg::ZERO.into(), @@ -248,7 +247,7 @@ impl std::ops::Sub for &MinifyingSugg<'static> { impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> { type Output = MinifyingSugg<'static>; fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { - match (self.as_str(), rhs.as_str()) { + match (self.to_string().as_str(), rhs.to_string().as_str()) { ("0", _) => rhs.clone(), (_, "0") => self, (_, _) => (self.0 + &rhs.0).into(), @@ -259,7 +258,7 @@ impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> { impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { type Output = MinifyingSugg<'static>; fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { - match (self.as_str(), rhs.as_str()) { + match (self.to_string().as_str(), rhs.to_string().as_str()) { (_, "0") => self, ("0", _) => (-rhs.0.clone()).into(), (x, y) if x == y => sugg::ZERO.into(), diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 5b22b64a370e2..50d80e6a1d224 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; use if_chain::if_chain; @@ -8,7 +9,6 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::hygiene::ExpnKind; use rustc_span::{edition::Edition, sym, Span}; declare_clippy_lint! { @@ -213,7 +213,3 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { } } } - -fn in_macro(span: Span) -> bool { - span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) -} diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/match_str_case_mismatch.rs index dbf103143d93c..2c0fc218ca07c 100644 --- a/clippy_lints/src/match_str_case_mismatch.rs +++ b/clippy_lints/src/match_str_case_mismatch.rs @@ -94,8 +94,8 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { match ex.kind { - ExprKind::MethodCall(segment, _, [receiver], _) - if self.case_altered(segment.ident.as_str(), receiver) => {}, + ExprKind::MethodCall(segment, _, [receiver], _) if self.case_altered(segment.ident.as_str(), receiver) => { + }, _ => walk_expr(self, ex), } } @@ -142,7 +142,7 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<( }) = arm.pat.kind; if let LitKind::Str(symbol, _) = lit.node; let input = symbol.as_str(); - if !case_check(&input); + if !case_check(input); then { return Some((lit.span, symbol)); } diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index e32594757d0ca..f5410c7fd7fc8 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -1,8 +1,10 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_method; +use clippy_utils::path_to_local; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::{BindingAnnotation, Node, PatKind}; use rustc_lint::LateContext; use rustc_span::sym; @@ -11,14 +13,34 @@ use super::ITER_SKIP_NEXT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { // lint if caller of skip is an Iterator if is_trait_method(cx, expr, sym::Iterator) { - span_lint_and_sugg( + let mut application = Applicability::MachineApplicable; + span_lint_and_then( cx, ITER_SKIP_NEXT, expr.span.trim_start(recv.span).unwrap(), "called `skip(..).next()` on an iterator", - "use `nth` instead", - format!(".nth({})", snippet(cx, arg.span, "..")), - Applicability::MachineApplicable, + |diag| { + if_chain! { + if let Some(id) = path_to_local(recv); + if let Node::Binding(pat) = cx.tcx.hir().get(id); + if let PatKind::Binding(ann, _, _, _) = pat.kind; + if ann != BindingAnnotation::Mutable; + then { + application = Applicability::Unspecified; + diag.span_help( + pat.span, + &format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), + ); + } + } + + diag.span_suggestion( + expr.span.trim_start(recv.span).unwrap(), + "use `nth` instead", + format!(".nth({})", snippet(cx, arg.span, "..")), + application, + ); + }, ); } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4934240abfc46..4e33b2ff14cde 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2112,7 +2112,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { { wrong_self_convention::check( cx, - &name, + name, self_ty, first_arg_ty, first_arg.pat.span, diff --git a/clippy_lints/src/methods/unwrap_or_else_default.rs b/clippy_lints/src/methods/unwrap_or_else_default.rs index 276467b1dfdbf..f3af281d6cacc 100644 --- a/clippy_lints/src/methods/unwrap_or_else_default.rs +++ b/clippy_lints/src/methods/unwrap_or_else_default.rs @@ -2,7 +2,8 @@ use super::UNWRAP_OR_ELSE_DEFAULT; use clippy_utils::{ - diagnostics::span_lint_and_sugg, is_trait_item, source::snippet_with_applicability, ty::is_type_diagnostic_item, + diagnostics::span_lint_and_sugg, is_default_equivalent_call, source::snippet_with_applicability, + ty::is_type_diagnostic_item, }; use rustc_errors::Applicability; use rustc_hir as hir; @@ -24,7 +25,7 @@ pub(super) fn check<'tcx>( if_chain! { if is_option || is_result; - if is_trait_item(cx, u_arg, sym::Default); + if is_default_equivalent_call(cx, u_arg); then { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index 11ad881ee7b95..24b44f819f419 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -57,7 +57,7 @@ pub(super) fn get_hint_if_single_char_arg( let string = r.as_str(); if string.chars().count() == 1; then { - let snip = snippet_with_applicability(cx, arg.span, &string, applicability); + let snip = snippet_with_applicability(cx, arg.span, string, applicability); let ch = if let ast::StrStyle::Raw(nhash) = style { let nhash = nhash as usize; // for raw string: r##"a"## diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index d391fbecf82e1..778d49cb4b6ed 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -187,14 +187,14 @@ impl<'tcx> LateLintPass<'tcx> for BoolComparison { BinOpKind::Eq => { let true_case = Some((|h| h, "equality checks against true are unnecessary")); let false_case = Some(( - |h: Sugg<'_>| !h, + |h: Sugg<'tcx>| !h, "equality checks against false can be replaced by a negation", )); check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); }, BinOpKind::Ne => { let true_case = Some(( - |h: Sugg<'_>| !h, + |h: Sugg<'tcx>| !h, "inequality checks against true can be replaced by a negation", )); let false_case = Some((|h| h, "inequality checks against false are unnecessary")); @@ -206,12 +206,12 @@ impl<'tcx> LateLintPass<'tcx> for BoolComparison { ignore_case, Some((|h| h, "greater than checks against false are unnecessary")), Some(( - |h: Sugg<'_>| !h, + |h: Sugg<'tcx>| !h, "less than comparison against true can be replaced by a negation", )), ignore_case, Some(( - |l: Sugg<'_>, r: Sugg<'_>| (!l).bit_and(&r), + |l: Sugg<'tcx>, r: Sugg<'tcx>| (!l).bit_and(&r), "order comparisons between booleans can be simplified", )), ), @@ -219,14 +219,14 @@ impl<'tcx> LateLintPass<'tcx> for BoolComparison { cx, e, Some(( - |h: Sugg<'_>| !h, + |h: Sugg<'tcx>| !h, "less than comparison against true can be replaced by a negation", )), ignore_case, ignore_case, Some((|h| h, "greater than checks against false are unnecessary")), Some(( - |l: Sugg<'_>, r: Sugg<'_>| l.bit_and(&(!r)), + |l: Sugg<'tcx>, r: Sugg<'tcx>| l.bit_and(&(!r)), "order comparisons between booleans can be simplified", )), ), diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index cb67fab174005..0d05c83ffe45e 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -1,6 +1,8 @@ use clippy_utils::consts::{self, Constant}; -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -18,12 +20,16 @@ declare_clippy_lint! { /// /// ### Example /// ```ignore - /// x * -1 + /// // Bad + /// let a = x * -1; + /// + /// // Good + /// let b = -x; /// ``` #[clippy::version = "pre 1.29.0"] pub NEG_MULTIPLY, style, - "multiplying integers with `-1`" + "multiplying integers by `-1`" } declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); @@ -49,8 +55,19 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { if let ExprKind::Lit(ref l) = lit.kind; if consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1); if cx.typeck_results().expr_ty(exp).is_integral(); + then { - span_lint(cx, NEG_MULTIPLY, span, "negation by multiplying with `-1`"); + let mut applicability = Applicability::MachineApplicable; + let suggestion = format!("-{}", snippet_with_applicability(cx, exp.span, "..", &mut applicability)); + span_lint_and_sugg( + cx, + NEG_MULTIPLY, + span, + "this multiplication by -1 can be written more succinctly", + "consider using", + suggestion, + applicability, + ); } } } diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 25fbcb3c6094b..39a37e3e378ed 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -218,20 +218,20 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { return; } for existing_name in &self.0.names { - if allowed_to_be_similar(&interned_name, existing_name.exemptions) { + if allowed_to_be_similar(interned_name, existing_name.exemptions) { continue; } match existing_name.len.cmp(&count) { Ordering::Greater => { if existing_name.len - count != 1 - || levenstein_not_1(&interned_name, existing_name.interned.as_str()) + || levenstein_not_1(interned_name, existing_name.interned.as_str()) { continue; } }, Ordering::Less => { if count - existing_name.len != 1 - || levenstein_not_1(existing_name.interned.as_str(), &interned_name) + || levenstein_not_1(existing_name.interned.as_str(), interned_name) { continue; } @@ -298,7 +298,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { return; } self.0.names.push(ExistingName { - exemptions: get_exemptions(&interned_name).unwrap_or(&[]), + exemptions: get_exemptions(interned_name).unwrap_or(&[]), interned: ident.name, span: ident.span, len: count, diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 52c060bc42c7a..c8cbfefb63d65 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -378,8 +378,8 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { span, "an inclusive range would be more readable", |diag| { - let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string()); - let end = Sugg::hir(cx, y, "y"); + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); + let end = Sugg::hir(cx, y, "y").maybe_par(); if let Some(is_wrapped) = &snippet_opt(cx, span) { if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') { diag.span_suggestion( @@ -415,8 +415,8 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "an exclusive range would be more readable", |diag| { - let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string()); - let end = Sugg::hir(cx, y, "y"); + let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); + let end = Sugg::hir(cx, y, "y").maybe_par(); diag.span_suggestion( expr.span, "use", diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 1118da6c8cb57..b57ec96bc7e6d 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -1,11 +1,12 @@ -use clippy_utils::{diagnostics::span_lint, must_use_attr, nth_arg, return_ty}; +use clippy_utils::ty::is_must_use_ty; +use clippy_utils::{diagnostics::span_lint, nth_arg, return_ty}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does @@ -50,9 +51,9 @@ fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalD if decl.implicit_self.has_implicit_self(); // We only show this warning for public exported methods. if cx.access_levels.is_exported(fn_def); + // We don't want to emit this lint if the `#[must_use]` attribute is already there. + if !cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::must_use)); if cx.tcx.visibility(fn_def.to_def_id()).is_public(); - // No need to warn if the attribute is already present. - if must_use_attr(cx.tcx.hir().attrs(hir_id)).is_none(); let ret_ty = return_ty(cx, hir_id); let self_arg = nth_arg(cx, hir_id, 0); // If `Self` has the same type as the returned type, then we want to warn. @@ -60,6 +61,8 @@ fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalD // For this check, we don't want to remove the reference on the returned type because if // there is one, we shouldn't emit a warning! if self_arg.peel_refs() == ret_ty; + // If `Self` is already marked as `#[must_use]`, no need for the attribute here. + if !is_must_use_ty(cx, ret_ty); then { span_lint( diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 494bc7dda18e7..112ccdcdd4202 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -73,6 +73,7 @@ declare_clippy_lint! { enum RetReplacement { Empty, Block, + Unit, } declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]); @@ -212,7 +213,7 @@ fn check_final_expr<'tcx>( // (except for unit type functions) so we don't match it ExprKind::Match(_, arms, MatchSource::Normal) => { for arm in arms.iter() { - check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block); + check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Unit); } }, ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty), @@ -259,6 +260,17 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "replace `return` with a unit value", + "()".to_string(), + Applicability::MachineApplicable, + ); + }, }, } } diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index f6880af0cab28..ce05c5a6164fd 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::hir_id::ItemLocalId; -use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, UnOp}; +use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, Symbol}; @@ -220,14 +220,14 @@ fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_ } } -/// Finds the "init" expression for a pattern: `let = ;` or +/// Finds the "init" expression for a pattern: `let = ;` (or `if let`) or /// `match { .., => .., .. }` fn find_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { for (_, node) in cx.tcx.hir().parent_iter(hir_id) { let init = match node { Node::Arm(_) | Node::Pat(_) => continue, Node::Expr(expr) => match expr.kind { - ExprKind::Match(e, _, _) => Some(e), + ExprKind::Match(e, _, _) | ExprKind::Let(&Let { init: e, .. }) => Some(e), _ => None, }, Node::Local(local) => local.init, diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index c9b4b245f4cc2..15543b6a26277 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -64,7 +64,7 @@ impl TabsInDocComments { if let ast::AttrKind::DocComment(_, comment) = attr.kind { let comment = comment.as_str(); - for (lo, hi) in get_chunks_of_tabs(&comment) { + for (lo, hi) in get_chunks_of_tabs(comment) { // +3 skips the opening delimiter let new_span = Span::new( attr.span.lo() + BytePos(3 + lo), diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index ccc49caf47c7a..3d3b4a6679dd1 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -15,7 +15,7 @@ use std::borrow::Cow; declare_clippy_lint! { /// ### What it does - /// Checks for `unsafe` blocks without a `// Safety: ` comment + /// Checks for `unsafe` blocks without a `// SAFETY: ` comment /// explaining why the unsafe operations performed inside /// the block are safe. /// @@ -36,7 +36,7 @@ declare_clippy_lint! { /// use std::ptr::NonNull; /// let a = &mut 42; /// - /// // Safety: references are guaranteed to be non-null. + /// // SAFETY: references are guaranteed to be non-null. /// let ptr = unsafe { NonNull::new_unchecked(a) }; /// ``` #[clippy::version = "1.58.0"] @@ -213,7 +213,7 @@ impl UndocumentedUnsafeBlocks { ); } else { let block_indent = indent_of(cx, span); - let suggestion = format!("// Safety: ...\n{}", snippet(cx, span, "..")); + let suggestion = format!("// SAFETY: ...\n{}", snippet(cx, span, "..")); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 44b1989dbc68a..64f7a055cd9bd 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -60,7 +60,7 @@ fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext<'_>, span: Span) { let old_str = old_name.name.as_str(); let new_str = new_name.name.as_str(); - if contains_unsafe(&old_str) && !contains_unsafe(&new_str) { + if contains_unsafe(old_str) && !contains_unsafe(new_str) { span_lint( cx, UNSAFE_REMOVED_FROM_NAME, diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 01a5691223bfc..918fa5f7dc124 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -161,7 +161,7 @@ fn collect_unwrap_info<'tcx>( if is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name); then { assert!(args.len() == 1); - let unwrappable = match name.as_ref() { + let unwrappable = match name { "is_some" | "is_ok" => true, "is_err" | "is_none" => false, _ => unreachable!(), diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index 0c62161e53d43..7286d0a7bf99b 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -87,7 +87,7 @@ fn check_ident(cx: &LateContext<'_>, ident: &Ident, be_aggressive: bool) { if (ident.chars().all(|c| c.is_ascii_uppercase()) && ident.len() > 2) // otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive // upper-case-acronyms-aggressive config option enabled - || (be_aggressive && ident != &corrected) + || (be_aggressive && ident != corrected) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 81e9c2e15c97a..e98dcd3cf983b 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -28,7 +28,7 @@ use rustc_middle::ty; use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Symbol, SymbolStr}; +use rustc_span::symbol::Symbol; use rustc_span::{sym, BytePos, Span}; use rustc_typeck::hir_ty_to_ty; @@ -344,11 +344,11 @@ impl EarlyLintPass for ClippyLintsInternal { if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { - let mut last_name: Option = None; + let mut last_name: Option<&str> = None; for item in items { let name = item.ident.as_str(); - if let Some(ref last_name) = last_name { - if **last_name > *name { + if let Some(last_name) = last_name { + if *last_name > *name { span_lint( cx, CLIPPY_LINTS_INTERNAL, @@ -608,8 +608,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { } let (method_names, arg_lists, spans) = method_calls(expr, 2); - let method_names: Vec = method_names.iter().map(|s| s.as_str()).collect(); - let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect(); + let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); if_chain! { if let ["expn_data", "outer_expn"] = method_names.as_slice(); let args = arg_lists[1]; @@ -839,7 +838,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]); // Extract the path to the matched type if let Some(segments) = path_to_matched_type(cx, ty_path); - let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect(); if let Some(ty_did) = path_to_res(cx, &segments[..]).opt_def_id(); // Check if the matched type is a diagnostic item if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did); @@ -862,7 +861,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { } } -fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { use rustc_hir::ItemKind; match &expr.kind { @@ -887,12 +886,12 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option {}, }, ExprKind::Array(exprs) => { - let segments: Vec = exprs + let segments: Vec = exprs .iter() .filter_map(|expr| { if let ExprKind::Lit(lit) = &expr.kind { if let LitKind::Str(sym, _) = lit.node { - return Some(sym.as_str()); + return Some(sym); } } @@ -1076,7 +1075,6 @@ impl InterningDefinedSymbol { &paths::SYMBOL_TO_IDENT_STRING, &paths::TO_STRING_METHOD, ]; - // SymbolStr might be de-referenced: `&*symbol.as_str()` let call = if_chain! { if let ExprKind::AddrOf(_, _, e) = expr.kind; if let ExprKind::Unary(UnOp::Deref, e) = e.kind; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index dc5ec5f229518..5c024612f8eba 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -168,6 +168,14 @@ impl Constant { None } } + + #[must_use] + pub fn peel_refs(mut self) -> Self { + while let Constant::Ref(r) = self { + self = *r; + } + self + } } /// Parses a `LitKind` to a `Constant`. @@ -320,7 +328,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let res = self.typeck_results.qpath_res(qpath, callee.hir_id); if let Some(def_id) = res.opt_def_id(); let def_path = self.lcx.get_def_path(def_id); - let def_path: Vec<&str> = def_path.iter().take(4).map(|s| s.as_str()).collect(); + let def_path: Vec<&str> = def_path.iter().take(4).map(Symbol::as_str).collect(); if let ["core", "num", int_impl, "max_value"] = *def_path; then { let value = match int_impl { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7e054a54c3c02..54d470ca73820 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1,8 +1,9 @@ #![feature(box_patterns)] +#![feature(control_flow_enum)] #![feature(in_band_lifetimes)] #![feature(let_else)] +#![feature(once_cell)] #![feature(rustc_private)] -#![feature(control_flow_enum)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] @@ -60,9 +61,12 @@ pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, Spanl use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; +use std::lazy::SyncOnceCell; +use std::sync::{Mutex, MutexGuard}; use if_chain::if_chain; use rustc_ast::ast::{self, Attribute, LitKind}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -87,6 +91,7 @@ use rustc_middle::ty::binding::BindingMode; use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture}; use rustc_semver::RustcVersion; use rustc_session::Session; +use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::sym; @@ -142,6 +147,13 @@ 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] @@ -156,18 +168,18 @@ pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool { /// instead. /// /// Examples: -/// ```ignore +/// ``` /// let abc = 1; /// // ^ output /// let def = abc; -/// dbg!(def) +/// dbg!(def); /// // ^^^ input /// /// // or... /// let abc = 1; /// let def = abc + 2; /// // ^^^^^^^ output -/// dbg!(def) +/// dbg!(def); /// // ^^^ input /// ``` pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { @@ -664,6 +676,22 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< false } +/// Return true if the expr is equal to `Default::default` when evaluated. +pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool { + if_chain! { + if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind; + if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); + if is_diag_trait_item(cx, repl_def_id, sym::Default) + || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); + then { + true + } + else { + false + } + } +} + /// Returns true if the expr is equal to `Default::default()` of it's type when evaluated. /// It doesn't cover all cases, for example indirect function calls (some of std /// functions are supported) but it is the best we have. @@ -686,18 +714,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { false } }, - ExprKind::Call(repl_func, _) => if_chain! { - if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; - if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - if is_diag_trait_item(cx, repl_def_id, sym::Default) - || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); - then { - true - } - else { - false - } - }, + ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func), ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), _ => false, @@ -1136,7 +1153,7 @@ pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec { /// Extends the span to the beginning of the spans line, incl. whitespaces. /// -/// ```rust,ignore +/// ```rust /// let x = (); /// // ^^ /// // will be converted to @@ -1337,7 +1354,7 @@ pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { cx.typeck_results().adjustments().get(e.hir_id).is_some() } -/// Returns the pre-expansion span if is this comes from an expansion of the +/// Returns the pre-expansion span if this comes from an expansion of the /// macro `name`. /// See also [`is_direct_expn_of`]. #[must_use] @@ -1364,7 +1381,8 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option { /// of the macro `name`. /// The difference with [`is_expn_of`] is that in /// ```rust -/// # macro_rules! foo { ($e:tt) => { $e } }; macro_rules! bar { ($e:expr) => { $e } } +/// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } } +/// # macro_rules! bar { ($e:expr) => { $e } } /// foo!(bar!(42)); /// ``` /// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only @@ -1905,7 +1923,9 @@ pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { /// Check if parent of a hir node is a trait implementation block. /// For example, `f` in -/// ```rust,ignore +/// ```rust +/// # struct S; +/// # trait Trait { fn f(); } /// impl Trait for S { /// fn f() {} /// } @@ -2124,17 +2144,16 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { false } -struct VisitConstTestStruct<'tcx> { +struct TestItemNamesVisitor<'tcx> { tcx: TyCtxt<'tcx>, names: Vec, - found: bool, } -impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'hir> { + +impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> { fn visit_item(&mut self, item: &Item<'_>) { if let ItemKind::Const(ty, _body) = item.kind { if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { // We could also check for the type name `test::TestDescAndFn` - // and the `#[rustc_test_marker]` attribute? if let Res::Def(DefKind::Struct, _) = path.res { let has_test_marker = self .tcx @@ -2142,8 +2161,8 @@ impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'hir> { .attrs(item.hir_id()) .iter() .any(|a| a.has_name(sym::rustc_test_marker)); - if has_test_marker && self.names.contains(&item.ident.name) { - self.found = true; + if has_test_marker { + self.names.push(item.ident.name); } } } @@ -2154,32 +2173,42 @@ impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'hir> { fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {} } +static TEST_ITEM_NAMES_CACHE: SyncOnceCell>>> = SyncOnceCell::new(); + +fn with_test_item_names(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>> = cache.lock().unwrap(); + match map.entry(module) { + Entry::Occupied(entry) => f(entry.get()), + Entry::Vacant(entry) => { + let mut visitor = TestItemNamesVisitor { tcx, names: Vec::new() }; + tcx.hir().visit_item_likes_in_module(module, &mut visitor); + visitor.names.sort_unstable(); + f(&*entry.insert(visitor.names)) + }, + } +} + /// Checks if the function containing the given `HirId` is a `#[test]` function /// /// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`. pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { - let names: Vec<_> = tcx - .hir() - .parent_iter(id) - // Since you can nest functions we need to collect all until we leave - // function scope - .filter_map(|(_id, node)| { - if let Node::Item(item) = node { - if let ItemKind::Fn(_, _, _) = item.kind { - return Some(item.ident.name); + with_test_item_names(tcx, tcx.parent_module(id), |names| { + tcx.hir() + .parent_iter(id) + // Since you can nest functions we need to collect all until we leave + // function scope + .any(|(_id, node)| { + if let Node::Item(item) = node { + if let ItemKind::Fn(_, _, _) = item.kind { + // Note that we have sorted the item names in the visitor, + // so the binary_search gets the same as `contains`, but faster. + return names.binary_search(&item.ident.name).is_ok(); + } } - } - None - }) - .collect(); - let parent_mod = tcx.parent_module(id); - let mut vis = VisitConstTestStruct { - tcx, - names, - found: false, - }; - tcx.hir().visit_item_likes_in_module(parent_mod, &mut vis); - vis.found + false + }) + }) } /// Checks whether item either has `test` attribute applied, or diff --git a/clippy_utils/src/str_utils.rs b/clippy_utils/src/str_utils.rs index cba96e05a2412..03a9d3c25fd98 100644 --- a/clippy_utils/src/str_utils.rs +++ b/clippy_utils/src/str_utils.rs @@ -15,6 +15,7 @@ impl StrIndex { /// Returns the index of the character after the first camel-case component of `s`. /// /// ``` +/// # use clippy_utils::str_utils::{camel_case_until, StrIndex}; /// assert_eq!(camel_case_until("AbcDef"), StrIndex::new(6, 6)); /// assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0)); /// assert_eq!(camel_case_until("AbcDD"), StrIndex::new(3, 3)); @@ -55,9 +56,10 @@ pub fn camel_case_until(s: &str) -> StrIndex { } } -/// Returns index of the last camel-case component of `s`. +/// Returns index of the first camel-case component of `s`. /// /// ``` +/// # use clippy_utils::str_utils::{camel_case_start, StrIndex}; /// assert_eq!(camel_case_start("AbcDef"), StrIndex::new(0, 0)); /// assert_eq!(camel_case_start("abcDef"), StrIndex::new(3, 3)); /// assert_eq!(camel_case_start("ABCD"), StrIndex::new(4, 4)); @@ -66,19 +68,37 @@ pub fn camel_case_until(s: &str) -> StrIndex { /// ``` #[must_use] pub fn camel_case_start(s: &str) -> StrIndex { + camel_case_start_from_idx(s, 0) +} + +/// Returns `StrIndex` of the last camel-case component of `s[idx..]`. +/// +/// ``` +/// # use clippy_utils::str_utils::{camel_case_start_from_idx, StrIndex}; +/// assert_eq!(camel_case_start_from_idx("AbcDef", 0), StrIndex::new(0, 0)); +/// assert_eq!(camel_case_start_from_idx("AbcDef", 1), StrIndex::new(3, 3)); +/// assert_eq!(camel_case_start_from_idx("AbcDefGhi", 0), StrIndex::new(0, 0)); +/// assert_eq!(camel_case_start_from_idx("AbcDefGhi", 1), StrIndex::new(3, 3)); +/// assert_eq!(camel_case_start_from_idx("Abcdefg", 1), StrIndex::new(7, 7)); +/// ``` +pub fn camel_case_start_from_idx(s: &str, start_idx: usize) -> StrIndex { let char_count = s.chars().count(); let range = 0..char_count; let mut iter = range.rev().zip(s.char_indices().rev()); - if let Some((char_index, (_, first))) = iter.next() { + if let Some((_, (_, first))) = iter.next() { if !first.is_lowercase() { - return StrIndex::new(char_index, s.len()); + return StrIndex::new(char_count, s.len()); } } else { return StrIndex::new(char_count, s.len()); } + let mut down = true; let mut last_index = StrIndex::new(char_count, s.len()); for (char_index, (byte_index, c)) in iter { + if byte_index < start_idx { + break; + } if down { if c.is_uppercase() { down = false; @@ -96,9 +116,55 @@ pub fn camel_case_start(s: &str) -> StrIndex { return last_index; } } + last_index } +/// Get the indexes of camel case components of a string `s` +/// +/// ``` +/// # use clippy_utils::str_utils::{camel_case_indices, StrIndex}; +/// assert_eq!( +/// camel_case_indices("AbcDef"), +/// vec![StrIndex::new(0, 0), StrIndex::new(3, 3), StrIndex::new(6, 6)] +/// ); +/// assert_eq!( +/// camel_case_indices("abcDef"), +/// vec![StrIndex::new(3, 3), StrIndex::new(6, 6)] +/// ); +/// ``` +pub fn camel_case_indices(s: &str) -> Vec { + let mut result = Vec::new(); + let mut str_idx = camel_case_start(s); + + while str_idx.byte_index < s.len() { + let next_idx = str_idx.byte_index + 1; + result.push(str_idx); + str_idx = camel_case_start_from_idx(s, next_idx); + } + result.push(str_idx); + + result +} + +/// Split camel case string into a vector of its components +/// +/// ``` +/// # use clippy_utils::str_utils::{camel_case_split, StrIndex}; +/// assert_eq!(camel_case_split("AbcDef"), vec!["Abc", "Def"]); +/// ``` +pub fn camel_case_split(s: &str) -> Vec<&str> { + let mut offsets = camel_case_indices(s) + .iter() + .map(|e| e.byte_index) + .collect::>(); + if offsets[0] != 0 { + offsets.insert(0, 0); + } + + offsets.windows(2).map(|w| &s[w[0]..w[1]]).collect() +} + /// Dealing with sting comparison can be complicated, this struct ensures that both the /// character and byte count are provided for correct indexing. #[derive(Debug, Default, PartialEq, Eq)] @@ -116,6 +182,7 @@ impl StrCount { /// Returns the number of chars that match from the start /// /// ``` +/// # use clippy_utils::str_utils::{count_match_start, StrCount}; /// assert_eq!(count_match_start("hello_mouse", "hello_penguin"), StrCount::new(6, 6)); /// assert_eq!(count_match_start("hello_clippy", "bye_bugs"), StrCount::new(0, 0)); /// assert_eq!(count_match_start("hello_world", "hello_world"), StrCount::new(11, 11)); @@ -141,6 +208,7 @@ pub fn count_match_start(str1: &str, str2: &str) -> StrCount { /// Returns the number of chars and bytes that match from the end /// /// ``` +/// # use clippy_utils::str_utils::{count_match_end, StrCount}; /// assert_eq!(count_match_end("hello_cat", "bye_cat"), StrCount::new(4, 4)); /// assert_eq!(count_match_end("if_item_thing", "enum_value"), StrCount::new(0, 0)); /// assert_eq!(count_match_end("Clippy", "Clippy"), StrCount::new(6, 6)); @@ -227,4 +295,31 @@ mod test { fn until_caps() { assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0)); } + + #[test] + fn camel_case_start_from_idx_full() { + assert_eq!(camel_case_start_from_idx("AbcDef", 0), StrIndex::new(0, 0)); + assert_eq!(camel_case_start_from_idx("AbcDef", 1), StrIndex::new(3, 3)); + assert_eq!(camel_case_start_from_idx("AbcDef", 4), StrIndex::new(6, 6)); + assert_eq!(camel_case_start_from_idx("AbcDefGhi", 0), StrIndex::new(0, 0)); + assert_eq!(camel_case_start_from_idx("AbcDefGhi", 1), StrIndex::new(3, 3)); + assert_eq!(camel_case_start_from_idx("Abcdefg", 1), StrIndex::new(7, 7)); + } + + #[test] + fn camel_case_indices_full() { + assert_eq!(camel_case_indices("Abc\u{f6}\u{f6}DD"), vec![StrIndex::new(7, 9)]); + } + + #[test] + fn camel_case_split_full() { + assert_eq!(camel_case_split("A"), vec!["A"]); + assert_eq!(camel_case_split("AbcDef"), vec!["Abc", "Def"]); + assert_eq!(camel_case_split("Abc"), vec!["Abc"]); + assert_eq!(camel_case_split("abcDef"), vec!["abc", "Def"]); + assert_eq!( + camel_case_split("\u{f6}\u{f6}AabABcd"), + vec!["\u{f6}\u{f6}", "Aab", "A", "Bcd"] + ); + } } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 586934df46037..92662c59226a2 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -1,9 +1,7 @@ //! Contains utility functions to generate suggestions. #![deny(clippy::missing_docs_in_private_items)] -use crate::source::{ - snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite, -}; +use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite}; use crate::{get_parent_expr_for_hir, higher}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ast, token}; @@ -33,7 +31,7 @@ pub enum Sugg<'a> { MaybeParen(Cow<'a, str>), /// A binary operator expression, including `as`-casts and explicit type /// coercion. - BinOp(AssocOp, Cow<'a, str>), + BinOp(AssocOp, Cow<'a, str>, Cow<'a, str>), } /// Literal constant `0`, for convenience. @@ -46,7 +44,8 @@ pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("")); impl Display for Sugg<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { match *self { - Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => s.fmt(f), + Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) => s.fmt(f), + Sugg::BinOp(op, ref lhs, ref rhs) => binop_to_string(op, lhs, rhs).fmt(f), } } } @@ -55,10 +54,8 @@ impl Display for Sugg<'_> { impl<'a> Sugg<'a> { /// Prepare a suggestion from an expression. pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { - snippet_opt(cx, expr.span).map(|snippet| { - let snippet = Cow::Owned(snippet); - Self::hir_from_snippet(expr, snippet) - }) + let get_snippet = |span| snippet(cx, span, ""); + snippet_opt(cx, expr.span).map(|_| Self::hir_from_snippet(expr, get_snippet)) } /// Convenience function around `hir_opt` for suggestions with a default @@ -93,9 +90,8 @@ impl<'a> Sugg<'a> { /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro. pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self { - let snippet = snippet_with_macro_callsite(cx, expr.span, default); - - Self::hir_from_snippet(expr, snippet) + let get_snippet = |span| snippet_with_macro_callsite(cx, span, default); + Self::hir_from_snippet(expr, get_snippet) } /// Same as `hir`, but first walks the span up to the given context. This will result in the @@ -112,24 +108,26 @@ impl<'a> Sugg<'a> { default: &'a str, applicability: &mut Applicability, ) -> Self { - let (snippet, in_macro) = snippet_with_context(cx, expr.span, ctxt, default, applicability); - - if in_macro { - Sugg::NonParen(snippet) + if expr.span.ctxt() == ctxt { + Self::hir_from_snippet(expr, |span| snippet(cx, span, default)) } else { - Self::hir_from_snippet(expr, snippet) + let snip = snippet_with_applicability(cx, expr.span, default, applicability); + Sugg::NonParen(snip) } } /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*` /// function variants of `Sugg`, since these use different snippet functions. - fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self { + fn hir_from_snippet(expr: &hir::Expr<'_>, get_snippet: impl Fn(Span) -> Cow<'a, str>) -> Self { if let Some(range) = higher::Range::hir(expr) { let op = match range.limits { ast::RangeLimits::HalfOpen => AssocOp::DotDot, ast::RangeLimits::Closed => AssocOp::DotDotEq, }; - return Sugg::BinOp(op, snippet); + let start = range.start.map_or("".into(), |expr| get_snippet(expr.span)); + let end = range.end.map_or("".into(), |expr| get_snippet(expr.span)); + + return Sugg::BinOp(op, start, end); } match expr.kind { @@ -139,7 +137,7 @@ impl<'a> Sugg<'a> { | hir::ExprKind::Let(..) | hir::ExprKind::Closure(..) | hir::ExprKind::Unary(..) - | hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet), + | hir::ExprKind::Match(..) => Sugg::MaybeParen(get_snippet(expr.span)), hir::ExprKind::Continue(..) | hir::ExprKind::Yield(..) | hir::ExprKind::Array(..) @@ -160,12 +158,20 @@ impl<'a> Sugg<'a> { | hir::ExprKind::Struct(..) | hir::ExprKind::Tup(..) | hir::ExprKind::DropTemps(_) - | hir::ExprKind::Err => Sugg::NonParen(snippet), - hir::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet), - hir::ExprKind::AssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet), - hir::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node.into()), snippet), - hir::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet), - hir::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet), + | hir::ExprKind::Err => Sugg::NonParen(get_snippet(expr.span)), + hir::ExprKind::Assign(lhs, rhs, _) => { + Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span)) + }, + hir::ExprKind::AssignOp(op, lhs, rhs) => { + Sugg::BinOp(hirbinop2assignop(op), get_snippet(lhs.span), get_snippet(rhs.span)) + }, + hir::ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp( + AssocOp::from_ast_binop(op.node.into()), + get_snippet(lhs.span), + get_snippet(rhs.span), + ), + hir::ExprKind::Cast(lhs, ty) => Sugg::BinOp(AssocOp::As, get_snippet(lhs.span), get_snippet(ty.span)), + hir::ExprKind::Type(lhs, ty) => Sugg::BinOp(AssocOp::Colon, get_snippet(lhs.span), get_snippet(ty.span)), } } @@ -173,10 +179,12 @@ impl<'a> Sugg<'a> { pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { use rustc_ast::ast::RangeLimits; - let snippet = if expr.span.from_expansion() { - snippet_with_macro_callsite(cx, expr.span, default) - } else { - snippet(cx, expr.span, default) + let get_whole_snippet = || { + if expr.span.from_expansion() { + snippet_with_macro_callsite(cx, expr.span, default) + } else { + snippet(cx, expr.span, default) + } }; match expr.kind { @@ -186,7 +194,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::If(..) | ast::ExprKind::Let(..) | ast::ExprKind::Unary(..) - | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet), + | ast::ExprKind::Match(..) => Sugg::MaybeParen(get_whole_snippet()), ast::ExprKind::Async(..) | ast::ExprKind::Block(..) | ast::ExprKind::Break(..) @@ -215,14 +223,42 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Array(..) | ast::ExprKind::While(..) | ast::ExprKind::Await(..) - | ast::ExprKind::Err => Sugg::NonParen(snippet), - ast::ExprKind::Range(.., RangeLimits::HalfOpen) => Sugg::BinOp(AssocOp::DotDot, snippet), - ast::ExprKind::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotEq, snippet), - ast::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet), - ast::ExprKind::AssignOp(op, ..) => Sugg::BinOp(astbinop2assignop(op), snippet), - ast::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node), snippet), - ast::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet), - ast::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet), + | ast::ExprKind::Err => Sugg::NonParen(get_whole_snippet()), + ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp( + AssocOp::DotDot, + lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)), + rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)), + ), + ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp( + AssocOp::DotDotEq, + lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)), + rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)), + ), + ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp( + AssocOp::Assign, + snippet(cx, lhs.span, default), + snippet(cx, rhs.span, default), + ), + ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp( + astbinop2assignop(op), + snippet(cx, lhs.span, default), + snippet(cx, rhs.span, default), + ), + ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp( + AssocOp::from_ast_binop(op.node), + snippet(cx, lhs.span, default), + snippet(cx, rhs.span, default), + ), + ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp( + AssocOp::As, + snippet(cx, lhs.span, default), + snippet(cx, ty.span, default), + ), + ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp( + AssocOp::Colon, + snippet(cx, lhs.span, default), + snippet(cx, ty.span, default), + ), } } @@ -306,17 +342,51 @@ impl<'a> Sugg<'a> { Sugg::NonParen(format!("({})", sugg).into()) } }, - Sugg::BinOp(_, sugg) => { - if has_enclosing_paren(&sugg) { - Sugg::NonParen(sugg) - } else { - Sugg::NonParen(format!("({})", sugg).into()) - } + Sugg::BinOp(op, lhs, rhs) => { + let sugg = binop_to_string(op, &lhs, &rhs); + Sugg::NonParen(format!("({})", sugg).into()) }, } } } +/// Generates a string from the operator and both sides. +fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { + match op { + AssocOp::Add + | AssocOp::Subtract + | AssocOp::Multiply + | AssocOp::Divide + | AssocOp::Modulus + | AssocOp::LAnd + | AssocOp::LOr + | AssocOp::BitXor + | AssocOp::BitAnd + | AssocOp::BitOr + | AssocOp::ShiftLeft + | AssocOp::ShiftRight + | AssocOp::Equal + | AssocOp::Less + | AssocOp::LessEqual + | AssocOp::NotEqual + | AssocOp::Greater + | AssocOp::GreaterEqual => format!( + "{} {} {}", + lhs, + op.to_ast_binop().expect("Those are AST ops").to_string(), + rhs + ), + AssocOp::Assign => format!("{} = {}", lhs, rhs), + AssocOp::AssignOp(op) => { + format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs) + }, + AssocOp::As => format!("{} as {}", lhs, rhs), + AssocOp::DotDot => format!("{}..{}", lhs, rhs), + AssocOp::DotDotEq => format!("{}..={}", lhs, rhs), + AssocOp::Colon => format!("{}: {}", lhs, rhs), + } +} + /// Return `true` if `sugg` is enclosed in parenthesis. fn has_enclosing_paren(sugg: impl AsRef) -> bool { let mut chars = sugg.as_ref().chars(); @@ -391,10 +461,25 @@ impl Neg for Sugg<'_> { } } -impl Not for Sugg<'_> { - type Output = Sugg<'static>; - fn not(self) -> Sugg<'static> { - make_unop("!", self) +impl Not for Sugg<'a> { + type Output = Sugg<'a>; + fn not(self) -> Sugg<'a> { + use AssocOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual}; + + if let Sugg::BinOp(op, lhs, rhs) = self { + let to_op = match op { + Equal => NotEqual, + NotEqual => Equal, + Less => GreaterEqual, + GreaterEqual => Less, + Greater => LessEqual, + LessEqual => Greater, + _ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)), + }; + Sugg::BinOp(to_op, lhs, rhs) + } else { + make_unop("!", self) + } } } @@ -463,53 +548,21 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> || is_shift(other) && is_arith(op) } - let lhs_paren = if let Sugg::BinOp(lop, _) = *lhs { + let lhs_paren = if let Sugg::BinOp(lop, _, _) = *lhs { needs_paren(op, lop, Associativity::Left) } else { false }; - let rhs_paren = if let Sugg::BinOp(rop, _) = *rhs { + let rhs_paren = if let Sugg::BinOp(rop, _, _) = *rhs { needs_paren(op, rop, Associativity::Right) } else { false }; - let lhs = ParenHelper::new(lhs_paren, lhs); - let rhs = ParenHelper::new(rhs_paren, rhs); - let sugg = match op { - AssocOp::Add - | AssocOp::BitAnd - | AssocOp::BitOr - | AssocOp::BitXor - | AssocOp::Divide - | AssocOp::Equal - | AssocOp::Greater - | AssocOp::GreaterEqual - | AssocOp::LAnd - | AssocOp::LOr - | AssocOp::Less - | AssocOp::LessEqual - | AssocOp::Modulus - | AssocOp::Multiply - | AssocOp::NotEqual - | AssocOp::ShiftLeft - | AssocOp::ShiftRight - | AssocOp::Subtract => format!( - "{} {} {}", - lhs, - op.to_ast_binop().expect("Those are AST ops").to_string(), - rhs - ), - AssocOp::Assign => format!("{} = {}", lhs, rhs), - AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs), - AssocOp::As => format!("{} as {}", lhs, rhs), - AssocOp::DotDot => format!("{}..{}", lhs, rhs), - AssocOp::DotDotEq => format!("{}..={}", lhs, rhs), - AssocOp::Colon => format!("{}: {}", lhs, rhs), - }; - - Sugg::BinOp(op, sugg.into()) + let lhs = ParenHelper::new(lhs_paren, lhs).to_string(); + let rhs = ParenHelper::new(rhs_paren, rhs).to_string(); + Sugg::BinOp(op, lhs.into(), rhs.into()) } /// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`. @@ -1007,10 +1060,32 @@ mod test { #[test] fn binop_maybe_par() { - let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1)".into()); + let sugg = Sugg::BinOp(AssocOp::Add, "1".into(), "1".into()); assert_eq!("(1 + 1)", sugg.maybe_par().to_string()); - let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1) + (1 + 1)".into()); + let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1)".into(), "(1 + 1)".into()); assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string()); } + #[test] + fn not_op() { + use AssocOp::{Add, Equal, Greater, GreaterEqual, LAnd, LOr, Less, LessEqual, NotEqual}; + + fn test_not(op: AssocOp, correct: &str) { + let sugg = Sugg::BinOp(op, "x".into(), "y".into()); + assert_eq!((!sugg).to_string(), correct); + } + + // Invert the comparison operator. + test_not(Equal, "x != y"); + test_not(NotEqual, "x == y"); + test_not(Less, "x >= y"); + test_not(LessEqual, "x > y"); + test_not(Greater, "x <= y"); + test_not(GreaterEqual, "x < y"); + + // Other operators are inverted like !(..). + test_not(Add, "!(x + y)"); + test_not(LAnd, "!(x && y)"); + test_not(LOr, "!(x || y)"); + } } diff --git a/rust-toolchain b/rust-toolchain index 3a5dfa6a8c7d8..471ae40f1ac7a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-12-17" +channel = "nightly-2021-12-30" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/ui/crashes/ice-7868.stderr b/tests/ui/crashes/ice-7868.stderr index d7b49eb89a28b..111350a6280dc 100644 --- a/tests/ui/crashes/ice-7868.stderr +++ b/tests/ui/crashes/ice-7868.stderr @@ -7,7 +7,7 @@ LL | unsafe { 0 }; = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` help: consider adding a safety comment | -LL ~ // Safety: ... +LL ~ // SAFETY: ... LL ~ unsafe { 0 }; | diff --git a/tests/ui/enum_variants.rs b/tests/ui/enum_variants.rs index 083f5143e6e4d..d3662a0a213d2 100644 --- a/tests/ui/enum_variants.rs +++ b/tests/ui/enum_variants.rs @@ -145,4 +145,10 @@ enum HIDataRequest { DeleteUnpubHIData(String), } +enum North { + Normal, + NoLeft, + NoRight, +} + fn main() {} diff --git a/tests/ui/enum_variants.stderr b/tests/ui/enum_variants.stderr index add8a91e26b85..8a3265086e84f 100644 --- a/tests/ui/enum_variants.stderr +++ b/tests/ui/enum_variants.stderr @@ -6,6 +6,18 @@ LL | cFoo, | = note: `-D clippy::enum-variant-names` implied by `-D warnings` +error: all variants have the same prefix: `c` + --> $DIR/enum_variants.rs:14:1 + | +LL | / enum Foo { +LL | | cFoo, +LL | | cBar, +LL | | cBaz, +LL | | } + | |_^ + | + = help: remove the prefixes and use full paths to the variants instead of glob imports + error: variant name starts with the enum's name --> $DIR/enum_variants.rs:26:5 | @@ -60,25 +72,25 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: all variants have the same prefix: `WithOut` - --> $DIR/enum_variants.rs:81:1 +error: all variants have the same prefix: `C` + --> $DIR/enum_variants.rs:59:1 | -LL | / enum Seallll { -LL | | WithOutCake, -LL | | WithOutTea, -LL | | WithOut, +LL | / enum Something { +LL | | CCall, +LL | | CCreate, +LL | | CCryogenize, LL | | } | |_^ | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: all variants have the same prefix: `Prefix` - --> $DIR/enum_variants.rs:87:1 +error: all variants have the same prefix: `WithOut` + --> $DIR/enum_variants.rs:81:1 | -LL | / enum NonCaps { -LL | | Prefix的, -LL | | PrefixTea, -LL | | PrefixCake, +LL | / enum Seallll { +LL | | WithOutCake, +LL | | WithOutTea, +LL | | WithOut, LL | | } | |_^ | @@ -108,5 +120,5 @@ LL | | } | = help: remove the postfixes and use full paths to the variants instead of glob imports -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/floating_point_rad.fixed b/tests/ui/floating_point_rad.fixed index a35bb1c27f35c..ce91fe176c6fc 100644 --- a/tests/ui/floating_point_rad.fixed +++ b/tests/ui/floating_point_rad.fixed @@ -11,7 +11,12 @@ pub const fn const_context() { fn main() { let x = 3f32; let _ = x.to_degrees(); + let _ = 90.0_f64.to_degrees(); + let _ = 90.5_f64.to_degrees(); let _ = x.to_radians(); + let _ = 90.0_f64.to_radians(); + let _ = 90.5_f64.to_radians(); + // let _ = 90.5 * 80. * std::f32::consts::PI / 180f32; // Cases where the lint shouldn't be applied let _ = x * 90f32 / std::f32::consts::PI; let _ = x * std::f32::consts::PI / 90f32; diff --git a/tests/ui/floating_point_rad.rs b/tests/ui/floating_point_rad.rs index 834db4be533c0..8f3234986148b 100644 --- a/tests/ui/floating_point_rad.rs +++ b/tests/ui/floating_point_rad.rs @@ -11,7 +11,12 @@ pub const fn const_context() { fn main() { let x = 3f32; let _ = x * 180f32 / std::f32::consts::PI; + let _ = 90. * 180f64 / std::f64::consts::PI; + let _ = 90.5 * 180f64 / std::f64::consts::PI; let _ = x * std::f32::consts::PI / 180f32; + let _ = 90. * std::f32::consts::PI / 180f32; + let _ = 90.5 * std::f32::consts::PI / 180f32; + // let _ = 90.5 * 80. * std::f32::consts::PI / 180f32; // Cases where the lint shouldn't be applied let _ = x * 90f32 / std::f32::consts::PI; let _ = x * std::f32::consts::PI / 90f32; diff --git a/tests/ui/floating_point_rad.stderr b/tests/ui/floating_point_rad.stderr index acecddbca53bf..f12d3d23f3ab9 100644 --- a/tests/ui/floating_point_rad.stderr +++ b/tests/ui/floating_point_rad.stderr @@ -6,11 +6,35 @@ LL | let _ = x * 180f32 / std::f32::consts::PI; | = note: `-D clippy::suboptimal-flops` implied by `-D warnings` -error: conversion to radians can be done more accurately +error: conversion to degrees can be done more accurately --> $DIR/floating_point_rad.rs:14:13 | +LL | let _ = 90. * 180f64 / std::f64::consts::PI; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_degrees()` + +error: conversion to degrees can be done more accurately + --> $DIR/floating_point_rad.rs:15:13 + | +LL | let _ = 90.5 * 180f64 / std::f64::consts::PI; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_degrees()` + +error: conversion to radians can be done more accurately + --> $DIR/floating_point_rad.rs:16:13 + | LL | let _ = x * std::f32::consts::PI / 180f32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()` -error: aborting due to 2 previous errors +error: conversion to radians can be done more accurately + --> $DIR/floating_point_rad.rs:17:13 + | +LL | let _ = 90. * std::f32::consts::PI / 180f32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_radians()` + +error: conversion to radians can be done more accurately + --> $DIR/floating_point_rad.rs:18:13 + | +LL | let _ = 90.5 * std::f32::consts::PI / 180f32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_radians()` + +error: aborting due to 6 previous errors diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index ceaacaaf6bd70..2ed4b5db574d4 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -2,10 +2,20 @@ const ONE: i64 = 1; const NEG_ONE: i64 = -1; const ZERO: i64 = 0; +struct A(String); + +impl std::ops::Shl for A { + type Output = A; + fn shl(mut self, other: i32) -> Self { + self.0.push_str(&format!("{}", other)); + self + } +} #[allow( clippy::eq_op, clippy::no_effect, clippy::unnecessary_operation, + clippy::op_ref, clippy::double_parens )] #[warn(clippy::identity_op)] @@ -38,4 +48,9 @@ fn main() { 42 << 0; 1 >> 0; 42 >> 0; + &x >> 0; + x >> &0; + + let mut a = A("".into()); + let b = a << 0; // no error: non-integer } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index d8d44a74f9ab0..ff34b38db015b 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -1,5 +1,5 @@ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:16:5 + --> $DIR/identity_op.rs:26:5 | LL | x + 0; | ^^^^^ @@ -7,64 +7,76 @@ LL | x + 0; = note: `-D clippy::identity-op` implied by `-D warnings` error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:17:5 + --> $DIR/identity_op.rs:27:5 | LL | x + (1 - 1); | ^^^^^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:19:5 + --> $DIR/identity_op.rs:29:5 | LL | 0 + x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:22:5 + --> $DIR/identity_op.rs:32:5 | LL | x | (0); | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:25:5 + --> $DIR/identity_op.rs:35:5 | LL | x * 1; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:26:5 + --> $DIR/identity_op.rs:36:5 | LL | 1 * x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:32:5 + --> $DIR/identity_op.rs:42:5 | LL | -1 & x; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `u` - --> $DIR/identity_op.rs:35:5 + --> $DIR/identity_op.rs:45:5 | LL | u & 255; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:38:5 + --> $DIR/identity_op.rs:48:5 | LL | 42 << 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:39:5 + --> $DIR/identity_op.rs:49:5 | LL | 1 >> 0; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:40:5 + --> $DIR/identity_op.rs:50:5 | LL | 42 >> 0; | ^^^^^^^ -error: aborting due to 11 previous errors +error: the operation is ineffective. Consider reducing it to `&x` + --> $DIR/identity_op.rs:51:5 + | +LL | &x >> 0; + | ^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:52:5 + | +LL | x >> &0; + | ^^^^^^^ + +error: aborting due to 13 previous errors diff --git a/tests/ui/iter_skip_next.fixed b/tests/ui/iter_skip_next.fixed index 928b6acb95101..2db4c2bee7f2b 100644 --- a/tests/ui/iter_skip_next.fixed +++ b/tests/ui/iter_skip_next.fixed @@ -4,6 +4,7 @@ #![warn(clippy::iter_skip_next)] #![allow(clippy::blacklisted_name)] #![allow(clippy::iter_nth)] +#![allow(unused_mut, dead_code)] extern crate option_helpers; @@ -19,4 +20,18 @@ fn main() { let foo = IteratorFalsePositives { foo: 0 }; let _ = foo.skip(42).next(); let _ = foo.filter().skip(42).next(); + + // fix #8128 + let test_string = "1|1 2"; + let mut sp = test_string.split('|').map(|s| s.trim()); + let _: Vec<&str> = sp.nth(1).unwrap().split(' ').collect(); + if let Some(mut s) = Some(test_string.split('|').map(|s| s.trim())) { + let _: Vec<&str> = s.nth(1).unwrap().split(' ').collect(); + }; + fn check(mut s: T) + where + T: Iterator, + { + let _: Vec<&str> = s.nth(1).unwrap().split(' ').collect(); + } } diff --git a/tests/ui/iter_skip_next.rs b/tests/ui/iter_skip_next.rs index 7075e2598ebee..692edb9aed939 100644 --- a/tests/ui/iter_skip_next.rs +++ b/tests/ui/iter_skip_next.rs @@ -4,6 +4,7 @@ #![warn(clippy::iter_skip_next)] #![allow(clippy::blacklisted_name)] #![allow(clippy::iter_nth)] +#![allow(unused_mut, dead_code)] extern crate option_helpers; @@ -19,4 +20,18 @@ fn main() { let foo = IteratorFalsePositives { foo: 0 }; let _ = foo.skip(42).next(); let _ = foo.filter().skip(42).next(); + + // fix #8128 + let test_string = "1|1 2"; + let mut sp = test_string.split('|').map(|s| s.trim()); + let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect(); + if let Some(mut s) = Some(test_string.split('|').map(|s| s.trim())) { + let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect(); + }; + fn check(mut s: T) + where + T: Iterator, + { + let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect(); + } } diff --git a/tests/ui/iter_skip_next.stderr b/tests/ui/iter_skip_next.stderr index 486de718bb563..ca6970b27f16c 100644 --- a/tests/ui/iter_skip_next.stderr +++ b/tests/ui/iter_skip_next.stderr @@ -1,5 +1,5 @@ error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:15:28 + --> $DIR/iter_skip_next.rs:16:28 | LL | let _ = some_vec.iter().skip(42).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` @@ -7,22 +7,40 @@ LL | let _ = some_vec.iter().skip(42).next(); = note: `-D clippy::iter-skip-next` implied by `-D warnings` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:16:36 + --> $DIR/iter_skip_next.rs:17:36 | LL | let _ = some_vec.iter().cycle().skip(42).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:17:20 + --> $DIR/iter_skip_next.rs:18:20 | LL | let _ = (1..10).skip(10).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:18:33 + --> $DIR/iter_skip_next.rs:19:33 | LL | let _ = &some_vec[..].iter().skip(3).next(); | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)` -error: aborting due to 4 previous errors +error: called `skip(..).next()` on an iterator + --> $DIR/iter_skip_next.rs:27:26 + | +LL | let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect(); + | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)` + +error: called `skip(..).next()` on an iterator + --> $DIR/iter_skip_next.rs:29:29 + | +LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect(); + | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)` + +error: called `skip(..).next()` on an iterator + --> $DIR/iter_skip_next.rs:35:29 + | +LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect(); + | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)` + +error: aborting due to 7 previous errors diff --git a/tests/ui/iter_skip_next_unfixable.rs b/tests/ui/iter_skip_next_unfixable.rs new file mode 100644 index 0000000000000..3607330cfa0d3 --- /dev/null +++ b/tests/ui/iter_skip_next_unfixable.rs @@ -0,0 +1,19 @@ +#![warn(clippy::iter_skip_next)] +#![allow(dead_code)] + +/// Checks implementation of `ITER_SKIP_NEXT` lint +fn main() { + // fix #8128 + let test_string = "1|1 2"; + let sp = test_string.split('|').map(|s| s.trim()); + let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect(); + if let Some(s) = Some(test_string.split('|').map(|s| s.trim())) { + let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect(); + }; + fn check(s: T) + where + T: Iterator, + { + let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect(); + } +} diff --git a/tests/ui/iter_skip_next_unfixable.stderr b/tests/ui/iter_skip_next_unfixable.stderr new file mode 100644 index 0000000000000..74c327c748361 --- /dev/null +++ b/tests/ui/iter_skip_next_unfixable.stderr @@ -0,0 +1,39 @@ +error: called `skip(..).next()` on an iterator + --> $DIR/iter_skip_next_unfixable.rs:9:26 + | +LL | let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect(); + | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)` + | + = note: `-D clippy::iter-skip-next` implied by `-D warnings` +help: for this change `sp` has to be mutable + --> $DIR/iter_skip_next_unfixable.rs:8:9 + | +LL | let sp = test_string.split('|').map(|s| s.trim()); + | ^^ + +error: called `skip(..).next()` on an iterator + --> $DIR/iter_skip_next_unfixable.rs:11:29 + | +LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect(); + | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)` + | +help: for this change `s` has to be mutable + --> $DIR/iter_skip_next_unfixable.rs:10:17 + | +LL | if let Some(s) = Some(test_string.split('|').map(|s| s.trim())) { + | ^ + +error: called `skip(..).next()` on an iterator + --> $DIR/iter_skip_next_unfixable.rs:17:29 + | +LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect(); + | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)` + | +help: for this change `s` has to be mutable + --> $DIR/iter_skip_next_unfixable.rs:13:17 + | +LL | fn check(s: T) + | ^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr index 0243158dec507..2e3ebadd7b5d2 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -43,7 +43,7 @@ LL | / for i in 3..(3 + src.len()) { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..((3 + src.len()) - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..(3 + src.len() - 3)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:35:5 diff --git a/tests/ui/needless_bool/fixable.fixed b/tests/ui/needless_bool/fixable.fixed index 85da1f4e10437..89dc13fd5b13d 100644 --- a/tests/ui/needless_bool/fixable.fixed +++ b/tests/ui/needless_bool/fixable.fixed @@ -41,6 +41,15 @@ fn main() { x; !x; !(x && y); + let a = 0; + let b = 1; + + a != b; + a == b; + a >= b; + a > b; + a <= b; + a < b; if x { x } else { diff --git a/tests/ui/needless_bool/fixable.rs b/tests/ui/needless_bool/fixable.rs index add606302511b..c11d9472e8d06 100644 --- a/tests/ui/needless_bool/fixable.rs +++ b/tests/ui/needless_bool/fixable.rs @@ -53,6 +53,39 @@ fn main() { } else { true }; + let a = 0; + let b = 1; + + if a == b { + false + } else { + true + }; + if a != b { + false + } else { + true + }; + if a < b { + false + } else { + true + }; + if a <= b { + false + } else { + true + }; + if a > b { + false + } else { + true + }; + if a >= b { + false + } else { + true + }; if x { x } else { diff --git a/tests/ui/needless_bool/fixable.stderr b/tests/ui/needless_bool/fixable.stderr index 22c0a7bb491c6..d2c48376f7662 100644 --- a/tests/ui/needless_bool/fixable.stderr +++ b/tests/ui/needless_bool/fixable.stderr @@ -31,7 +31,67 @@ LL | | }; | |_____^ help: you can reduce it to: `!(x && y)` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:72:5 + --> $DIR/fixable.rs:59:5 + | +LL | / if a == b { +LL | | false +LL | | } else { +LL | | true +LL | | }; + | |_____^ help: you can reduce it to: `a != b` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:64:5 + | +LL | / if a != b { +LL | | false +LL | | } else { +LL | | true +LL | | }; + | |_____^ help: you can reduce it to: `a == b` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:69:5 + | +LL | / if a < b { +LL | | false +LL | | } else { +LL | | true +LL | | }; + | |_____^ help: you can reduce it to: `a >= b` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:74:5 + | +LL | / if a <= b { +LL | | false +LL | | } else { +LL | | true +LL | | }; + | |_____^ help: you can reduce it to: `a > b` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:79:5 + | +LL | / if a > b { +LL | | false +LL | | } else { +LL | | true +LL | | }; + | |_____^ help: you can reduce it to: `a <= b` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:84:5 + | +LL | / if a >= b { +LL | | false +LL | | } else { +LL | | true +LL | | }; + | |_____^ help: you can reduce it to: `a < b` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:105:5 | LL | / if x { LL | | return true; @@ -41,7 +101,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:80:5 + --> $DIR/fixable.rs:113:5 | LL | / if x { LL | | return false; @@ -51,7 +111,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:88:5 + --> $DIR/fixable.rs:121:5 | LL | / if x && y { LL | | return true; @@ -61,7 +121,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x && y` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:96:5 + --> $DIR/fixable.rs:129:5 | LL | / if x && y { LL | | return false; @@ -71,7 +131,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !(x && y)` error: equality checks against true are unnecessary - --> $DIR/fixable.rs:104:8 + --> $DIR/fixable.rs:137:8 | LL | if x == true {}; | ^^^^^^^^^ help: try simplifying it as shown: `x` @@ -79,25 +139,25 @@ LL | if x == true {}; = note: `-D clippy::bool-comparison` implied by `-D warnings` error: equality checks against false can be replaced by a negation - --> $DIR/fixable.rs:108:8 + --> $DIR/fixable.rs:141:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: equality checks against true are unnecessary - --> $DIR/fixable.rs:118:8 + --> $DIR/fixable.rs:151:8 | LL | if x == true {}; | ^^^^^^^^^ help: try simplifying it as shown: `x` error: equality checks against false can be replaced by a negation - --> $DIR/fixable.rs:119:8 + --> $DIR/fixable.rs:152:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:128:12 + --> $DIR/fixable.rs:161:12 | LL | } else if returns_bool() { | ____________^ @@ -108,7 +168,7 @@ LL | | }; | |_____^ help: you can reduce it to: `{ !returns_bool() }` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:141:5 + --> $DIR/fixable.rs:174:5 | LL | / if unsafe { no(4) } & 1 != 0 { LL | | true @@ -118,16 +178,16 @@ LL | | }; | |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:146:30 + --> $DIR/fixable.rs:179:30 | LL | let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:149:9 + --> $DIR/fixable.rs:182:9 | LL | if unsafe { no(4) } & 1 != 0 { true } else { false } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` -error: aborting due to 15 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 83f467c840026..603d438d55889 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -71,7 +71,18 @@ fn test_void_if_fun(b: bool) { fn test_void_match(x: u32) { match x { 0 => (), - _ => {}, + _ => (), + } +} + +fn test_nested_match(x: u32) { + match x { + 0 => (), + 1 => { + let _ = 42; + + }, + _ => (), } } @@ -182,7 +193,7 @@ async fn async_test_void_if_fun(b: bool) { async fn async_test_void_match(x: u32) { match x { 0 => (), - _ => {}, + _ => (), } } diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 341caf18bd60c..c6c8cb9ec1520 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -75,6 +75,17 @@ fn test_void_match(x: u32) { } } +fn test_nested_match(x: u32) { + match x { + 0 => (), + 1 => { + let _ = 42; + return; + }, + _ => return, + } +} + fn read_line() -> String { use std::io::BufRead; let stdin = ::std::io::stdin(); diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index c0abc2c63dde1..5bc787c56a65b 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -70,127 +70,139 @@ error: unneeded `return` statement --> $DIR/needless_return.rs:74:14 | LL | _ => return, - | ^^^^^^ help: replace `return` with an empty block: `{}` + | ^^^^^^ help: replace `return` with a unit value: `()` error: unneeded `return` statement - --> $DIR/needless_return.rs:89:9 + --> $DIR/needless_return.rs:83:13 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:85:14 + | +LL | _ => return, + | ^^^^^^ help: replace `return` with a unit value: `()` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:100:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` error: unneeded `return` statement - --> $DIR/needless_return.rs:91:9 + --> $DIR/needless_return.rs:102:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` error: unneeded `return` statement - --> $DIR/needless_return.rs:113:32 + --> $DIR/needless_return.rs:124:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:118:13 + --> $DIR/needless_return.rs:129:13 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:120:20 + --> $DIR/needless_return.rs:131:20 | LL | let _ = || return; | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:126:32 + --> $DIR/needless_return.rs:137:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ help: remove `return`: `Foo` error: unneeded `return` statement - --> $DIR/needless_return.rs:135:5 + --> $DIR/needless_return.rs:146:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:139:5 + --> $DIR/needless_return.rs:150:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:144:9 + --> $DIR/needless_return.rs:155:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:146:9 + --> $DIR/needless_return.rs:157:9 | LL | return false; | ^^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:152:17 + --> $DIR/needless_return.rs:163:17 | LL | true => return false, | ^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:154:13 + --> $DIR/needless_return.rs:165:13 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:161:9 + --> $DIR/needless_return.rs:172:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:163:16 + --> $DIR/needless_return.rs:174:16 | LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:171:5 + --> $DIR/needless_return.rs:182:5 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:176:9 + --> $DIR/needless_return.rs:187:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:178:9 + --> $DIR/needless_return.rs:189:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:185:14 + --> $DIR/needless_return.rs:196:14 | LL | _ => return, - | ^^^^^^ help: replace `return` with an empty block: `{}` + | ^^^^^^ help: replace `return` with a unit value: `()` error: unneeded `return` statement - --> $DIR/needless_return.rs:200:9 + --> $DIR/needless_return.rs:211:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` error: unneeded `return` statement - --> $DIR/needless_return.rs:202:9 + --> $DIR/needless_return.rs:213:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` -error: aborting due to 32 previous errors +error: aborting due to 34 previous errors diff --git a/tests/ui/neg_multiply.fixed b/tests/ui/neg_multiply.fixed new file mode 100644 index 0000000000000..35af9d6ae317f --- /dev/null +++ b/tests/ui/neg_multiply.fixed @@ -0,0 +1,45 @@ +// run-rustfix +#![warn(clippy::neg_multiply)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::precedence)] +#![allow(unused)] + +use std::ops::Mul; + +struct X; + +impl Mul for X { + type Output = X; + + fn mul(self, _r: isize) -> Self { + self + } +} + +impl Mul for isize { + type Output = X; + + fn mul(self, _r: X) -> X { + X + } +} + +fn main() { + let x = 0; + + -x; + + -x; + + 100 + -x; + + -(100 + x); + + -17; + + 0xcafe | -0xff00; + + -1 * -1; // should be ok + + X * -1; // should be ok + -1 * X; // should also be ok +} diff --git a/tests/ui/neg_multiply.rs b/tests/ui/neg_multiply.rs index d4a20ce9db1c8..7dbdb0906ceeb 100644 --- a/tests/ui/neg_multiply.rs +++ b/tests/ui/neg_multiply.rs @@ -1,5 +1,7 @@ +// run-rustfix #![warn(clippy::neg_multiply)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::precedence)] +#![allow(unused)] use std::ops::Mul; @@ -28,6 +30,14 @@ fn main() { -1 * x; + 100 + x * -1; + + (100 + x) * -1; + + -1 * 17; + + 0xcafe | 0xff00 * -1; + -1 * -1; // should be ok X * -1; // should be ok diff --git a/tests/ui/neg_multiply.stderr b/tests/ui/neg_multiply.stderr index ad677f6d6fb9b..dbf8fb36938cb 100644 --- a/tests/ui/neg_multiply.stderr +++ b/tests/ui/neg_multiply.stderr @@ -1,16 +1,40 @@ -error: negation by multiplying with `-1` - --> $DIR/neg_multiply.rs:27:5 +error: this multiplication by -1 can be written more succinctly + --> $DIR/neg_multiply.rs:29:5 | LL | x * -1; - | ^^^^^^ + | ^^^^^^ help: consider using: `-x` | = note: `-D clippy::neg-multiply` implied by `-D warnings` -error: negation by multiplying with `-1` - --> $DIR/neg_multiply.rs:29:5 +error: this multiplication by -1 can be written more succinctly + --> $DIR/neg_multiply.rs:31:5 | LL | -1 * x; - | ^^^^^^ + | ^^^^^^ help: consider using: `-x` + +error: this multiplication by -1 can be written more succinctly + --> $DIR/neg_multiply.rs:33:11 + | +LL | 100 + x * -1; + | ^^^^^^ help: consider using: `-x` + +error: this multiplication by -1 can be written more succinctly + --> $DIR/neg_multiply.rs:35:5 + | +LL | (100 + x) * -1; + | ^^^^^^^^^^^^^^ help: consider using: `-(100 + x)` + +error: this multiplication by -1 can be written more succinctly + --> $DIR/neg_multiply.rs:37:5 + | +LL | -1 * 17; + | ^^^^^^^ help: consider using: `-17` + +error: this multiplication by -1 can be written more succinctly + --> $DIR/neg_multiply.rs:39:14 + | +LL | 0xcafe | 0xff00 * -1; + | ^^^^^^^^^^^ help: consider using: `-0xff00` -error: aborting due to 2 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/numbered_fields.fixed b/tests/ui/numbered_fields.fixed new file mode 100644 index 0000000000000..1da97e9687988 --- /dev/null +++ b/tests/ui/numbered_fields.fixed @@ -0,0 +1,33 @@ +//run-rustfix +#![warn(clippy::init_numbered_fields)] + +#[derive(Default)] +struct TupleStruct(u32, u32, u8); + +// This shouldn't lint because it's in a macro +macro_rules! tuple_struct_init { + () => { + TupleStruct { 0: 0, 1: 1, 2: 2 } + }; +} + +fn main() { + let tuple_struct = TupleStruct::default(); + + // This should lint + let _ = TupleStruct(1u32, 42, 23u8); + + // This should also lint and order the fields correctly + let _ = TupleStruct(1u32, 3u32, 2u8); + + // Ok because of default initializer + let _ = TupleStruct { 0: 42, ..tuple_struct }; + + let _ = TupleStruct { + 1: 23, + ..TupleStruct::default() + }; + + // Ok because it's in macro + let _ = tuple_struct_init!(); +} diff --git a/tests/ui/numbered_fields.rs b/tests/ui/numbered_fields.rs new file mode 100644 index 0000000000000..08ec405a5606e --- /dev/null +++ b/tests/ui/numbered_fields.rs @@ -0,0 +1,41 @@ +//run-rustfix +#![warn(clippy::init_numbered_fields)] + +#[derive(Default)] +struct TupleStruct(u32, u32, u8); + +// This shouldn't lint because it's in a macro +macro_rules! tuple_struct_init { + () => { + TupleStruct { 0: 0, 1: 1, 2: 2 } + }; +} + +fn main() { + let tuple_struct = TupleStruct::default(); + + // This should lint + let _ = TupleStruct { + 0: 1u32, + 1: 42, + 2: 23u8, + }; + + // This should also lint and order the fields correctly + let _ = TupleStruct { + 0: 1u32, + 2: 2u8, + 1: 3u32, + }; + + // Ok because of default initializer + let _ = TupleStruct { 0: 42, ..tuple_struct }; + + let _ = TupleStruct { + 1: 23, + ..TupleStruct::default() + }; + + // Ok because it's in macro + let _ = tuple_struct_init!(); +} diff --git a/tests/ui/numbered_fields.stderr b/tests/ui/numbered_fields.stderr new file mode 100644 index 0000000000000..01691c8b141e8 --- /dev/null +++ b/tests/ui/numbered_fields.stderr @@ -0,0 +1,26 @@ +error: used a field initializer for a tuple struct + --> $DIR/numbered_fields.rs:18:13 + | +LL | let _ = TupleStruct { + | _____________^ +LL | | 0: 1u32, +LL | | 1: 42, +LL | | 2: 23u8, +LL | | }; + | |_____^ help: try this instead: `TupleStruct(1u32, 42, 23u8)` + | + = note: `-D clippy::init-numbered-fields` implied by `-D warnings` + +error: used a field initializer for a tuple struct + --> $DIR/numbered_fields.rs:25:13 + | +LL | let _ = TupleStruct { + | _____________^ +LL | | 0: 1u32, +LL | | 2: 2u8, +LL | | 1: 3u32, +LL | | }; + | |_____^ help: try this instead: `TupleStruct(1u32, 3u32, 2u8)` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/return_self_not_must_use.rs b/tests/ui/return_self_not_must_use.rs index bdf3f3d799582..7dd5742dae9f2 100644 --- a/tests/ui/return_self_not_must_use.rs +++ b/tests/ui/return_self_not_must_use.rs @@ -5,12 +5,12 @@ pub struct Bar; pub trait Whatever { fn what(&self) -> Self; - // There should be no warning here! + // There should be no warning here! (returns a reference) fn what2(&self) -> &Self; } impl Bar { - // There should be no warning here! + // There should be no warning here! (note taking a self argument) pub fn not_new() -> Self { Self } @@ -20,23 +20,38 @@ impl Bar { pub fn bar(self) -> Self { self } - // There should be no warning here! + // There should be no warning here! (private method) fn foo2(&self) -> Self { Self } - // There should be no warning here! + // There should be no warning here! (returns a reference) pub fn foo3(&self) -> &Self { self } + // There should be no warning here! (already a `must_use` attribute) + #[must_use] + pub fn foo4(&self) -> Self { + Self + } } impl Whatever for Bar { - // There should be no warning here! + // There should be no warning here! (comes from the trait) fn what(&self) -> Self { self.foo2() } - // There should be no warning here! + // There should be no warning here! (comes from the trait) fn what2(&self) -> &Self { self } } + +#[must_use] +pub struct Foo; + +impl Foo { + // There should be no warning here! (`Foo` already implements `#[must_use]`) + fn foo(&self) -> Self { + Self + } +} diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index 06f6949b66f5a..0321f8c4cdf84 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -47,6 +47,8 @@ fn syntax() { let _ = |[x]: [u32; 1]| { let x = 1; }; + let y = Some(1); + if let Some(y) = y {} } fn negative() { diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index dcc7d4e6b2ff9..f8b9221d55587 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -241,17 +241,29 @@ note: previous binding is here LL | let _ = |[x]: [u32; 1]| { | ^ +error: `y` is shadowed + --> $DIR/shadow.rs:51:17 + | +LL | if let Some(y) = y {} + | ^ + | +note: previous binding is here + --> $DIR/shadow.rs:50:9 + | +LL | let y = Some(1); + | ^ + error: `_b` shadows a previous, unrelated binding - --> $DIR/shadow.rs:85:9 + --> $DIR/shadow.rs:87:9 | LL | let _b = _a; | ^^ | note: previous binding is here - --> $DIR/shadow.rs:84:28 + --> $DIR/shadow.rs:86:28 | LL | pub async fn foo2(_a: i32, _b: i64) { | ^^ -error: aborting due to 21 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/short_circuit_statement.fixed b/tests/ui/short_circuit_statement.fixed index af0a397bd1aff..dd22ecab0b551 100644 --- a/tests/ui/short_circuit_statement.fixed +++ b/tests/ui/short_circuit_statement.fixed @@ -6,7 +6,7 @@ fn main() { if f() { g(); } if !f() { g(); } - if !(1 == 2) { g(); } + if 1 != 2 { g(); } } fn f() -> bool { diff --git a/tests/ui/short_circuit_statement.stderr b/tests/ui/short_circuit_statement.stderr index 0a3f60c3d132d..aa84ac3a7925f 100644 --- a/tests/ui/short_circuit_statement.stderr +++ b/tests/ui/short_circuit_statement.stderr @@ -16,7 +16,7 @@ error: boolean short circuit operator in statement may be clearer using an expli --> $DIR/short_circuit_statement.rs:9:5 | LL | 1 == 2 || g(); - | ^^^^^^^^^^^^^^ help: replace it with: `if !(1 == 2) { g(); }` + | ^^^^^^^^^^^^^^ help: replace it with: `if 1 != 2 { g(); }` error: aborting due to 3 previous errors diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index 7e510d8947522..380303d8152aa 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -5,7 +5,7 @@ fn nested_local() { let _ = { let _ = { - // Safety: + // SAFETY: let _ = unsafe {}; }; }; @@ -14,7 +14,7 @@ fn nested_local() { fn deep_nest() { let _ = { let _ = { - // Safety: + // SAFETY: let _ = unsafe {}; // Safety: @@ -28,7 +28,7 @@ fn deep_nest() { // Safety: let _ = unsafe {}; - // Safety: + // SAFETY: unsafe {}; }; }; @@ -44,7 +44,7 @@ fn deep_nest() { unsafe {}; }; - // Safety: + // SAFETY: unsafe {}; } @@ -59,7 +59,7 @@ fn line_comment() { } fn line_comment_newlines() { - // Safety: + // SAFETY: unsafe {} } @@ -84,7 +84,7 @@ fn block_comment() { } fn block_comment_newlines() { - /* Safety: */ + /* SAFETY: */ unsafe {} } @@ -96,7 +96,7 @@ fn inline_block_comment() { fn block_comment_with_extras() { /* This is a description - * Safety: + * SAFETY: */ unsafe {} } @@ -122,7 +122,7 @@ fn buried_safety() { } fn safety_with_prepended_text() { - // This is a test. Safety: + // This is a test. safety: unsafe {} } @@ -132,7 +132,7 @@ fn local_line_comment() { } fn local_block_comment() { - /* Safety: */ + /* SAFETY: */ let _ = unsafe {}; } @@ -142,18 +142,18 @@ fn comment_array() { } fn comment_tuple() { - // Safety: + // sAFETY: let _ = (42, unsafe {}, "test", unsafe {}); } fn comment_unary() { - // Safety: + // SAFETY: let _ = *unsafe { &42 }; } #[allow(clippy::match_single_binding)] fn comment_match() { - // Safety: + // SAFETY: let _ = match unsafe {} { _ => {}, }; @@ -177,7 +177,7 @@ fn comment_macro_call() { } t!( - // Safety: + // SAFETY: unsafe {} ); } @@ -194,18 +194,18 @@ fn comment_macro_def() { } fn non_ascii_comment() { - // ॐ᧻໒ Safety: ௵∰ + // ॐ᧻໒ SaFeTy: ௵∰ unsafe {}; } fn local_commented_block() { let _ = - // Safety: + // safety: unsafe {}; } fn local_nest() { - // Safety: + // safety: let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})]; } @@ -267,17 +267,17 @@ fn no_comment_macro_def() { } fn trailing_comment() { - unsafe {} // Safety: + unsafe {} // SAFETY: } fn internal_comment() { unsafe { - // Safety: + // SAFETY: } } fn interference() { - // Safety + // SAFETY let _ = 42; diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index ebe589001a1fe..f69d0da54e0d6 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -7,7 +7,7 @@ LL | unsafe {} = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` help: consider adding a safety comment | -LL ~ // Safety: ... +LL ~ // SAFETY: ... LL + unsafe {} | @@ -19,7 +19,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | help: consider adding a safety comment | -LL ~ // Safety: ... +LL ~ // SAFETY: ... LL + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | @@ -31,7 +31,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); | help: consider adding a safety comment | -LL ~ // Safety: ... +LL ~ // SAFETY: ... LL + let _ = (42, unsafe {}, "test", unsafe {}); | @@ -43,7 +43,7 @@ LL | let _ = *unsafe { &42 }; | help: consider adding a safety comment | -LL ~ // Safety: ... +LL ~ // SAFETY: ... LL + let _ = *unsafe { &42 }; | @@ -55,7 +55,7 @@ LL | let _ = match unsafe {} { | help: consider adding a safety comment | -LL ~ // Safety: ... +LL ~ // SAFETY: ... LL + let _ = match unsafe {} { | @@ -67,7 +67,7 @@ LL | let _ = &unsafe {}; | help: consider adding a safety comment | -LL ~ // Safety: ... +LL ~ // SAFETY: ... LL + let _ = &unsafe {}; | @@ -79,7 +79,7 @@ LL | let _ = [unsafe {}; 5]; | help: consider adding a safety comment | -LL ~ // Safety: ... +LL ~ // SAFETY: ... LL + let _ = [unsafe {}; 5]; | @@ -91,7 +91,7 @@ LL | let _ = unsafe {}; | help: consider adding a safety comment | -LL ~ // Safety: ... +LL ~ // SAFETY: ... LL + let _ = unsafe {}; | @@ -103,7 +103,7 @@ LL | t!(unsafe {}); | help: consider adding a safety comment | -LL ~ t!(// Safety: ... +LL ~ t!(// SAFETY: ... LL ~ unsafe {}); | @@ -122,13 +122,13 @@ LL | t!(); error: unsafe block missing a safety comment --> $DIR/undocumented_unsafe_blocks.rs:270:5 | -LL | unsafe {} // Safety: +LL | unsafe {} // SAFETY: | ^^^^^^^^^ | help: consider adding a safety comment | -LL ~ // Safety: ... -LL ~ unsafe {} // Safety: +LL ~ // SAFETY: ... +LL ~ unsafe {} // SAFETY: | error: unsafe block missing a safety comment @@ -139,7 +139,7 @@ LL | unsafe { | help: consider adding a safety comment | -LL ~ // Safety: ... +LL ~ // SAFETY: ... LL + unsafe { | @@ -151,7 +151,7 @@ LL | unsafe {}; | help: consider adding a safety comment | -LL ~ // Safety: ... +LL ~ // SAFETY: ... LL ~ unsafe {}; | @@ -163,7 +163,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | help: consider adding a safety comment | -LL ~ println!("{}", // Safety: ... +LL ~ println!("{}", // SAFETY: ... LL ~ unsafe { String::from_utf8_unchecked(vec![]) }); | diff --git a/tests/ui/unwrap_or_else_default.fixed b/tests/ui/unwrap_or_else_default.fixed index 7ac3f426c9775..c2b9bd2c881fe 100644 --- a/tests/ui/unwrap_or_else_default.fixed +++ b/tests/ui/unwrap_or_else_default.fixed @@ -45,7 +45,7 @@ fn unwrap_or_else_default() { with_enum.unwrap_or_else(Enum::A); let with_new = Some(vec![1]); - with_new.unwrap_or_else(Vec::new); + with_new.unwrap_or_default(); let with_err: Result<_, ()> = Ok(vec![1]); with_err.unwrap_or_else(make); @@ -66,6 +66,9 @@ fn unwrap_or_else_default() { let with_default_type = Some(1); with_default_type.unwrap_or_default(); + + let with_default_type: Option> = None; + with_default_type.unwrap_or_default(); } fn main() {} diff --git a/tests/ui/unwrap_or_else_default.rs b/tests/ui/unwrap_or_else_default.rs index 82b727a039ed4..d55664990aeb9 100644 --- a/tests/ui/unwrap_or_else_default.rs +++ b/tests/ui/unwrap_or_else_default.rs @@ -66,6 +66,9 @@ fn unwrap_or_else_default() { let with_default_type = Some(1); with_default_type.unwrap_or_else(u64::default); + + let with_default_type: Option> = None; + with_default_type.unwrap_or_else(Vec::new); } fn main() {} diff --git a/tests/ui/unwrap_or_else_default.stderr b/tests/ui/unwrap_or_else_default.stderr index feb215b09f662..53e31d85edfca 100644 --- a/tests/ui/unwrap_or_else_default.stderr +++ b/tests/ui/unwrap_or_else_default.stderr @@ -1,10 +1,16 @@ +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:48:5 + | +LL | with_new.unwrap_or_else(Vec::new); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_new.unwrap_or_default()` + | + = note: `-D clippy::unwrap-or-else-default` implied by `-D warnings` + error: use of `.unwrap_or_else(..)` to construct default value --> $DIR/unwrap_or_else_default.rs:62:5 | LL | with_real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_real_default.unwrap_or_default()` - | - = note: `-D clippy::unwrap-or-else-default` implied by `-D warnings` error: use of `.unwrap_or_else(..)` to construct default value --> $DIR/unwrap_or_else_default.rs:65:5 @@ -18,5 +24,11 @@ error: use of `.unwrap_or_else(..)` to construct default value LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_type.unwrap_or_default()` -error: aborting due to 3 previous errors +error: use of `.unwrap_or_else(..)` to construct default value + --> $DIR/unwrap_or_else_default.rs:71:5 + | +LL | with_default_type.unwrap_or_else(Vec::new); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_type.unwrap_or_default()` + +error: aborting due to 5 previous errors From 2b5257e4c770c6a4964a7897fa1348be5050797f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 30 Dec 2021 13:37:14 -0600 Subject: [PATCH 08/72] Remove method_call! macro --- clippy_lints/src/methods/mod.rs | 41 +++++++++++++-------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4e33b2ff14cde..ad07370f26d3b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -80,7 +80,6 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; use rustc_typeck::hir_ty_to_ty; @@ -1997,24 +1996,16 @@ impl_lint_pass!(Methods => [ ]); /// Extracts a method call name, args, and `Span` of the method name. -fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(Symbol, &'tcx [hir::Expr<'tcx>], Span)> { +fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> { if let ExprKind::MethodCall(path, span, args, _) = recv.kind { if !args.iter().any(|e| e.span.from_expansion()) { - return Some((path.ident.name, args, span)); + let name = path.ident.name.as_str(); + return Some((name, args, span)); } } None } -/// Same as `method_call` but the `Symbol` is dereferenced into a temporary `&str` -macro_rules! method_call { - ($expr:expr) => { - method_call($expr) - .as_ref() - .map(|&(ref name, args, span)| (name.as_str(), args, span)) - }; -} - impl<'tcx> LateLintPass<'tcx> for Methods { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if expr.span.from_expansion() { @@ -2217,7 +2208,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { - if let Some((name, [recv, args @ ..], span)) = method_call!(expr) { + if let Some((name, [recv, args @ ..], span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); @@ -2233,7 +2224,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv), - ("collect", []) => match method_call!(recv) { + ("collect", []) => match method_call(recv) { Some((name @ ("cloned" | "copied"), [recv2], _)) => { iter_cloned_collect::check(cx, name, expr, recv2); }, @@ -2247,14 +2238,14 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, _ => {}, }, - ("count", []) => match method_call!(recv) { + ("count", []) => match method_call(recv) { Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { iter_count::check(cx, expr, recv2, name); }, Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), _ => {}, }, - ("expect", [_]) => match method_call!(recv) { + ("expect", [_]) => match method_call(recv) { Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), _ => expect_used::check(cx, expr, recv), }, @@ -2271,13 +2262,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio flat_map_option::check(cx, expr, arg, span); }, ("flatten", []) => { - if let Some(("map", [recv, map_arg], _)) = method_call!(recv) { + if let Some(("map", [recv, map_arg], _)) = method_call(recv) { map_flatten::check(cx, expr, recv, map_arg); } }, ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), ("for_each", [_]) => { - if let Some(("inspect", [_, _], span2)) = method_call!(recv) { + if let Some(("inspect", [_, _], span2)) = method_call(recv) { inspect_for_each::check(cx, expr, span2); } }, @@ -2286,7 +2277,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("map", [m_arg]) => { - if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) { + if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) { match (name, args) { ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv), ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv), @@ -2301,7 +2292,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), ("next", []) => { - if let Some((name, [recv, args @ ..], _)) = method_call!(recv) { + if let Some((name, [recv, args @ ..], _)) = method_call(recv) { match (name, args) { ("filter", [arg]) => filter_next::check(cx, expr, recv, arg), ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv), @@ -2312,7 +2303,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } } }, - ("nth", [n_arg]) => match method_call!(recv) { + ("nth", [n_arg]) => match method_call(recv) { Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg), Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), @@ -2344,12 +2335,12 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv, span); }, - ("unwrap", []) => match method_call!(recv) { + ("unwrap", []) => match method_call(recv) { Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false), Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true), _ => unwrap_used::check(cx, expr, recv), }, - ("unwrap_or", [u_arg]) => match method_call!(recv) { + ("unwrap_or", [u_arg]) => match method_call(recv) { Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => { manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, @@ -2358,7 +2349,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, _ => {}, }, - ("unwrap_or_else", [u_arg]) => match method_call!(recv) { + ("unwrap_or_else", [u_arg]) => match method_call(recv) { Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {}, _ => { unwrap_or_else_default::check(cx, expr, recv, u_arg); @@ -2371,7 +2362,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { - if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) { + if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); } } From 65d1f83d2c593c7997b4fe781814eb0fb69272e9 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 27 Dec 2021 11:17:17 -0500 Subject: [PATCH 09/72] Extend [`unused_io_amount`] to cover AsyncRead and AsyncWrite. Clippy helpfully warns about code like this, telling you that you probably meant "write_all": fn say_hi(w: &mut W) { w.write(b"hello").unwrap(); } This patch attempts to extend the lint so it also covers this case: async fn say_hi(w: &mut W) { w.write(b"hello").await.unwrap(); } (I've run into this second case several times in my own programming, and so have my coworkers, so unless we're especially accident-prone in this area, it's probably worth addressing?) This patch covers the Async{Read,Write}Ext traits in futures-rs, and in tokio, since both are quite widely used. changelog: [`unused_io_amount`] now supports AsyncReadExt and AsyncWriteExt. --- Cargo.toml | 2 + clippy_lints/src/unused_io_amount.rs | 75 ++++++++++++++++++++++++---- clippy_utils/src/paths.rs | 8 +++ tests/compile-test.rs | 6 +++ tests/ui/unused_io_amount.rs | 53 ++++++++++++++++++++ tests/ui/unused_io_amount.stderr | 58 +++++++++++++++++---- 6 files changed, 181 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8661a86775887..79a7ec92071c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,9 @@ itertools = "0.10" quote = "1.0" serde = { version = "1.0", features = ["derive"] } syn = { version = "1.0", features = ["full"] } +futures = "0.3" parking_lot = "0.11.2" +tokio = { version = "1", features = ["io-util"] } [build-dependencies] rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 004530db0861d..8afd8472db7ab 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -17,10 +17,17 @@ declare_clippy_lint! { /// partial-write/read, use /// `write_all`/`read_exact` instead. /// + /// When working with asynchronous code (either with the `futures` + /// crate or with `tokio`), a similar issue exists for + /// `AsyncWriteExt::write()` and `AsyncReadExt::read()` : these + /// functions are also not guaranteed to process the entire + /// buffer. Your code should either handle partial-writes/reads, or + /// call the `write_all`/`read_exact` methods on those traits instead. + /// /// ### Known problems /// Detects only common patterns. /// - /// ### Example + /// ### Examples /// ```rust,ignore /// use std::io; /// fn foo(w: &mut W) -> io::Result<()> { @@ -68,6 +75,23 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } } +/// If `expr` is an (e).await, return the inner expression "e" that's being +/// waited on. Otherwise return None. +fn try_remove_await<'a>(expr: &'a hir::Expr<'a>) -> Option<&hir::Expr<'a>> { + if let hir::ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { + if let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind { + if matches!( + func.kind, + hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) + ) { + return Some(arg_0); + } + } + } + + None +} + fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { let mut call = call; while let hir::ExprKind::MethodCall(path, _, args, _) = call.kind { @@ -77,30 +101,61 @@ fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr< break; } } - check_method_call(cx, call, expr); + + if let Some(call) = try_remove_await(call) { + check_method_call(cx, call, expr, true); + } else { + check_method_call(cx, call, expr, false); + } } -fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { +fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>, is_await: bool) { if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind { let symbol = path.ident.as_str(); - let read_trait = match_trait_method(cx, call, &paths::IO_READ); - let write_trait = match_trait_method(cx, call, &paths::IO_WRITE); + let read_trait = if is_await { + match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) + || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT) + } else { + match_trait_method(cx, call, &paths::IO_READ) + }; + let write_trait = if is_await { + match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT) + || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) + } else { + match_trait_method(cx, call, &paths::IO_WRITE) + }; - match (read_trait, write_trait, symbol) { - (true, _, "read") => span_lint( + match (read_trait, write_trait, symbol, is_await) { + (true, _, "read", false) => span_lint( cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled. Use `Read::read_exact` instead", ), - (true, _, "read_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"), - (_, true, "write") => span_lint( + (true, _, "read", true) => span_lint( + cx, + UNUSED_IO_AMOUNT, + expr.span, + "read amount is not handled. Use `AsyncReadExt::read_exact` instead", + ), + (true, _, "read_vectored", _) => { + span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"); + }, + (_, true, "write", false) => span_lint( cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled. Use `Write::write_all` instead", ), - (_, true, "write_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"), + (_, true, "write", true) => span_lint( + cx, + UNUSED_IO_AMOUNT, + expr.span, + "written amount is not handled. Use `AsyncWriteExt::write_all` instead", + ), + (_, true, "write_vectored", _) => { + span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"); + }, _ => (), } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 6171823abbbd0..aa3b3af235670 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -64,6 +64,10 @@ 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"]; @@ -194,6 +198,10 @@ pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; 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/tests/compile-test.rs b/tests/compile-test.rs index a2d58491872b3..d602d2042ee7f 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -21,6 +21,7 @@ const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints"); static TEST_DEPENDENCIES: &[&str] = &[ "clippy_utils", "derive_new", + "futures", "if_chain", "itertools", "quote", @@ -28,6 +29,7 @@ static TEST_DEPENDENCIES: &[&str] = &[ "serde", "serde_derive", "syn", + "tokio", "parking_lot", ]; @@ -38,6 +40,8 @@ extern crate clippy_utils; #[allow(unused_extern_crates)] extern crate derive_new; #[allow(unused_extern_crates)] +extern crate futures; +#[allow(unused_extern_crates)] extern crate if_chain; #[allow(unused_extern_crates)] extern crate itertools; @@ -47,6 +51,8 @@ extern crate parking_lot; extern crate quote; #[allow(unused_extern_crates)] extern crate syn; +#[allow(unused_extern_crates)] +extern crate tokio; /// Produces a string with an `--extern` flag for all UI test crate /// dependencies. diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index 8b141e25942d7..4b059558173fb 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] #![warn(clippy::unused_io_amount)] +extern crate futures; +use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use std::io::{self, Read}; fn question_mark(s: &mut T) -> io::Result<()> { @@ -61,4 +63,55 @@ fn combine_or(file: &str) -> Result<(), Error> { Ok(()) } +async fn bad_async_write(w: &mut W) { + w.write(b"hello world").await.unwrap(); +} + +async fn bad_async_read(r: &mut R) { + let mut buf = [0u8; 0]; + r.read(&mut buf[..]).await.unwrap(); +} + +async fn io_not_ignored_async_write(mut w: W) { + // Here we're forgetting to await the future, so we should get a + // warning about _that_ (or we would, if it were enabled), but we + // won't get one about ignoring the return value. + w.write(b"hello world"); +} + +fn bad_async_write_closure(w: W) -> impl futures::Future> { + let mut w = w; + async move { + w.write(b"hello world").await?; + Ok(()) + } +} + +async fn async_read_nested_or(r: &mut R, do_it: bool) -> Result<[u8; 1], Error> { + let mut buf = [0u8; 1]; + if do_it { + r.read(&mut buf[..]).await.or(Err(Error::Kind))?; + } + Ok(buf) +} + +use tokio::io::{AsyncRead as TokioAsyncRead, AsyncReadExt as _, AsyncWrite as TokioAsyncWrite, AsyncWriteExt as _}; + +async fn bad_async_write_tokio(w: &mut W) { + w.write(b"hello world").await.unwrap(); +} + +async fn bad_async_read_tokio(r: &mut R) { + let mut buf = [0u8; 0]; + r.read(&mut buf[..]).await.unwrap(); +} + +async fn undetected_bad_async_write(w: &mut W) { + // It would be good to detect this case some day, but the current lint + // doesn't handle it. (The documentation says that this lint "detects + // only common patterns".) + let future = w.write(b"Hello world"); + future.await.unwrap(); +} + fn main() {} diff --git a/tests/ui/unused_io_amount.stderr b/tests/ui/unused_io_amount.stderr index d8dfc0e5a798c..3852d0622d6a5 100644 --- a/tests/ui/unused_io_amount.stderr +++ b/tests/ui/unused_io_amount.stderr @@ -1,5 +1,5 @@ error: written amount is not handled. Use `Write::write_all` instead - --> $DIR/unused_io_amount.rs:7:5 + --> $DIR/unused_io_amount.rs:9:5 | LL | s.write(b"test")?; | ^^^^^^^^^^^^^^^^^ @@ -7,55 +7,55 @@ LL | s.write(b"test")?; = note: `-D clippy::unused-io-amount` implied by `-D warnings` error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:9:5 + --> $DIR/unused_io_amount.rs:11:5 | LL | s.read(&mut buf)?; | ^^^^^^^^^^^^^^^^^ error: written amount is not handled. Use `Write::write_all` instead - --> $DIR/unused_io_amount.rs:14:5 + --> $DIR/unused_io_amount.rs:16:5 | LL | s.write(b"test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:16:5 + --> $DIR/unused_io_amount.rs:18:5 | LL | s.read(&mut buf).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: read amount is not handled - --> $DIR/unused_io_amount.rs:20:5 + --> $DIR/unused_io_amount.rs:22:5 | LL | s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: written amount is not handled - --> $DIR/unused_io_amount.rs:21:5 + --> $DIR/unused_io_amount.rs:23:5 | LL | s.write_vectored(&[io::IoSlice::new(&[])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:28:5 + --> $DIR/unused_io_amount.rs:30:5 | LL | reader.read(&mut result).ok()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:37:5 + --> $DIR/unused_io_amount.rs:39:5 | LL | reader.read(&mut result).or_else(|err| Err(err))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:49:5 + --> $DIR/unused_io_amount.rs:51:5 | LL | reader.read(&mut result).or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: read amount is not handled. Use `Read::read_exact` instead - --> $DIR/unused_io_amount.rs:56:5 + --> $DIR/unused_io_amount.rs:58:5 | LL | / reader LL | | .read(&mut result) @@ -64,5 +64,41 @@ LL | | .or(Err(Error::Kind)) LL | | .expect("error"); | |________________________^ -error: aborting due to 10 previous errors +error: written amount is not handled. Use `AsyncWriteExt::write_all` instead + --> $DIR/unused_io_amount.rs:67:5 + | +LL | w.write(b"hello world").await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `AsyncReadExt::read_exact` instead + --> $DIR/unused_io_amount.rs:72:5 + | +LL | r.read(&mut buf[..]).await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: written amount is not handled. Use `AsyncWriteExt::write_all` instead + --> $DIR/unused_io_amount.rs:85:9 + | +LL | w.write(b"hello world").await?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `AsyncReadExt::read_exact` instead + --> $DIR/unused_io_amount.rs:93:9 + | +LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: written amount is not handled. Use `AsyncWriteExt::write_all` instead + --> $DIR/unused_io_amount.rs:101:5 + | +LL | w.write(b"hello world").await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `AsyncReadExt::read_exact` instead + --> $DIR/unused_io_amount.rs:106:5 + | +LL | r.read(&mut buf[..]).await.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors From b6bcf0c51b0d719cfd141c1c010b41ebe74f2abb Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 31 Dec 2021 12:20:02 -0500 Subject: [PATCH 10/72] unused_io_amount: Use span_lint_and_help. This improves the quality of the genrated output and makes it more in line with other lint messages. changelog: [`unused_io_amount`]: Improve help text --- clippy_lints/src/unused_io_amount.rs | 26 ++++++++----- tests/ui/unused_io_amount.stderr | 55 +++++++++++++++++++++------- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 8afd8472db7ab..287ac5b4a9083 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::{is_try, match_trait_method, paths}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -126,32 +126,40 @@ fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Exp }; match (read_trait, write_trait, symbol, is_await) { - (true, _, "read", false) => span_lint( + (true, _, "read", false) => span_lint_and_help( cx, UNUSED_IO_AMOUNT, expr.span, - "read amount is not handled. Use `Read::read_exact` instead", + "read amount is not handled", + None, + "use `Read::read_exact` instead, or handle partial reads", ), - (true, _, "read", true) => span_lint( + (true, _, "read", true) => span_lint_and_help( cx, UNUSED_IO_AMOUNT, expr.span, - "read amount is not handled. Use `AsyncReadExt::read_exact` instead", + "read amount is not handled", + None, + "use `AsyncReadExt::read_exact` instead, or handle partial reads", ), (true, _, "read_vectored", _) => { span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"); }, - (_, true, "write", false) => span_lint( + (_, true, "write", false) => span_lint_and_help( cx, UNUSED_IO_AMOUNT, expr.span, - "written amount is not handled. Use `Write::write_all` instead", + "written amount is not handled", + None, + "use `Write::write_all` instead, or handle partial writes", ), - (_, true, "write", true) => span_lint( + (_, true, "write", true) => span_lint_and_help( cx, UNUSED_IO_AMOUNT, expr.span, - "written amount is not handled. Use `AsyncWriteExt::write_all` instead", + "written amount is not handled", + None, + "use `AsyncWriteExt::write_all` instead, or handle partial writes", ), (_, true, "write_vectored", _) => { span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"); diff --git a/tests/ui/unused_io_amount.stderr b/tests/ui/unused_io_amount.stderr index 3852d0622d6a5..e5bdd993aa1ad 100644 --- a/tests/ui/unused_io_amount.stderr +++ b/tests/ui/unused_io_amount.stderr @@ -1,28 +1,35 @@ -error: written amount is not handled. Use `Write::write_all` instead +error: written amount is not handled --> $DIR/unused_io_amount.rs:9:5 | LL | s.write(b"test")?; | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unused-io-amount` implied by `-D warnings` + = help: use `Write::write_all` instead, or handle partial writes -error: read amount is not handled. Use `Read::read_exact` instead +error: read amount is not handled --> $DIR/unused_io_amount.rs:11:5 | LL | s.read(&mut buf)?; | ^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: written amount is not handled. Use `Write::write_all` instead +error: written amount is not handled --> $DIR/unused_io_amount.rs:16:5 | LL | s.write(b"test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes -error: read amount is not handled. Use `Read::read_exact` instead +error: read amount is not handled --> $DIR/unused_io_amount.rs:18:5 | LL | s.read(&mut buf).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled --> $DIR/unused_io_amount.rs:22:5 @@ -36,25 +43,31 @@ error: written amount is not handled LL | s.write_vectored(&[io::IoSlice::new(&[])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: read amount is not handled. Use `Read::read_exact` instead +error: read amount is not handled --> $DIR/unused_io_amount.rs:30:5 | LL | reader.read(&mut result).ok()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: read amount is not handled. Use `Read::read_exact` instead +error: read amount is not handled --> $DIR/unused_io_amount.rs:39:5 | LL | reader.read(&mut result).or_else(|err| Err(err))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: read amount is not handled. Use `Read::read_exact` instead +error: read amount is not handled --> $DIR/unused_io_amount.rs:51:5 | LL | reader.read(&mut result).or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: read amount is not handled. Use `Read::read_exact` instead +error: read amount is not handled --> $DIR/unused_io_amount.rs:58:5 | LL | / reader @@ -63,42 +76,56 @@ LL | | .or(Err(Error::Kind)) LL | | .or(Err(Error::Kind)) LL | | .expect("error"); | |________________________^ + | + = help: use `Read::read_exact` instead, or handle partial reads -error: written amount is not handled. Use `AsyncWriteExt::write_all` instead +error: written amount is not handled --> $DIR/unused_io_amount.rs:67:5 | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncWriteExt::write_all` instead, or handle partial writes -error: read amount is not handled. Use `AsyncReadExt::read_exact` instead +error: read amount is not handled --> $DIR/unused_io_amount.rs:72:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncReadExt::read_exact` instead, or handle partial reads -error: written amount is not handled. Use `AsyncWriteExt::write_all` instead +error: written amount is not handled --> $DIR/unused_io_amount.rs:85:9 | LL | w.write(b"hello world").await?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncWriteExt::write_all` instead, or handle partial writes -error: read amount is not handled. Use `AsyncReadExt::read_exact` instead +error: read amount is not handled --> $DIR/unused_io_amount.rs:93:9 | LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncReadExt::read_exact` instead, or handle partial reads -error: written amount is not handled. Use `AsyncWriteExt::write_all` instead +error: written amount is not handled --> $DIR/unused_io_amount.rs:101:5 | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncWriteExt::write_all` instead, or handle partial writes -error: read amount is not handled. Use `AsyncReadExt::read_exact` instead +error: read amount is not handled --> $DIR/unused_io_amount.rs:106:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: aborting due to 16 previous errors From 3d41358a554cb70e23e001f6ac92cf79d805b671 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 31 Dec 2021 23:39:40 -0500 Subject: [PATCH 11/72] wrong_self_convention: Match `SelfKind::No` more restrictively The `wrong_self_convention` lint uses a `SelfKind` type to decide whether a method has the right kind of "self" for its name, or whether the kind of "self" it has makes its name confusable for a method in a common trait. One possibility is `SelfKind::No`, which is supposed to mean "No `self`". Previously, SelfKind::No matched everything _except_ Self, including references to Self. This patch changes it to match Self, &Self, &mut Self, Box, and so on. For example, this kind of method was allowed before: ``` impl S { // Should trigger the lint, because // "methods called `is_*` usually take `self` by reference or no `self`" fn is_foo(&mut self) -> bool { todo!() } } ``` But since SelfKind::No matched "&mut self", no lint was triggered (see #8142). With this patch, the code above now gives a lint as expected. Fixes #8142 changelog: [`wrong_self_convention`] rejects `self` references in more cases --- clippy_lints/src/methods/mod.rs | 8 +++++++- clippy_lints/src/redundant_clone.rs | 4 ++-- tests/ui/issue_4266.stderr | 11 ++++++++++- tests/ui/wrong_self_convention.rs | 21 +++++++++++++++++++++ tests/ui/wrong_self_convention.stderr | 10 +++++++++- 5 files changed, 49 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4e33b2ff14cde..8a2a468c85233 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2535,11 +2535,17 @@ impl SelfKind { implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) } + fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + !matches_value(cx, parent_ty, ty) + && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty) + && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty) + } + match self { Self::Value => matches_value(cx, parent_ty, ty), Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty), - Self::No => ty != parent_ty, + Self::No => matches_none(cx, parent_ty, ty), } } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 1cf349f8aa7c7..1991a01fb60be 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc { // cloned value is mutated, and the clone is alive. - if possible_borrower.is_alive_at(ret_local, loc) { + if possible_borrower.local_is_alive_at(ret_local, loc) { continue; } } @@ -767,7 +767,7 @@ impl PossibleBorrowerMap<'_, '_> { self.bitset.0 == self.bitset.1 } - fn is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { + fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { self.maybe_live.seek_after_primary_effect(at); self.maybe_live.contains(local) } diff --git a/tests/ui/issue_4266.stderr b/tests/ui/issue_4266.stderr index 20419457b47f0..e5042aaa776b4 100644 --- a/tests/ui/issue_4266.stderr +++ b/tests/ui/issue_4266.stderr @@ -12,5 +12,14 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: methods called `new` usually take no `self` + --> $DIR/issue_4266.rs:27:22 + | +LL | pub async fn new(&mut self) -> Self { + | ^^^^^^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name + +error: aborting due to 3 previous errors diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index 1b9da8a55e53f..f8fee4b3ab2d8 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -188,3 +188,24 @@ mod issue6727 { } } } + +pub mod issue8142 { + struct S; + + impl S { + // Should lint: is_ methods should only take &self, or no self at all. + fn is_still_buggy(&mut self) -> bool { + false + } + + // Should not lint: "no self at all" is allowed. + fn is_forty_two(x: u32) -> bool { + x == 42 + } + + // Should not lint: &self is allowed. + fn is_test_code(&self) -> bool { + true + } + } +} diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 590ee6d9c529d..5493a99572e06 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -191,5 +191,13 @@ LL | fn to_u64(self) -> u64 { | = help: consider choosing a less ambiguous name -error: aborting due to 24 previous errors +error: methods called `is_*` usually take `self` by reference or no `self` + --> $DIR/wrong_self_convention.rs:197:27 + | +LL | fn is_still_buggy(&mut self) -> bool { + | ^^^^^^^^^ + | + = help: consider choosing a less ambiguous name + +error: aborting due to 25 previous errors From f5bbd1b5299da31532d00abb0502feb5b60f2606 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 31 Dec 2021 21:13:07 -0800 Subject: [PATCH 12/72] Make tidy check for magic numbers that spell things Remove existing problematic cases. --- tests/ui/unreadable_literal.fixed | 2 +- tests/ui/unreadable_literal.rs | 2 +- tests/ui/unreadable_literal.stderr | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/ui/unreadable_literal.fixed b/tests/ui/unreadable_literal.fixed index c2e38037addd2..e726b652ef1ed 100644 --- a/tests/ui/unreadable_literal.fixed +++ b/tests/ui/unreadable_literal.fixed @@ -30,7 +30,7 @@ fn main() { 1_234.123_f32, 1.123_4_f32, ); - let _bad = (0b11_0110_i64, 0xcafe_babe_usize, 123_456_f32, 1.234_567_f32); + let _bad = (0b11_0110_i64, 0x1234_5678_usize, 123_456_f32, 1.234_567_f32); let _good_sci = 1.1234e1; let _bad_sci = 1.123_456e1; diff --git a/tests/ui/unreadable_literal.rs b/tests/ui/unreadable_literal.rs index 8296945b25eb4..5bbb2fc9dc137 100644 --- a/tests/ui/unreadable_literal.rs +++ b/tests/ui/unreadable_literal.rs @@ -30,7 +30,7 @@ fn main() { 1_234.123_f32, 1.123_4_f32, ); - let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); + let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32); let _good_sci = 1.1234e1; let _bad_sci = 1.123456e1; diff --git a/tests/ui/unreadable_literal.stderr b/tests/ui/unreadable_literal.stderr index 8436aac17acfe..ee5466fd517fd 100644 --- a/tests/ui/unreadable_literal.stderr +++ b/tests/ui/unreadable_literal.stderr @@ -9,7 +9,7 @@ LL | 0x1_234_567, error: long literal lacking separators --> $DIR/unreadable_literal.rs:33:17 | -LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); +LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^^^ help: consider: `0b11_0110_i64` | = note: `-D clippy::unreadable-literal` implied by `-D warnings` @@ -17,19 +17,19 @@ LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); error: long literal lacking separators --> $DIR/unreadable_literal.rs:33:31 | -LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); - | ^^^^^^^^^^^^^^^^ help: consider: `0xcafe_babe_usize` +LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32); + | ^^^^^^^^^^^^^^^^ help: consider: `0x1234_5678_usize` error: long literal lacking separators --> $DIR/unreadable_literal.rs:33:49 | -LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); +LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^ help: consider: `123_456_f32` error: long literal lacking separators --> $DIR/unreadable_literal.rs:33:61 | -LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); +LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^^^ help: consider: `1.234_567_f32` error: long literal lacking separators From 262b148d8830efa4773f05b27403f94e47d27f52 Mon Sep 17 00:00:00 2001 From: bors Date: Sat, 1 Jan 2022 13:16:49 +0000 Subject: [PATCH 13/72] return_self_not_must_use document `#[must_use]` on the type Inspired by a discussion in rust-lang/rust-clippy#8197 --- r? `@llogiq` changelog: none The lint is this on nightly, therefore no changelog entry for you xD --- clippy_lints/src/return_self_not_must_use.rs | 36 ++++++++++++++++---- tests/ui/return_self_not_must_use.stderr | 5 +++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index b57ec96bc7e6d..935bbc37d75d0 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -1,5 +1,6 @@ +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_must_use_ty; -use clippy_utils::{diagnostics::span_lint, nth_arg, return_ty}; +use clippy_utils::{nth_arg, return_ty}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind}; @@ -13,25 +14,46 @@ declare_clippy_lint! { /// This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute. /// /// ### Why is this bad? - /// It prevents to "forget" to use the newly created value. + /// Methods returning `Self` often create new values, having the `#[must_use]` attribute + /// prevents users from "forgetting" to use the newly created value. + /// + /// The `#[must_use]` attribute can be added to the type itself to ensure that instances + /// are never forgotten. Functions returning a type marked with `#[must_use]` will not be + /// linted, as the usage is already enforced by the type attribute. /// /// ### Limitations /// This lint is only applied on methods taking a `self` argument. It would be mostly noise /// if it was added on constructors for example. /// /// ### Example + /// Missing attribute /// ```rust /// pub struct Bar; - /// /// impl Bar { /// // Bad /// pub fn bar(&self) -> Self { /// Self /// } + /// } + /// ``` /// - /// // Good + /// It's better to have the `#[must_use]` attribute on the method like this: + /// ```rust + /// pub struct Bar; + /// impl Bar { /// #[must_use] - /// pub fn foo(&self) -> Self { + /// pub fn bar(&self) -> Self { + /// Self + /// } + /// } + /// ``` + /// + /// Or on the type definition like this: + /// ```rust + /// #[must_use] + /// pub struct Bar; + /// impl Bar { + /// pub fn bar(&self) -> Self { /// Self /// } /// } @@ -65,11 +87,13 @@ fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalD if !is_must_use_ty(cx, ret_ty); then { - span_lint( + span_lint_and_help( cx, RETURN_SELF_NOT_MUST_USE, span, "missing `#[must_use]` attribute on a method returning `Self`", + None, + "consider adding the `#[must_use]` attribute to the method or directly to the `Self` type" ); } } diff --git a/tests/ui/return_self_not_must_use.stderr b/tests/ui/return_self_not_must_use.stderr index 3793a5559ba55..8af10cb65c406 100644 --- a/tests/ui/return_self_not_must_use.stderr +++ b/tests/ui/return_self_not_must_use.stderr @@ -5,6 +5,7 @@ LL | fn what(&self) -> Self; | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::return-self-not-must-use` implied by `-D warnings` + = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type error: missing `#[must_use]` attribute on a method returning `Self` --> $DIR/return_self_not_must_use.rs:17:5 @@ -13,6 +14,8 @@ LL | / pub fn foo(&self) -> Self { LL | | Self LL | | } | |_____^ + | + = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type error: missing `#[must_use]` attribute on a method returning `Self` --> $DIR/return_self_not_must_use.rs:20:5 @@ -21,6 +24,8 @@ LL | / pub fn bar(self) -> Self { LL | | self LL | | } | |_____^ + | + = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type error: aborting due to 3 previous errors From e8b6b2ac0c115e7208d725b4f6340e68194d0cf3 Mon Sep 17 00:00:00 2001 From: Wigy Date: Fri, 31 Dec 2021 09:37:39 +0100 Subject: [PATCH 14/72] erasing_op lint ignored when output type is different from the non-const one --- clippy_lints/src/erasing_op.rs | 26 +++++++++++++++++++------- tests/ui/erasing_op.rs | 34 ++++++++++++++++++++++++++++++++++ tests/ui/erasing_op.stderr | 20 ++++++++++++++++---- tests/ui/identity_op.rs | 13 +++++++++++++ tests/ui/identity_op.stderr | 26 +++++++++++++------------- 5 files changed, 95 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/erasing_op.rs b/clippy_lints/src/erasing_op.rs index d49cec26be5f0..bb6acd8c5dd4b 100644 --- a/clippy_lints/src/erasing_op.rs +++ b/clippy_lints/src/erasing_op.rs @@ -1,9 +1,11 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::same_type_and_consts; + use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TypeckResults; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; declare_clippy_lint! { /// ### What it does @@ -35,24 +37,34 @@ impl<'tcx> LateLintPass<'tcx> for ErasingOp { return; } if let ExprKind::Binary(ref cmp, left, right) = e.kind { + let tck = cx.typeck_results(); match cmp.node { BinOpKind::Mul | BinOpKind::BitAnd => { - check(cx, left, e.span); - check(cx, right, e.span); + check(cx, tck, left, right, e); + check(cx, tck, right, left, e); }, - BinOpKind::Div => check(cx, left, e.span), + BinOpKind::Div => check(cx, tck, left, right, e), _ => (), } } } } -fn check(cx: &LateContext<'_>, e: &Expr<'_>, span: Span) { - if constant_simple(cx, cx.typeck_results(), e) == Some(Constant::Int(0)) { +fn different_types(tck: &TypeckResults<'tcx>, input: &'tcx Expr<'_>, output: &'tcx Expr<'_>) -> bool { + let input_ty = tck.expr_ty(input).peel_refs(); + let output_ty = tck.expr_ty(output).peel_refs(); + !same_type_and_consts(input_ty, output_ty) +} + +fn check(cx: &LateContext<'cx>, tck: &TypeckResults<'cx>, op: &Expr<'cx>, other: &Expr<'cx>, parent: &Expr<'cx>) { + if constant_simple(cx, tck, op) == Some(Constant::Int(0)) { + if different_types(tck, other, parent) { + return; + } span_lint( cx, ERASING_OP, - span, + parent.span, "this operation will always return zero. This is likely not the intended outcome", ); } diff --git a/tests/ui/erasing_op.rs b/tests/ui/erasing_op.rs index 1540062a4bc3e..ae2fad0086daa 100644 --- a/tests/ui/erasing_op.rs +++ b/tests/ui/erasing_op.rs @@ -1,3 +1,34 @@ +struct Length(u8); +struct Meter; + +impl core::ops::Mul for u8 { + type Output = Length; + fn mul(self, _: Meter) -> Length { + Length(self) + } +} + +#[derive(Clone, Default, PartialEq, Eq, Hash)] +struct Vec1 { + x: i32, +} + +impl core::ops::Mul for i32 { + type Output = Vec1; + fn mul(self, mut right: Vec1) -> Vec1 { + right.x *= self; + right + } +} + +impl core::ops::Mul for Vec1 { + type Output = Vec1; + fn mul(mut self, right: i32) -> Vec1 { + self.x *= right; + self + } +} + #[allow(clippy::no_effect)] #[warn(clippy::erasing_op)] fn main() { @@ -6,4 +37,7 @@ fn main() { x * 0; 0 & x; 0 / x; + 0 * Meter; // no error: Output type is different from the non-zero argument + 0 * Vec1 { x: 5 }; + Vec1 { x: 5 } * 0; } diff --git a/tests/ui/erasing_op.stderr b/tests/ui/erasing_op.stderr index e54ce85f98ec7..165ed9bfe58b1 100644 --- a/tests/ui/erasing_op.stderr +++ b/tests/ui/erasing_op.stderr @@ -1,5 +1,5 @@ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:6:5 + --> $DIR/erasing_op.rs:37:5 | LL | x * 0; | ^^^^^ @@ -7,16 +7,28 @@ LL | x * 0; = note: `-D clippy::erasing-op` implied by `-D warnings` error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:7:5 + --> $DIR/erasing_op.rs:38:5 | LL | 0 & x; | ^^^^^ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:8:5 + --> $DIR/erasing_op.rs:39:5 | LL | 0 / x; | ^^^^^ -error: aborting due to 3 previous errors +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/erasing_op.rs:41:5 + | +LL | 0 * Vec1 { x: 5 }; + | ^^^^^^^^^^^^^^^^^ + +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/erasing_op.rs:42:5 + | +LL | Vec1 { x: 5 } * 0; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index 2ed4b5db574d4..12bbda71f4348 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -11,6 +11,17 @@ impl std::ops::Shl for A { self } } + +struct Length(u8); +struct Meter; + +impl core::ops::Mul for u8 { + type Output = Length; + fn mul(self, _: Meter) -> Length { + Length(self) + } +} + #[allow( clippy::eq_op, clippy::no_effect, @@ -53,4 +64,6 @@ fn main() { let mut a = A("".into()); let b = a << 0; // no error: non-integer + + 1 * Meter; // no error: non-integer } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index ff34b38db015b..0103cf5457e81 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -1,5 +1,5 @@ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:26:5 + --> $DIR/identity_op.rs:37:5 | LL | x + 0; | ^^^^^ @@ -7,73 +7,73 @@ LL | x + 0; = note: `-D clippy::identity-op` implied by `-D warnings` error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:27:5 + --> $DIR/identity_op.rs:38:5 | LL | x + (1 - 1); | ^^^^^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:29:5 + --> $DIR/identity_op.rs:40:5 | LL | 0 + x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:32:5 + --> $DIR/identity_op.rs:43:5 | LL | x | (0); | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:35:5 + --> $DIR/identity_op.rs:46:5 | LL | x * 1; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:36:5 + --> $DIR/identity_op.rs:47:5 | LL | 1 * x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:42:5 + --> $DIR/identity_op.rs:53:5 | LL | -1 & x; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `u` - --> $DIR/identity_op.rs:45:5 + --> $DIR/identity_op.rs:56:5 | LL | u & 255; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:48:5 + --> $DIR/identity_op.rs:59:5 | LL | 42 << 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:49:5 + --> $DIR/identity_op.rs:60:5 | LL | 1 >> 0; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:50:5 + --> $DIR/identity_op.rs:61:5 | LL | 42 >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `&x` - --> $DIR/identity_op.rs:51:5 + --> $DIR/identity_op.rs:62:5 | LL | &x >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:52:5 + --> $DIR/identity_op.rs:63:5 | LL | x >> &0; | ^^^^^^^ From 19cfcd5c5a604523fd58c844f90f149021b0fb51 Mon Sep 17 00:00:00 2001 From: Piotr Mikulski Date: Mon, 3 Jan 2022 06:11:11 -0800 Subject: [PATCH 15/72] Fix clippy warnings --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 7ebdd947893e9..240e233420f0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -96,7 +96,7 @@ impl ClippyCmd { clippy_args.push("--no-deps".into()); } - ClippyCmd { + Self { cargo_subcommand, args, clippy_args, From ff58efb2b26e395f2887dad2f904aee26da3dc13 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 3 Jan 2022 23:13:31 -0500 Subject: [PATCH 16/72] Better detect when a field can be moved from in `while_let_on_iterator` --- .../src/loops/while_let_on_iterator.rs | 35 ++++++++++++++----- tests/ui/while_let_on_iterator.fixed | 30 ++++++++++++++++ tests/ui/while_let_on_iterator.rs | 30 ++++++++++++++++ tests/ui/while_let_on_iterator.stderr | 16 +++++++-- 4 files changed, 100 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index b390476a664d9..942d6ca6b4a3a 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -8,12 +8,13 @@ use clippy_utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; -use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, PatKind, QPath, UnOp}; +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_span::{symbol::sym, Span, Symbol}; +use rustc_middle::ty::adjustment::Adjust; +use rustc_span::{symbol::sym, Symbol}; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! { + let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! { if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr); // check for `Some(..)` pattern if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind; @@ -27,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // get the loop containing the match expression if !uses_iter(cx, &iter_expr_struct, if_then); then { - (let_expr, iter_expr_struct, some_pat, expr) + (let_expr, iter_expr_struct, iter_expr, some_pat, expr) } else { return; } @@ -47,7 +48,11 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used // afterwards a mutable borrow of a field isn't necessary. - let by_ref = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) { + let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut) + || !iter_expr_struct.can_move + || !iter_expr_struct.fields.is_empty() + || needs_mutable_borrow(cx, &iter_expr_struct, loop_expr) + { ".by_ref()" } else { "" @@ -67,26 +72,36 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { #[derive(Debug)] struct IterExpr { - /// The span of the whole expression, not just the path and fields stored here. - span: Span, /// The fields used, in order of child to parent. fields: Vec, /// The path being used. path: Res, + /// Whether or not the iterator can be moved. + can_move: bool, } /// Parses any expression to find out which field of which variable is used. Will return `None` if /// the expression might have side effects. fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option { - let span = e.span; let mut fields = Vec::new(); + let mut can_move = true; loop { + if cx + .typeck_results() + .expr_adjustments(e) + .iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + { + // Custom deref impls need to borrow the whole value as it's captured by reference + can_move = false; + fields.clear(); + } match e.kind { ExprKind::Path(ref path) => { break Some(IterExpr { - span, fields, path: cx.qpath_res(path, e.hir_id), + can_move, }); }, ExprKind::Field(base, name) => { @@ -99,10 +114,12 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option { + can_move = false; fields.clear(); e = base; }, ExprKind::Unary(UnOp::Deref, base) => { + can_move = false; fields.clear(); e = base; }, diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index 1e74ad2de655a..cb8892a3f009b 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -372,6 +372,36 @@ fn exact_match_with_single_field() { } } +fn custom_deref() { + struct S1 { + x: T, + } + struct S2(S1); + impl core::ops::Deref for S2 { + type Target = S1; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl core::ops::DerefMut for S2 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + let mut s = S2(S1 { x: 0..10 }); + for x in s.x.by_ref() { + println!("{}", x); + } +} + +fn issue_8113() { + let mut x = [0..10]; + for x in x[0].by_ref() { + println!("{}", x); + } +} + fn main() { let mut it = 0..20; for _ in it { diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 69cb636cee826..d91571844959e 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -372,6 +372,36 @@ fn exact_match_with_single_field() { } } +fn custom_deref() { + struct S1 { + x: T, + } + struct S2(S1); + impl core::ops::Deref for S2 { + type Target = S1; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl core::ops::DerefMut for S2 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + let mut s = S2(S1 { x: 0..10 }); + while let Some(x) = s.x.next() { + println!("{}", x); + } +} + +fn issue_8113() { + let mut x = [0..10]; + while let Some(x) = x[0].next() { + println!("{}", x); + } +} + fn main() { let mut it = 0..20; while let Some(..) = it.next() { diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 1a11ba26eef0f..fb2b0f2467c0d 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -123,10 +123,22 @@ LL | while let Some(x) = it.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:377:5 + --> $DIR/while_let_on_iterator.rs:393:5 + | +LL | while let Some(x) = s.x.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:400:5 + | +LL | while let Some(x) = x[0].next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:407:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` -error: aborting due to 21 previous errors +error: aborting due to 23 previous errors From 00da1b8f14989b57e56ed28da177b5819a2b53f9 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 4 Jan 2022 11:58:39 -0600 Subject: [PATCH 17/72] Remove in_macro from utils --- clippy_lints/src/init_numbered_fields.rs | 3 +-- clippy_lints/src/macro_use.rs | 6 +++++- clippy_utils/src/lib.rs | 7 ------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs index 5fe6725b581dc..9284e00240992 100644 --- a/clippy_lints/src/init_numbered_fields.rs +++ b/clippy_lints/src/init_numbered_fields.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::in_macro; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -46,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Struct(path, fields, None) = e.kind { if !fields.is_empty() - && !in_macro(e.span) + && !e.span.from_expansion() && fields .iter() .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index bbc14fc535dac..262a26951c681 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::in_macro; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; use if_chain::if_chain; @@ -9,6 +8,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::hygiene::ExpnKind; use rustc_span::{edition::Edition, sym, Span}; declare_clippy_lint! { @@ -214,3 +214,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { } } } + +fn in_macro(span: Span) -> bool { + span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 54d470ca73820..c6bf96d519314 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -147,13 +147,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] From a7097b80c3b864d0558fec91e4097de6af972746 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 3 Jan 2022 22:13:52 -0500 Subject: [PATCH 18/72] Consider auto-deref when linting `manual_swap` --- clippy_utils/src/lib.rs | 21 +++++++++++++++++++++ tests/ui/swap.fixed | 33 +++++++++++++++++++++++++++++++++ tests/ui/swap.rs | 35 +++++++++++++++++++++++++++++++++++ tests/ui/swap.stderr | 12 +++++++++++- 4 files changed, 100 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 54d470ca73820..8c9e229359781 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -621,6 +621,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 { + 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 { @@ -629,7 +642,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 { diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed index ef518359ec5f3..3329efbd4ff42 100644 --- a/tests/ui/swap.fixed +++ b/tests/ui/swap.fixed @@ -122,3 +122,36 @@ fn main() { ; std::mem::swap(&mut c.0, &mut a); } + +fn issue_8154() { + struct S1 { + x: i32, + y: i32, + } + struct S2(S1); + struct S3<'a, 'b>(&'a mut &'b mut S1); + + impl std::ops::Deref for S2 { + type Target = S1; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for S2 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + // Don't lint. `s.0` is mutably borrowed by `s.x` and `s.y` via the deref impl. + let mut s = S2(S1 { x: 0, y: 0 }); + let t = s.x; + s.x = s.y; + s.y = t; + + // Accessing through a mutable reference is fine + let mut s = S1 { x: 0, y: 0 }; + let mut s = &mut s; + let s = S3(&mut s); + std::mem::swap(&mut s.0.x, &mut s.0.y); +} diff --git a/tests/ui/swap.rs b/tests/ui/swap.rs index 8518659ccf316..8179ac1f2ab02 100644 --- a/tests/ui/swap.rs +++ b/tests/ui/swap.rs @@ -144,3 +144,38 @@ fn main() { c.0 = a; a = t; } + +fn issue_8154() { + struct S1 { + x: i32, + y: i32, + } + struct S2(S1); + struct S3<'a, 'b>(&'a mut &'b mut S1); + + impl std::ops::Deref for S2 { + type Target = S1; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for S2 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + // Don't lint. `s.0` is mutably borrowed by `s.x` and `s.y` via the deref impl. + let mut s = S2(S1 { x: 0, y: 0 }); + let t = s.x; + s.x = s.y; + s.y = t; + + // Accessing through a mutable reference is fine + let mut s = S1 { x: 0, y: 0 }; + let mut s = &mut s; + let s = S3(&mut s); + let t = s.0.x; + s.0.x = s.0.y; + s.0.y = t; +} diff --git a/tests/ui/swap.stderr b/tests/ui/swap.stderr index 614d16ced40f1..2b556b475cee9 100644 --- a/tests/ui/swap.stderr +++ b/tests/ui/swap.stderr @@ -108,5 +108,15 @@ LL | | a = c.0; | = note: or maybe you should use `std::mem::replace`? -error: aborting due to 12 previous errors +error: this looks like you are swapping `s.0.x` and `s.0.y` manually + --> $DIR/swap.rs:178:5 + | +LL | / let t = s.0.x; +LL | | s.0.x = s.0.y; +LL | | s.0.y = t; + | |_____________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y)` + | + = note: or maybe you should use `std::mem::replace`? + +error: aborting due to 13 previous errors From 2dd216a186bc3b0cea9b2afe889f99aa9f0a5d9f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 4 Jan 2022 14:20:27 -0500 Subject: [PATCH 19/72] Check for full equality in `type_repetition_in_bounds` rather than just equal hashes --- clippy_lints/src/trait_bounds.rs | 39 +++++++++++++++++++-------- clippy_utils/src/hir_utils.rs | 4 ++- tests/ui/type_repetition_in_bounds.rs | 10 +++++++ 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index fb4abceac25e2..f2848ad379093 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::SpanlessHash; +use clippy_utils::{SpanlessEq, SpanlessHash}; +use core::hash::{Hash, Hasher}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; -use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate}; +use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, Ty, TyKind, WherePredicate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -94,24 +95,40 @@ fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span) } impl TraitBounds { - fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) { + fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { + struct SpanlessTy<'cx, 'tcx> { + ty: &'tcx Ty<'tcx>, + cx: &'cx LateContext<'tcx>, + } + impl PartialEq for SpanlessTy<'_, '_> { + fn eq(&self, other: &Self) -> bool { + let mut eq = SpanlessEq::new(self.cx); + eq.inter_expr().eq_ty(self.ty, other.ty) + } + } + impl Hash for SpanlessTy<'_, '_> { + fn hash(&self, h: &mut H) { + let mut t = SpanlessHash::new(self.cx); + t.hash_ty(self.ty); + h.write_u64(t.finish()); + } + } + impl Eq for SpanlessTy<'_, '_> {} + if gen.span.from_expansion() { return; } - let hash = |ty| -> u64 { - let mut hasher = SpanlessHash::new(cx); - hasher.hash_ty(ty); - hasher.finish() - }; - let mut map: UnhashMap>> = UnhashMap::default(); + let mut map: UnhashMap, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in gen.where_clause.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref p) = bound; if p.bounds.len() as u64 <= self.max_trait_bounds; if !p.span.from_expansion(); - let h = hash(p.bounded_ty); - if let Some(ref v) = map.insert(h, p.bounds.iter().collect::>()); + if let Some(ref v) = map.insert( + SpanlessTy { ty: p.bounded_ty, cx }, + p.bounds.iter().collect::>() + ); then { let mut hint_string = format!( diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index ad50759effaf8..9df296beb60c9 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -388,7 +388,7 @@ 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, ref ll_id), &TyKind::Array(rt, ref rl_id)) => { @@ -845,6 +845,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 { diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index 766190f209977..fc740ee11d6ab 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -69,4 +69,14 @@ mod issue4326 { } } +// Issue #7360 +struct Foo +where + T: Clone, + U: Clone, +{ + t: T, + u: U, +} + fn main() {} From 786f874c349b995ebed5e3d8f20db1cf65f20782 Mon Sep 17 00:00:00 2001 From: bors Date: Tue, 4 Jan 2022 22:32:02 +0000 Subject: [PATCH 20/72] New macro utils changelog: none Sorry, this is a big one. A lot of interrelated changes and I wanted to put the new utils to use to make sure they are somewhat battle-tested. We may want to divide some of the lint-specific refactoring commits into batches for smaller reviewing tasks. I could also split into more PRs. Introduces a bunch of new utils at `clippy_utils::macros::...`. Please read through the docs and give any feedback! I'm happy to introduce `MacroCall` and various functions to retrieve an instance. It feels like the missing puzzle piece. I'm also introducing `ExpnId` from rustc as "useful for Clippy too". `@rust-lang/clippy` Fixes #7843 by not parsing every node of macro implementations, at least the major offenders. I probably want to get rid of `is_expn_of` at some point. --- clippy_lints/src/assertions_on_constants.rs | 120 +--- clippy_lints/src/attrs.rs | 18 +- clippy_lints/src/bool_assert_comparison.rs | 73 ++- clippy_lints/src/doc.rs | 33 +- clippy_lints/src/eq_op.rs | 45 +- clippy_lints/src/explicit_write.rs | 6 +- clippy_lints/src/fallible_impl_from.rs | 15 +- clippy_lints/src/format.rs | 33 +- clippy_lints/src/format_args.rs | 53 +- clippy_lints/src/manual_assert.rs | 68 +-- clippy_lints/src/matches.rs | 24 +- clippy_lints/src/methods/expect_fun_call.rs | 21 +- clippy_lints/src/mutable_debug_assertion.rs | 42 +- clippy_lints/src/panic_in_result_fn.rs | 32 +- clippy_lints/src/panic_unimplemented.rs | 56 +- clippy_lints/src/unit_types/unit_cmp.rs | 40 +- clippy_lints/src/utils/internal_lints.rs | 11 +- clippy_utils/Cargo.toml | 1 + clippy_utils/src/ast_utils.rs | 32 -- clippy_utils/src/higher.rs | 295 +--------- clippy_utils/src/lib.rs | 40 +- clippy_utils/src/macros.rs | 539 ++++++++++++++++++ clippy_utils/src/paths.rs | 5 - tests/ui/assertions_on_constants.rs | 1 - tests/ui/assertions_on_constants.stderr | 42 +- tests/ui/eq_op_macros.stderr | 44 +- tests/ui/missing_panics_doc.stderr | 6 - tests/ui/panic_in_result_fn.stderr | 6 - tests/ui/panic_in_result_fn_assertions.stderr | 3 - tests/ui/panicking_macros.stderr | 21 - tests/ui/unit_cmp.stderr | 8 - 31 files changed, 868 insertions(+), 865 deletions(-) create mode 100644 clippy_utils/src/macros.rs diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index b7f414742f156..c82837746bd5d 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,12 +1,10 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::higher; -use clippy_utils::source::snippet_opt; -use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call, peel_blocks}; -use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind, UnOp}; +use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; +use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -36,107 +34,39 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - let lint_true = |is_debug: bool| { - span_lint_and_help( - cx, - ASSERTIONS_ON_CONSTANTS, - e.span, - if is_debug { - "`debug_assert!(true)` will be optimized out by the compiler" - } else { - "`assert!(true)` will be optimized out by the compiler" - }, - None, - "remove it", - ); + let Some(macro_call) = root_macro_call_first_node(cx, e) else { return }; + let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::debug_assert_macro) => true, + Some(sym::assert_macro) => false, + _ => return, }; - let lint_false_without_message = || { + let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return }; + let Some((Constant::Bool(val), _)) = constant(cx, cx.typeck_results(), condition) else { return }; + if val { span_lint_and_help( cx, ASSERTIONS_ON_CONSTANTS, - e.span, - "`assert!(false)` should probably be replaced", + macro_call.span, + &format!( + "`{}!(true)` will be optimized out by the compiler", + cx.tcx.item_name(macro_call.def_id) + ), None, - "use `panic!()` or `unreachable!()`", + "remove it", ); - }; - let lint_false_with_message = |panic_message: String| { + } else if !is_debug { + let (assert_arg, panic_arg) = match panic_expn { + PanicExpn::Empty => ("", ""), + _ => (", ..", ".."), + }; span_lint_and_help( cx, ASSERTIONS_ON_CONSTANTS, - e.span, - &format!("`assert!(false, {})` should probably be replaced", panic_message), + macro_call.span, + &format!("`assert!(false{})` should probably be replaced", assert_arg), None, - &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message), + &format!("use `panic!({})` or `unreachable!({0})`", panic_arg), ); - }; - - if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") { - if debug_assert_span.from_expansion() { - return; - } - if_chain! { - if let ExprKind::Unary(_, lit) = e.kind; - if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit); - if is_true; - then { - lint_true(true); - } - }; - } else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") { - if assert_span.from_expansion() { - return; - } - if let Some(assert_match) = match_assert_with_message(cx, e) { - match assert_match { - // matched assert but not message - AssertKind::WithoutMessage(false) => lint_false_without_message(), - AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false), - AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message), - }; - } - } - } -} - -/// Result of calling `match_assert_with_message`. -enum AssertKind { - WithMessage(String, bool), - WithoutMessage(bool), -} - -/// Check if the expression matches -/// -/// ```rust,ignore -/// if !c { -/// { -/// ::std::rt::begin_panic(message, _) -/// } -/// } -/// ``` -/// -/// where `message` is any expression and `c` is a constant bool. -fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { - if_chain! { - if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); - if let ExprKind::Unary(UnOp::Not, expr) = cond.kind; - // bind the first argument of the `assert!` macro - if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr); - let begin_panic_call = peel_blocks(then); - // function call - if let Some(arg) = match_panic_call(cx, begin_panic_call); - // bind the second argument of the `assert!` macro if it exists - if let panic_message = snippet_opt(cx, arg.span); - // second argument of begin_panic is irrelevant - // as is the second match arm - then { - // an empty message occurs when it was generated by the macro - // (and not passed by the user) - return panic_message - .filter(|msg| !msg.is_empty()) - .map(|msg| AssertKind::WithMessage(msg, is_true)) - .or(Some(AssertKind::WithoutMessage(is_true))); } } - None } diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 0629674307ba7..a58d12ddd6b43 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -1,9 +1,10 @@ //! checks for attributes use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::macros::{is_panic, macro_backtrace}; use clippy_utils::msrvs; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; -use clippy_utils::{extract_msrv_attr, match_panic_def_id, meets_msrv}; +use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; @@ -443,20 +444,15 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_ } fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool { + if macro_backtrace(expr.span).last().map_or(false, |macro_call| { + is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable + }) { + return false; + } match &expr.kind { ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block), ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e), ExprKind::Ret(None) | ExprKind::Break(_, None) => false, - ExprKind::Call(path_expr, _) => { - if let ExprKind::Path(qpath) = &path_expr.kind { - typeck_results - .qpath_res(qpath, path_expr.hir_id) - .opt_def_id() - .map_or(true, |fun_id| !match_panic_def_id(cx, fun_id)) - } else { - true - } - }, _ => true, } } diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index d0b8c52a36a92..ea2cfb0bc0828 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, higher, is_direct_expn_of, ty::implements_trait}; +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Lit}; @@ -66,44 +67,40 @@ fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let macros = ["assert_eq", "debug_assert_eq"]; - let inverted_macros = ["assert_ne", "debug_assert_ne"]; - - for mac in macros.iter().chain(inverted_macros.iter()) { - if let Some(span) = is_direct_expn_of(expr.span, mac) { - if let Some(args) = higher::extract_assert_macro_args(expr) { - if let [a, b, ..] = args[..] { - let nb_bool_args = usize::from(is_bool_lit(a)) + usize::from(is_bool_lit(b)); - - if nb_bool_args != 1 { - // If there are two boolean arguments, we definitely don't understand - // what's going on, so better leave things as is... - // - // Or there is simply no boolean and then we can leave things as is! - return; - } - - if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) { - // At this point the expression which is not a boolean - // literal does not implement Not trait with a bool output, - // so we cannot suggest to rewrite our code - return; - } + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let macro_name = cx.tcx.item_name(macro_call.def_id); + if !matches!( + macro_name.as_str(), + "assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne" + ) { + return; + } + let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + if !(is_bool_lit(a) ^ is_bool_lit(b)) { + // If there are two boolean arguments, we definitely don't understand + // what's going on, so better leave things as is... + // + // Or there is simply no boolean and then we can leave things as is! + return; + } - let non_eq_mac = &mac[..mac.len() - 3]; - span_lint_and_sugg( - cx, - BOOL_ASSERT_COMPARISON, - span, - &format!("used `{}!` with a literal bool", mac), - "replace it with", - format!("{}!(..)", non_eq_mac), - Applicability::MaybeIncorrect, - ); - return; - } - } - } + if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) { + // At this point the expression which is not a boolean + // literal does not implement Not trait with a bool output, + // so we cannot suggest to rewrite our code + return; } + + let macro_name = macro_name.as_str(); + let non_eq_mac = ¯o_name[..macro_name.len() - 3]; + span_lint_and_sugg( + cx, + BOOL_ASSERT_COMPARISON, + macro_call.span, + &format!("used `{}!` with a literal bool", macro_name), + "replace it with", + format!("{}!(..)", non_eq_mac), + Applicability::MaybeIncorrect, + ); } } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 3650e4f91a001..9d4195e400dbd 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,8 +1,9 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then}; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::source::{first_line_of_span, snippet_with_applicability}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty}; +use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty}; use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind}; @@ -13,7 +14,7 @@ use rustc_errors::emitter::EmitterWriter; use rustc_errors::{Applicability, Handler, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{AnonConst, Expr, ExprKind, QPath}; +use rustc_hir::{AnonConst, Expr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; @@ -805,24 +806,17 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { return; } - // check for `begin_panic` - if_chain! { - if let ExprKind::Call(func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; - if let Some(path_def_id) = path.res.opt_def_id(); - if match_panic_def_id(self.cx, path_def_id); - if is_expn_of(expr.span, "unreachable").is_none(); - if !is_expn_of_debug_assertions(expr.span); - then { - self.panic_span = Some(expr.span); + if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) { + if is_panic(self.cx, macro_call.def_id) + || matches!( + self.cx.tcx.item_name(macro_call.def_id).as_str(), + "assert" | "assert_eq" | "assert_ne" | "todo" + ) + { + self.panic_span = Some(macro_call.span); } } - // check for `assert_eq` or `assert_ne` - if is_expn_of(expr.span, "assert_eq").is_some() || is_expn_of(expr.span, "assert_ne").is_some() { - self.panic_span = Some(expr.span); - } - // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); @@ -844,8 +838,3 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } - -fn is_expn_of_debug_assertions(span: Span) -> bool { - const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"]; - MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some()) -} diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 1012346052733..df75b815436b8 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; +use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, is_expn_of, is_in_test_function}; +use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -68,32 +69,26 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); -const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_eq", "debug_assert_ne"]; - impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Block(block, _) = e.kind { - for stmt in block.stmts { - for amn in &ASSERT_MACRO_NAMES { - if_chain! { - if is_expn_of(stmt.span, amn).is_some(); - if let StmtKind::Semi(matchexpr) = stmt.kind; - if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr); - if macro_args.len() == 2; - let (lhs, rhs) = (macro_args[0], macro_args[1]); - if eq_expr_value(cx, lhs, rhs); - if !is_in_test_function(cx.tcx, e.hir_id); - then { - span_lint( - cx, - EQ_OP, - lhs.span.to(rhs.span), - &format!("identical args used in this `{}!` macro call", amn), - ); - } - } - } + if_chain! { + if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| { + let name = cx.tcx.item_name(macro_call.def_id); + matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne") + .then(|| (macro_call, name)) + }); + if let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn); + if eq_expr_value(cx, lhs, rhs); + if macro_call.is_local(); + if !is_in_test_function(cx.tcx, e.hir_id); + then { + span_lint( + cx, + EQ_OP, + lhs.span.to(rhs.span), + &format!("identical args used in this `{}!` macro call", macro_name), + ); } } if let ExprKind::Binary(op, left, right) = e.kind { diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 6b327b9ce1720..98e5234e0aa94 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; -use clippy_utils::higher::FormatArgsExpn; +use clippy_utils::macros::FormatArgsExpn; use clippy_utils::{is_expn_of, match_function_call, paths}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { } else { None }; - if let Some(format_args) = FormatArgsExpn::parse(write_arg); + if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg); then { let calling_macro = // ordering is important here, since `writeln!` uses `write!` internally @@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { ) }; let msg = format!("use of `{}.unwrap()`", used); - if let [write_output] = *format_args.format_string_symbols { + if let [write_output] = *format_args.format_string_parts { let mut write_output = write_output.to_string(); if write_output.ends_with('\n') { write_output.pop(); diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 05d300058cf4f..02f1baf27fae8 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; +use clippy_utils::method_chain_args; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expn_of, match_panic_def_id, method_chain_args}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) { use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; - use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath}; + use rustc_hir::{Expr, ImplItemKind}; struct FindPanicUnwrap<'a, 'tcx> { lcx: &'a LateContext<'tcx>, @@ -80,14 +81,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - // check for `begin_panic` - if_chain! { - if let ExprKind::Call(func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; - if let Some(path_def_id) = path.res.opt_def_id(); - if match_panic_def_id(self.lcx, path_def_id); - if is_expn_of(expr.span, "unreachable").is_none(); - then { + if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) { + if is_panic(self.lcx, macro_call.def_id) { self.result.push(expr.span); } } diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 3f043e5f2f1c5..688d8f8630f3f 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::FormatExpn; +use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use if_chain::if_chain; @@ -43,38 +43,41 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]); impl<'tcx> LateLintPass<'tcx> for UselessFormat { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) { - Some(e) if !e.call_site.from_expansion() => e, - _ => return, + let (format_args, call_site) = if_chain! { + if let Some(macro_call) = root_macro_call_first_node(cx, expr); + if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id); + if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn); + then { + (format_args, macro_call.span) + } else { + return + } }; let mut applicability = Applicability::MachineApplicable; if format_args.value_args.is_empty() { - if format_args.format_string_parts.is_empty() { - span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability); - } else { - if_chain! { - if let [e] = &*format_args.format_string_parts; - if let ExprKind::Lit(lit) = &e.kind; - if let Some(s_src) = snippet_opt(cx, lit.span); - then { + match *format_args.format_string_parts { + [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability), + [_] => { + if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) { // Simulate macro expansion, converting {{ and }} to { and }. let s_expand = s_src.replace("{{", "{").replace("}}", "}"); let sugg = format!("{}.to_string()", s_expand); span_useless_format(cx, call_site, sugg, applicability); } - } + }, + [..] => {}, } } else if let [value] = *format_args.value_args { if_chain! { - if format_args.format_string_symbols == [kw::Empty]; + if format_args.format_string_parts == [kw::Empty]; if match cx.typeck_results().expr_ty(value).peel_refs().kind() { ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did), ty::Str => true, _ => false, }; if let Some(args) = format_args.args(); - if args.iter().all(|arg| arg.is_display() && !arg.has_string_formatting()); + if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting()); then { let is_new_string = match value.kind { ExprKind::Binary(..) => true, diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index f0e1a67dcddb9..ae423d799d71a 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::higher::{FormatArgsArg, FormatArgsExpn, FormatExpn}; +use clippy_utils::macros::{FormatArgsArg, FormatArgsExpn}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use clippy_utils::{is_diag_trait_item, match_def_path, paths}; @@ -83,7 +83,7 @@ const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[sym::format_macro, sym::std_panic_m impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { - if let Some(format_args) = FormatArgsExpn::parse(expr); + if let Some(format_args) = FormatArgsExpn::parse(cx, expr); let expr_expn_data = expr.span.ctxt().outer_expn_data(); let outermost_expn_data = outermost_expn_data(expr_expn_data); if let Some(macro_def_id) = outermost_expn_data.macro_def_id; @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if let Some(args) = format_args.args(); then { for (i, arg) in args.iter().enumerate() { - if !arg.is_display() { + if arg.format_trait != sym::Display { continue; } if arg.has_string_formatting() { @@ -106,8 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if is_aliased(&args, i) { continue; } - check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg); - check_to_string_in_format_args(cx, name, arg); + check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value); + check_to_string_in_format_args(cx, name, arg.value); } } } @@ -122,30 +122,31 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData { } } -fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &FormatArgsArg<'_>) { - if_chain! { - if FormatExpn::parse(arg.value).is_some(); - if !arg.value.span.ctxt().outer_expn_data().call_site.from_expansion(); - then { - span_lint_and_then( - cx, - FORMAT_IN_FORMAT_ARGS, - call_site, - &format!("`format!` in `{}!` args", name), - |diag| { - diag.help(&format!( - "combine the `format!(..)` arguments with the outer `{}!(..)` call", - name - )); - diag.help("or consider changing `format!` to `format_args!`"); - }, - ); - } +fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) { + let expn_data = arg.span.ctxt().outer_expn_data(); + if expn_data.call_site.from_expansion() { + return; + } + let Some(mac_id) = expn_data.macro_def_id else { return }; + if !cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) { + return; } + span_lint_and_then( + cx, + FORMAT_IN_FORMAT_ARGS, + call_site, + &format!("`format!` in `{}!` args", name), + |diag| { + diag.help(&format!( + "combine the `format!(..)` arguments with the outer `{}!(..)` call", + name + )); + diag.help("or consider changing `format!` to `format_args!`"); + }, + ); } -fn check_to_string_in_format_args<'tcx>(cx: &LateContext<'tcx>, name: Symbol, arg: &FormatArgsArg<'tcx>) { - let value = arg.value; +fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) { if_chain! { if !value.span.from_expansion(); if let ExprKind::MethodCall(_, _, [receiver], _) = value.kind; diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 5a2a965716cc6..e3a34b22e3252 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::PanicExpn; +use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_expn_of, sugg}; +use clippy_utils::{peel_blocks_with_stmt, sugg}; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp}; +use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -35,64 +36,33 @@ declare_clippy_lint! { declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); impl LateLintPass<'_> for ManualAssert { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if_chain! { - if let Expr { - kind: ExprKind:: If(cond, Expr { - kind: ExprKind::Block( - Block { - stmts: [stmt], - .. - }, - _), - .. - }, None), - .. - } = &expr; - if is_expn_of(stmt.span, "panic").is_some(); + if let ExprKind::If(cond, then, None) = expr.kind; if !matches!(cond.kind, ExprKind::Let(_)); - if let StmtKind::Semi(semi) = stmt.kind; + if !expr.span.from_expansion(); + let then = peel_blocks_with_stmt(then); + if let Some(macro_call) = root_macro_call(then.span); + if cx.tcx.item_name(macro_call.def_id) == sym::panic; if !cx.tcx.sess.source_map().is_multiline(cond.span); - + if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn); then { - let call = if_chain! { - if let ExprKind::Block(block, _) = semi.kind; - if let Some(init) = block.expr; - then { - init - } else { - semi - } - }; - let span = if let Some(panic_expn) = PanicExpn::parse(call) { - match *panic_expn.format_args.value_args { - [] => panic_expn.format_args.format_string_span, - [.., last] => panic_expn.format_args.format_string_span.to(last.span), - } - } else if let ExprKind::Call(_, [format_args]) = call.kind { - format_args.span - } else { - return - }; let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); - let cond_sugg = if let ExprKind::DropTemps(e, ..) = cond.kind { - if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e { - sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string() - } else { - format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par()) - } - } else { - format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par()) + let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); + let cond = cond.peel_drop_temps(); + let (cond, not) = match cond.kind { + ExprKind::Unary(UnOp::Not, e) => (e, ""), + _ => (cond, "!"), }; - + let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); + let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); span_lint_and_sugg( cx, MANUAL_ASSERT, expr.span, "only a `panic!` in `if`-then statement", "try", - format!("assert!({}, {});", cond_sugg, sugg), + sugg, Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 22970507f964c..0d693499bc67f 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,16 +2,17 @@ use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt}; use clippy_utils::diagnostics::{ multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; -use clippy_utils::higher; +use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use clippy_utils::visitors::is_local_used; use clippy_utils::{ - get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, + get_parent_expr, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, strip_pat_refs, }; +use clippy_utils::{higher, peel_blocks_with_stmt}; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use core::iter::{once, ExactSizeIterator}; use if_chain::if_chain; @@ -974,7 +975,8 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm } if_chain! { if matching_wild; - if is_panic_call(arm.body); + if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span); + if is_panic(cx, macro_call.def_id); then { // `Err(_)` or `Err(_e)` arm with `panic!` found span_lint_and_note(cx, @@ -1179,22 +1181,6 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) }; } -// If the block contains only a `panic!` macro (as expression or statement) -fn is_panic_call(expr: &Expr<'_>) -> bool { - // Unwrap any wrapping blocks - let span = if let ExprKind::Block(block, _) = expr.kind { - match (&block.expr, block.stmts.len(), block.stmts.first()) { - (&Some(exp), 0, _) => exp.span, - (&None, 1, Some(stmt)) => stmt.span, - _ => return false, - } - } else { - expr.span - }; - - is_expn_of(span, "panic").is_some() && is_expn_of(span, "unreachable").is_none() -} - fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>) where 'b: 'a, diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 0ec9387f9c460..866eaff5b1d76 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::FormatExpn; +use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; @@ -14,7 +14,13 @@ use super::EXPECT_FUN_CALL; /// Checks for the `EXPECT_FUN_CALL` lint. #[allow(clippy::too_many_lines)] -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, name: &str, args: &[hir::Expr<'_>]) { +pub(super) fn check( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + method_span: Span, + name: &str, + args: &'tcx [hir::Expr<'tcx>], +) { // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or // `&str` fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { @@ -128,11 +134,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa let mut applicability = Applicability::MachineApplicable; //Special handling for `format!` as arg_root - if let Some(format_expn) = FormatExpn::parse(arg_root) { - let span = match *format_expn.format_args.value_args { - [] => format_expn.format_args.format_string_span, - [.., last] => format_expn.format_args.format_string_span.to(last.span), - }; + if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) { + if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) { + return; + } + let Some(format_args) = FormatArgsExpn::find_nested(cx, arg_root, macro_call.expn) else { return }; + let span = format_args.inputs_span(); let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index fa7274990db39..49d4b3a1850fe 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{higher, is_direct_expn_of}; +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -34,26 +34,30 @@ declare_clippy_lint! { declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]); -const DEBUG_MACRO_NAMES: [&str; 3] = ["debug_assert", "debug_assert_eq", "debug_assert_ne"]; - impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - for dmn in &DEBUG_MACRO_NAMES { - if is_direct_expn_of(e.span, dmn).is_some() { - if let Some(macro_args) = higher::extract_assert_macro_args(e) { - for arg in macro_args { - let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(arg); - if let Some(span) = visitor.expr_span() { - span_lint( - cx, - DEBUG_ASSERT_WITH_MUT_CALL, - span, - &format!("do not call a function with mutable arguments inside of `{}!`", dmn), - ); - } - } - } + let Some(macro_call) = root_macro_call_first_node(cx, e) else { return }; + let macro_name = cx.tcx.item_name(macro_call.def_id); + if !matches!( + macro_name.as_str(), + "debug_assert" | "debug_assert_eq" | "debug_assert_ne" + ) { + return; + } + let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else { return }; + for arg in [lhs, rhs] { + let mut visitor = MutArgVisitor::new(cx); + visitor.visit_expr(arg); + if let Some(span) = visitor.expr_span() { + span_lint( + cx, + DEBUG_ASSERT_WITH_MUT_CALL, + span, + &format!( + "do not call a function with mutable arguments inside of `{}!`", + macro_name + ), + ); } } } diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 8769c04521464..b7a56970b3355 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::return_ty; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{find_macro_calls, is_expn_of, return_ty}; +use clippy_utils::visitors::expr_visitor_no_bodies; use rustc_hir as hir; -use rustc_hir::intravisit::FnKind; +use rustc_hir::intravisit::{FnKind, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; @@ -55,19 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { } fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { - let mut panics = find_macro_calls( - &[ - "unimplemented", - "unreachable", - "panic", - "todo", - "assert", - "assert_eq", - "assert_ne", - ], - body, - ); - panics.retain(|span| is_expn_of(*span, "debug_assert").is_none()); + let mut panics = Vec::new(); + expr_visitor_no_bodies(|expr| { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true }; + if matches!( + &*cx.tcx.item_name(macro_call.def_id).as_str(), + "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne" + ) { + panics.push(macro_call.span); + return false; + } + true + }) + .visit_expr(&body.value); if !panics.is_empty() { span_lint_and_then( cx, diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index edfac824ded98..6ef6b9a20aa4b 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -1,10 +1,8 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_expn_of, match_panic_call}; -use if_chain::if_chain; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -78,37 +76,37 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if match_panic_call(cx, expr).is_some() - && (is_expn_of(expr.span, "debug_assert").is_none() && is_expn_of(expr.span, "assert").is_none()) - { - let span = get_outer_span(expr); - if is_expn_of(expr.span, "unimplemented").is_some() { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + if is_panic(cx, macro_call.def_id) { + span_lint( + cx, + PANIC, + macro_call.span, + "`panic` should not be present in production code", + ); + return; + } + match cx.tcx.item_name(macro_call.def_id).as_str() { + "todo" => { + span_lint( + cx, + TODO, + macro_call.span, + "`todo` should not be present in production code", + ); + }, + "unimplemented" => { span_lint( cx, UNIMPLEMENTED, - span, + macro_call.span, "`unimplemented` should not be present in production code", ); - } else if is_expn_of(expr.span, "todo").is_some() { - span_lint(cx, TODO, span, "`todo` should not be present in production code"); - } else if is_expn_of(expr.span, "unreachable").is_some() { - span_lint(cx, UNREACHABLE, span, "usage of the `unreachable!` macro"); - } else if is_expn_of(expr.span, "panic").is_some() { - span_lint(cx, PANIC, span, "`panic` should not be present in production code"); - } - } - } -} - -fn get_outer_span(expr: &Expr<'_>) -> Span { - if_chain! { - if expr.span.from_expansion(); - let first = expr.span.ctxt().outer_expn_data().call_site; - if first.from_expansion(); - then { - first.ctxt().outer_expn_data().call_site - } else { - expr.span + }, + "unreachable" => { + span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro"); + }, + _ => {}, } } } diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index 6d9aff474214c..1dd8895ebd076 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -1,35 +1,29 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::hygiene::{ExpnKind, MacroKind}; use super::UNIT_CMP; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { - if let Some(callee) = expr.span.source_callee() { - if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { - if let ExprKind::Binary(ref cmp, left, _) = expr.kind { - let op = cmp.node; - if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() { - let result = match symbol.as_str() { - "assert_eq" | "debug_assert_eq" => "succeed", - "assert_ne" | "debug_assert_ne" => "fail", - _ => return, - }; - span_lint( - cx, - UNIT_CMP, - expr.span, - &format!( - "`{}` of unit values detected. This will always {}", - symbol.as_str(), - result - ), - ); - } - } + if let Some(macro_call) = root_macro_call_first_node(cx, expr) { + let macro_name = cx.tcx.item_name(macro_call.def_id); + let result = match macro_name.as_str() { + "assert_eq" | "debug_assert_eq" => "succeed", + "assert_ne" | "debug_assert_ne" => "fail", + _ => return, + }; + let Some ((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + if !cx.typeck_results().expr_ty(left).is_unit() { + return; } + span_lint( + cx, + UNIT_CMP, + macro_call.span, + &format!("`{}` of unit values detected. This will always {}", macro_name, result), + ); } return; } diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index e98dcd3cf983b..165004e5b413b 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{ @@ -410,9 +411,13 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { } self.declared_lints.insert(item.ident.name, item.span); } - } else if is_expn_of(item.span, "impl_lint_pass").is_some() - || is_expn_of(item.span, "declare_lint_pass").is_some() - { + } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { + if !matches!( + &*cx.tcx.item_name(macro_call.def_id).as_str(), + "impl_lint_pass" | "declare_lint_pass" + ) { + return; + } if let hir::ItemKind::Impl(hir::Impl { of_trait: None, items: impl_item_refs, diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 0ba0b59ed13da..af1e2eb4dd11f 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" publish = false [dependencies] +arrayvec = { version = "0.7", default-features = false } if_chain = "1.0" rustc-semver = "1.1" diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 8400bfbbd99d2..3d3180521ab7a 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/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/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index c764c35d444fb..160a51740cd7c 100644 --- a/clippy_utils/src/higher.rs +++ b/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>> { - /// 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>> { - 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 { - 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, - /// 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 { - 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>> { - 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::(); - 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 { - 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/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 54d470ca73820..bbb70e3adb5fe 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -44,6 +44,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; @@ -1138,19 +1139,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 { - 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 @@ -1679,32 +1667,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 diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs new file mode 100644 index 0000000000000..2bf43aeb995a4 --- /dev/null +++ b/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 { + 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 { + 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 { + 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 { + 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 { + 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 { + // 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 { + 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: &'a Expr<'a>, 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, + /// 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 FormatArgsExpn<'tcx> { + /// Parses an expanded `format_args!` or `format_args_nl!` invocation + pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option { + 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 = None; + let mut format_string_parts: Vec = 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(, ::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 { + 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>> { + 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/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index aa3b3af235670..27db53a6e6d58 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -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"]; @@ -110,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"]; diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs index cb516d0f97783..7477c01ca7828 100644 --- a/tests/ui/assertions_on_constants.rs +++ b/tests/ui/assertions_on_constants.rs @@ -1,4 +1,3 @@ -//FIXME: suggestions are wrongly expanded, this should be fixed along with #7843 #![allow(non_fmt_panics)] macro_rules! assert_const { diff --git a/tests/ui/assertions_on_constants.stderr b/tests/ui/assertions_on_constants.stderr index ec80ec702fb57..e1f818814d500 100644 --- a/tests/ui/assertions_on_constants.stderr +++ b/tests/ui/assertions_on_constants.stderr @@ -1,75 +1,75 @@ error: `assert!(true)` will be optimized out by the compiler - --> $DIR/assertions_on_constants.rs:11:5 + --> $DIR/assertions_on_constants.rs:10:5 | LL | assert!(true); | ^^^^^^^^^^^^^ | = note: `-D clippy::assertions-on-constants` implied by `-D warnings` = help: remove it - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: `assert!(false)` should probably be replaced - --> $DIR/assertions_on_constants.rs:12:5 + --> $DIR/assertions_on_constants.rs:11:5 | LL | assert!(false); | ^^^^^^^^^^^^^^ | = help: use `panic!()` or `unreachable!()` - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: `assert!(true)` will be optimized out by the compiler - --> $DIR/assertions_on_constants.rs:13:5 + --> $DIR/assertions_on_constants.rs:12:5 | LL | assert!(true, "true message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: remove it - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `assert!(false, $crate::const_format_args!($($t)+))` should probably be replaced - --> $DIR/assertions_on_constants.rs:14:5 +error: `assert!(false, ..)` should probably be replaced + --> $DIR/assertions_on_constants.rs:13:5 | LL | assert!(false, "false message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!($crate::const_format_args!($($t)+))` or `unreachable!($crate::const_format_args!($($t)+))` - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: use `panic!(..)` or `unreachable!(..)` + +error: `assert!(false, ..)` should probably be replaced + --> $DIR/assertions_on_constants.rs:16:5 + | +LL | assert!(false, "{}", msg.to_uppercase()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `panic!(..)` or `unreachable!(..)` error: `assert!(true)` will be optimized out by the compiler - --> $DIR/assertions_on_constants.rs:20:5 + --> $DIR/assertions_on_constants.rs:19:5 | LL | assert!(B); | ^^^^^^^^^^ | = help: remove it - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: `assert!(false)` should probably be replaced - --> $DIR/assertions_on_constants.rs:23:5 + --> $DIR/assertions_on_constants.rs:22:5 | LL | assert!(C); | ^^^^^^^^^^ | = help: use `panic!()` or `unreachable!()` - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `assert!(false, $crate::const_format_args!($($t)+))` should probably be replaced - --> $DIR/assertions_on_constants.rs:24:5 +error: `assert!(false, ..)` should probably be replaced + --> $DIR/assertions_on_constants.rs:23:5 | LL | assert!(C, "C message"); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!($crate::const_format_args!($($t)+))` or `unreachable!($crate::const_format_args!($($t)+))` - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: use `panic!(..)` or `unreachable!(..)` error: `debug_assert!(true)` will be optimized out by the compiler - --> $DIR/assertions_on_constants.rs:26:5 + --> $DIR/assertions_on_constants.rs:25:5 | LL | debug_assert!(true); | ^^^^^^^^^^^^^^^^^^^ | = help: remove it - = note: this error originates in the macro `$crate::assert` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/eq_op_macros.stderr b/tests/ui/eq_op_macros.stderr index 885415b42c787..cd9f1826e59bf 100644 --- a/tests/ui/eq_op_macros.stderr +++ b/tests/ui/eq_op_macros.stderr @@ -21,6 +21,28 @@ LL | assert_in_macro_def!(); | = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_macros.rs:9:26 + | +LL | debug_assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ---------------------- in this macro invocation + | + = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_macros.rs:10:26 + | +LL | debug_assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ---------------------- in this macro invocation + | + = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) + error: identical args used in this `assert_eq!` macro call --> $DIR/eq_op_macros.rs:22:16 | @@ -45,28 +67,6 @@ error: identical args used in this `assert_ne!` macro call LL | assert_ne!(a + 1, a + 1); | ^^^^^^^^^^^^ -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op_macros.rs:9:26 - | -LL | debug_assert_eq!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ---------------------- in this macro invocation - | - = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op_macros.rs:10:26 - | -LL | debug_assert_ne!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ---------------------- in this macro invocation - | - = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) - error: identical args used in this `debug_assert_eq!` macro call --> $DIR/eq_op_macros.rs:38:22 | diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr index 8bccbaefe23c5..91ebd695238bb 100644 --- a/tests/ui/missing_panics_doc.stderr +++ b/tests/ui/missing_panics_doc.stderr @@ -27,7 +27,6 @@ note: first possible panic found here | LL | panic!("This function panics") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:17:1 @@ -42,7 +41,6 @@ note: first possible panic found here | LL | todo!() | ^^^^^^^ - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:22:1 @@ -61,7 +59,6 @@ note: first possible panic found here | LL | panic!() | ^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:31:1 @@ -76,7 +73,6 @@ note: first possible panic found here | LL | if true { unreachable!() } else { panic!() } | ^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:36:1 @@ -92,7 +88,6 @@ note: first possible panic found here | LL | assert_eq!(x, 0); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:42:1 @@ -108,7 +103,6 @@ note: first possible panic found here | LL | assert_ne!(x, 0); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 7 previous errors diff --git a/tests/ui/panic_in_result_fn.stderr b/tests/ui/panic_in_result_fn.stderr index 78d09b8b2108a..561503ae54fa2 100644 --- a/tests/ui/panic_in_result_fn.stderr +++ b/tests/ui/panic_in_result_fn.stderr @@ -14,7 +14,6 @@ note: return Err() instead of panicking | LL | panic!("error"); | ^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:11:5 @@ -31,7 +30,6 @@ note: return Err() instead of panicking | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:16:5 @@ -48,7 +46,6 @@ note: return Err() instead of panicking | LL | unreachable!(); | ^^^^^^^^^^^^^^ - = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:21:5 @@ -65,7 +62,6 @@ note: return Err() instead of panicking | LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:52:1 @@ -82,7 +78,6 @@ note: return Err() instead of panicking | LL | panic!("error"); | ^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:67:1 @@ -99,7 +94,6 @@ note: return Err() instead of panicking | LL | todo!("finish main method"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors diff --git a/tests/ui/panic_in_result_fn_assertions.stderr b/tests/ui/panic_in_result_fn_assertions.stderr index 7501d6d85edd7..b6aa005e7b521 100644 --- a/tests/ui/panic_in_result_fn_assertions.stderr +++ b/tests/ui/panic_in_result_fn_assertions.stderr @@ -15,7 +15,6 @@ note: return Err() instead of panicking | LL | assert!(x == 5, "wrong argument"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn_assertions.rs:13:5 @@ -33,7 +32,6 @@ note: return Err() instead of panicking | LL | assert_eq!(x, 5); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn_assertions.rs:19:5 @@ -51,7 +49,6 @@ note: return Err() instead of panicking | LL | assert_ne!(x, 1); | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr index 2b607ff588895..bfd1c7a380149 100644 --- a/tests/ui/panicking_macros.stderr +++ b/tests/ui/panicking_macros.stderr @@ -25,23 +25,18 @@ LL | todo!(); | ^^^^^^^ | = note: `-D clippy::todo` implied by `-D warnings` - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `todo` should not be present in production code --> $DIR/panicking_macros.rs:17:5 | LL | todo!("message"); | ^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `todo` should not be present in production code --> $DIR/panicking_macros.rs:18:5 | LL | todo!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:24:5 @@ -50,23 +45,18 @@ LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ | = note: `-D clippy::unimplemented` implied by `-D warnings` - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:25:5 | LL | unimplemented!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:26:5 | LL | unimplemented!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:32:5 @@ -75,23 +65,18 @@ LL | unreachable!(); | ^^^^^^^^^^^^^^ | = note: `-D clippy::unreachable` implied by `-D warnings` - = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:33:5 | LL | unreachable!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `$crate::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:34:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: `panic` should not be present in production code --> $DIR/panicking_macros.rs:40:5 @@ -104,24 +89,18 @@ error: `todo` should not be present in production code | LL | todo!(); | ^^^^^^^ - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:42:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:43:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 16 previous errors diff --git a/tests/ui/unit_cmp.stderr b/tests/ui/unit_cmp.stderr index 2b5a7b348b982..824506a4257ef 100644 --- a/tests/ui/unit_cmp.stderr +++ b/tests/ui/unit_cmp.stderr @@ -33,8 +33,6 @@ LL | | }, LL | | } LL | | ); | |_____^ - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: `debug_assert_eq` of unit values detected. This will always succeed --> $DIR/unit_cmp.rs:32:5 @@ -47,8 +45,6 @@ LL | | }, LL | | } LL | | ); | |_____^ - | - = note: this error originates in the macro `$crate::assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: `assert_ne` of unit values detected. This will always fail --> $DIR/unit_cmp.rs:41:5 @@ -61,8 +57,6 @@ LL | | }, LL | | } LL | | ); | |_____^ - | - = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: `debug_assert_ne` of unit values detected. This will always fail --> $DIR/unit_cmp.rs:49:5 @@ -75,8 +69,6 @@ LL | | }, LL | | } LL | | ); | |_____^ - | - = note: this error originates in the macro `$crate::assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors From a4ebf6f78e8a3f586251e7d0e4c72af51b436de5 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Thu, 30 Dec 2021 18:43:34 -0500 Subject: [PATCH 21/72] Change `unnecessary_to_owned` `into_iter` suggestions to `MaybeIncorrect` --- .../src/methods/unnecessary_iter_cloned.rs | 17 ++++++++++++++--- .../src/methods/unnecessary_to_owned.rs | 13 +++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 8300df03e9935..4f589a6d3187e 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -18,7 +18,7 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol if let Some(callee_def_id) = fn_def_id(cx, parent); if is_into_iter(cx, callee_def_id); then { - check_for_loop_iter(cx, parent, method_name, receiver) + check_for_loop_iter(cx, parent, method_name, receiver, false) } else { false } @@ -34,6 +34,7 @@ pub fn check_for_loop_iter( expr: &'tcx Expr<'tcx>, method_name: Symbol, receiver: &'tcx Expr<'tcx>, + cloned_before_iter: bool, ) -> bool { if_chain! { if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent)); @@ -70,12 +71,22 @@ pub fn check_for_loop_iter( expr.span, &format!("unnecessary use of `{}`", method_name), |diag| { - diag.span_suggestion(expr.span, "use", snippet, Applicability::MachineApplicable); + // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to + // a `to_owned`-like function was removed, then the next suggestion may be + // incorrect. This is because the iterator that results from the call's removal + // could hold a reference to a resource that is used mutably. See + // https://github.com/rust-lang/rust-clippy/issues/8148. + let applicability = if cloned_before_iter { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + diag.span_suggestion(expr.span, "use", snippet, applicability); for addr_of_expr in addr_of_exprs { match addr_of_expr.kind { ExprKind::AddrOf(_, _, referent) => { let span = addr_of_expr.span.with_hi(referent.span.lo()); - diag.span_suggestion(span, "remove this `&`", String::new(), Applicability::MachineApplicable); + diag.span_suggestion(span, "remove this `&`", String::new(), applicability); } _ => unreachable!(), } diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index c48bacfce0d37..7e17f163b2126 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -187,7 +187,13 @@ fn check_into_iter_call_arg( if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { - if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver) { + if unnecessary_iter_cloned::check_for_loop_iter( + cx, + parent, + method_name, + receiver, + true, + ) { return true; } let cloned_or_copied = if is_copy(cx, item_ty) { @@ -195,6 +201,9 @@ fn check_into_iter_call_arg( } else { "cloned" }; + // The next suggestion may be incorrect because the removal of the `to_owned`-like + // function could cause the iterator to hold a reference to a resource that is used + // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. span_lint_and_sugg( cx, UNNECESSARY_TO_OWNED, @@ -202,7 +211,7 @@ fn check_into_iter_call_arg( &format!("unnecessary use of `{}`", method_name), "use", format!("{}.iter().{}()", receiver_snippet, cloned_or_copied), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); return true; } From 14f34454b04e6fd578ccbeaed99d8d3ad3ba1bc9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 5 Jan 2022 12:15:10 -0500 Subject: [PATCH 22/72] Don't lint `DequeVec` in `manual_memcpy` --- clippy_lints/src/loops/manual_memcpy.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index c62fa5e998bd4..0bff868641cc4 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -2,7 +2,6 @@ use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; use if_chain::if_chain; use rustc_ast::ast; @@ -325,13 +324,12 @@ struct Start<'hir> { } fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { - let is_slice = match ty.kind() { + match ty.kind() { + ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::Vec, adt.did), ty::Ref(_, subty, _) => is_slice_like(cx, subty), ty::Slice(..) | ty::Array(..) => true, _ => false, - }; - - is_slice || is_type_diagnostic_item(cx, ty, sym::Vec) || is_type_diagnostic_item(cx, ty, sym::VecDeque) + } } fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { From 3925def9cf0daace7e725840d6044ad9d831c40d Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 5 Jan 2022 12:35:35 -0500 Subject: [PATCH 23/72] Suggest `copy_from_slice` for `manual_memcpy` when possible --- clippy_lints/src/loops/manual_memcpy.rs | 29 ++++++++++++------- .../manual_memcpy/with_loop_counters.stderr | 24 +++++++-------- .../without_loop_counters.stderr | 26 ++++++++--------- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 0bff868641cc4..17302f43c324c 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -2,6 +2,7 @@ use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_copy; use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; use if_chain::if_chain; use rustc_ast::ast; @@ -61,15 +62,15 @@ pub(super) fn check<'tcx>( if_chain! { if let ExprKind::Index(base_left, idx_left) = lhs.kind; if let ExprKind::Index(base_right, idx_right) = rhs.kind; - if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)); - if is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); + if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left)); + if get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some(); if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts); if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts); // Source and destination must be different if path_to_local(base_left) != path_to_local(base_right); then { - Some((IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, + Some((ty, IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right })) } else { None @@ -77,7 +78,7 @@ pub(super) fn check<'tcx>( } }) }) - .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src))) + .map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src))) .collect::>>() .filter(|v| !v.is_empty()) .map(|v| v.join("\n ")); @@ -104,6 +105,7 @@ fn build_manual_memcpy_suggestion<'tcx>( start: &Expr<'_>, end: &Expr<'_>, limits: ast::RangeLimits, + elem_ty: Ty<'tcx>, dst: &IndexExpr<'_>, src: &IndexExpr<'_>, ) -> String { @@ -186,9 +188,16 @@ fn build_manual_memcpy_suggestion<'tcx>( .into() }; + let method_str = if is_copy(cx, elem_ty) { + "copy_from_slice" + } else { + "clone_from_slice" + }; + format!( - "{}.clone_from_slice(&{}[{}..{}]);", + "{}.{}(&{}[{}..{}]);", dst, + method_str, src_base_str, src_offset.maybe_par(), src_limit.maybe_par() @@ -323,12 +332,12 @@ struct Start<'hir> { kind: StartKind<'hir>, } -fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { +fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { match ty.kind() { - ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::Vec, adt.did), - ty::Ref(_, subty, _) => is_slice_like(cx, subty), - ty::Slice(..) | ty::Array(..) => true, - _ => false, + ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Vec, adt.did) => Some(subs.type_at(0)), + ty::Ref(_, subty, _) => get_slice_like_element_ty(cx, subty), + ty::Slice(ty) | ty::Array(ty, _) => Some(ty), + _ => None, } } diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr index 2e3ebadd7b5d2..79d40c0bcb1d9 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -5,7 +5,7 @@ LL | / for i in 3..src.len() { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..src.len()].copy_from_slice(&src[..(src.len() - 3)]);` | = note: `-D clippy::manual-memcpy` implied by `-D warnings` @@ -16,7 +16,7 @@ LL | / for i in 3..src.len() { LL | | dst[count] = src[i]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` + | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].copy_from_slice(&src[3..]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:17:5 @@ -25,7 +25,7 @@ LL | / for i in 0..src.len() { LL | | dst[count] = src[i]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` + | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].copy_from_slice(&src[..]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:23:5 @@ -34,7 +34,7 @@ LL | / for i in 0..src.len() { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` + | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[3..(src.len() + 3)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:29:5 @@ -43,7 +43,7 @@ LL | / for i in 3..(3 + src.len()) { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..(3 + src.len() - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].copy_from_slice(&src[..(3 + src.len() - 3)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:35:5 @@ -52,7 +52,7 @@ LL | / for i in 5..src.len() { LL | | dst[i] = src[count - 2]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` + | |_____^ help: try replacing the loop by: `dst[5..src.len()].copy_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:41:5 @@ -61,7 +61,7 @@ LL | / for i in 0..dst.len() { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[2..(dst.len() + 2)]);` + | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[2..(dst.len() + 2)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:47:5 @@ -70,7 +70,7 @@ LL | / for i in 3..10 { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..10].copy_from_slice(&src[5..(10 + 5 - 3)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:54:5 @@ -85,8 +85,8 @@ LL | | } | help: try replacing the loop by | -LL ~ dst[3..(src.len() + 3)].clone_from_slice(&src[..]); -LL + dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); +LL ~ dst[3..(src.len() + 3)].copy_from_slice(&src[..]); +LL + dst2[30..(src.len() + 30)].copy_from_slice(&src[..]); | error: it looks like you're manually copying between slices @@ -96,7 +96,7 @@ LL | / for i in 0..1 << 1 { LL | | dst[count] = src[i + 2]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` + | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].copy_from_slice(&src[2..((1 << 1) + 2)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:71:5 @@ -105,7 +105,7 @@ LL | / for i in 3..src.len() { LL | | dst[i] = src[count]; LL | | count += 1 LL | | } - | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..src.len()].copy_from_slice(&src[..(src.len() - 3)]);` error: aborting due to 11 previous errors diff --git a/tests/ui/manual_memcpy/without_loop_counters.stderr b/tests/ui/manual_memcpy/without_loop_counters.stderr index 8ff0137a8120e..654dee32a1e84 100644 --- a/tests/ui/manual_memcpy/without_loop_counters.stderr +++ b/tests/ui/manual_memcpy/without_loop_counters.stderr @@ -4,7 +4,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..src.len() { LL | | dst[i] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` + | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[..]);` | = note: `-D clippy::manual-memcpy` implied by `-D warnings` @@ -14,7 +14,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..src.len() { LL | | dst[i + 10] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..]);` + | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].copy_from_slice(&src[..]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:17:5 @@ -22,7 +22,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..src.len() { LL | | dst[i] = src[i + 10]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)]);` + | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[10..(src.len() + 10)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:22:5 @@ -30,7 +30,7 @@ error: it looks like you're manually copying between slices LL | / for i in 11..src.len() { LL | | dst[i] = src[i - 10]; LL | | } - | |_____^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)]);` + | |_____^ help: try replacing the loop by: `dst[11..src.len()].copy_from_slice(&src[(11 - 10)..(src.len() - 10)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:27:5 @@ -38,7 +38,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..dst.len() { LL | | dst[i] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()]);` + | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..dst.len()]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:40:5 @@ -51,8 +51,8 @@ LL | | } | help: try replacing the loop by | -LL ~ dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]); -LL + dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]); +LL ~ dst[10..256].copy_from_slice(&src[(10 - 5)..(256 - 5)]); +LL + dst2[(10 + 500)..(256 + 500)].copy_from_slice(&src[10..256]); | error: it looks like you're manually copying between slices @@ -61,7 +61,7 @@ error: it looks like you're manually copying between slices LL | / for i in 10..LOOP_OFFSET { LL | | dst[i + LOOP_OFFSET] = src[i - some_var]; LL | | } - | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` + | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].copy_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:65:5 @@ -69,7 +69,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..src_vec.len() { LL | | dst_vec[i] = src_vec[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..]);` + | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].copy_from_slice(&src_vec[..]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:94:5 @@ -77,7 +77,7 @@ error: it looks like you're manually copying between slices LL | / for i in from..from + src.len() { LL | | dst[i] = src[i - from]; LL | | } - | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)]);` + | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].copy_from_slice(&src[..(from + src.len() - from)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:98:5 @@ -85,7 +85,7 @@ error: it looks like you're manually copying between slices LL | / for i in from..from + 3 { LL | | dst[i] = src[i - from]; LL | | } - | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)]);` + | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].copy_from_slice(&src[..(from + 3 - from)]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:103:5 @@ -93,7 +93,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..5 { LL | | dst[i - 0] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5]);` + | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src[..5]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:108:5 @@ -101,7 +101,7 @@ error: it looks like you're manually copying between slices LL | / for i in 0..0 { LL | | dst[i] = src[i]; LL | | } - | |_____^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0]);` + | |_____^ help: try replacing the loop by: `dst[..0].copy_from_slice(&src[..0]);` error: it looks like you're manually copying between slices --> $DIR/without_loop_counters.rs:120:5 From 2cc38a23227e743aa24b7656ccbcee768ebef5ac Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 5 Jan 2022 13:46:35 -0500 Subject: [PATCH 24/72] Lint `iter_not_returning_iterator` on the trait definition rather than the implementation --- .../src/iter_not_returning_iterator.rs | 61 +++++++++++++------ tests/ui/iter_not_returning_iterator.rs | 12 ++++ tests/ui/iter_not_returning_iterator.stderr | 8 ++- 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index 0af6b3b7d464e..91d3c00039a02 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -1,8 +1,7 @@ -use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait}; -use rustc_hir::{ImplItem, ImplItemKind}; +use clippy_utils::{diagnostics::span_lint, get_parent_node, ty::implements_trait}; +use rustc_hir::{def_id::LocalDefId, FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::kw; use rustc_span::symbol::sym; declare_clippy_lint! { @@ -41,25 +40,47 @@ declare_clippy_lint! { declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]); impl LateLintPass<'_> for IterNotReturningIterator { - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) { - let name = impl_item.ident.name.as_str(); - if_chain! { - if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind; - let ret_ty = return_ty(cx, impl_item.hir_id()); - if matches!(name, "iter" | "iter_mut"); - if let [param] = cx.tcx.fn_arg_names(impl_item.def_id); - if param.name == kw::SelfLower; - if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if !implements_trait(cx, ret_ty, iter_trait_id, &[]); + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + let name = item.ident.name.as_str(); + if matches!(name, "iter" | "iter_mut") { + if let TraitItemKind::Fn(fn_sig, _) = &item.kind { + check_sig(cx, name, fn_sig, item.def_id); + } + } + } - then { - span_lint( - cx, - ITER_NOT_RETURNING_ITERATOR, - fn_sig.span, - &format!("this method is named `{}` but its return type does not implement `Iterator`", name), - ); + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { + let name = item.ident.name.as_str(); + if matches!(name, "iter" | "iter_mut") + && !matches!( + get_parent_node(cx.tcx, item.hir_id()), + Some(Node::Item(Item { kind: ItemKind::Impl(i), .. })) if i.of_trait.is_some() + ) + { + if let ImplItemKind::Fn(fn_sig, _) = &item.kind { + check_sig(cx, name, fn_sig, item.def_id); } } } } + +fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) { + if sig.decl.implicit_self.has_implicit_self() { + let ret_ty = cx.tcx.fn_sig(fn_id).skip_binder().output(); + if cx + .tcx + .get_diagnostic_item(sym::Iterator) + .map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[])) + { + span_lint( + cx, + ITER_NOT_RETURNING_ITERATOR, + sig.span, + &format!( + "this method is named `{}` but its return type does not implement `Iterator`", + name + ), + ); + } + } +} diff --git a/tests/ui/iter_not_returning_iterator.rs b/tests/ui/iter_not_returning_iterator.rs index 377f760b3c4b2..f5ee044576c53 100644 --- a/tests/ui/iter_not_returning_iterator.rs +++ b/tests/ui/iter_not_returning_iterator.rs @@ -44,4 +44,16 @@ impl Iterator for Counter { } } +trait Iter { + type I; + fn iter(&self) -> Self::I; +} + +impl Iter for () { + type I = core::slice::Iter<'static, ()>; + fn iter(&self) -> Self::I { + [].iter() + } +} + fn main() {} diff --git a/tests/ui/iter_not_returning_iterator.stderr b/tests/ui/iter_not_returning_iterator.stderr index 2273cd0be66ff..ddb2b88d5f90b 100644 --- a/tests/ui/iter_not_returning_iterator.stderr +++ b/tests/ui/iter_not_returning_iterator.stderr @@ -12,5 +12,11 @@ error: this method is named `iter_mut` but its return type does not implement `I LL | fn iter_mut(&self) -> Counter2 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: this method is named `iter` but its return type does not implement `Iterator` + --> $DIR/iter_not_returning_iterator.rs:49:5 + | +LL | fn iter(&self) -> Self::I; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors From d98339d3e002b8b1bddf225e98c3140d7f1cecc7 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 5 Jan 2022 13:57:45 -0500 Subject: [PATCH 25/72] Handle type projections in `iter_not_returning_iterator` --- clippy_lints/src/iter_not_returning_iterator.rs | 4 ++++ tests/ui/iter_not_returning_iterator.rs | 8 ++++++++ tests/ui/iter_not_returning_iterator.stderr | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index 91d3c00039a02..017a8a779d942 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -67,6 +67,10 @@ impl LateLintPass<'_> for IterNotReturningIterator { fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) { if sig.decl.implicit_self.has_implicit_self() { let ret_ty = cx.tcx.fn_sig(fn_id).skip_binder().output(); + let ret_ty = cx + .tcx + .try_normalize_erasing_regions(cx.param_env, ret_ty) + .unwrap_or(ret_ty); if cx .tcx .get_diagnostic_item(sym::Iterator) diff --git a/tests/ui/iter_not_returning_iterator.rs b/tests/ui/iter_not_returning_iterator.rs index f5ee044576c53..2c91e02e84223 100644 --- a/tests/ui/iter_not_returning_iterator.rs +++ b/tests/ui/iter_not_returning_iterator.rs @@ -44,6 +44,7 @@ impl Iterator for Counter { } } +// Issue #8225 trait Iter { type I; fn iter(&self) -> Self::I; @@ -56,4 +57,11 @@ impl Iter for () { } } +struct S; +impl S { + fn iter(&self) -> <() as Iter>::I { + ().iter() + } +} + fn main() {} diff --git a/tests/ui/iter_not_returning_iterator.stderr b/tests/ui/iter_not_returning_iterator.stderr index ddb2b88d5f90b..44f0295583695 100644 --- a/tests/ui/iter_not_returning_iterator.stderr +++ b/tests/ui/iter_not_returning_iterator.stderr @@ -13,7 +13,7 @@ LL | fn iter_mut(&self) -> Counter2 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this method is named `iter` but its return type does not implement `Iterator` - --> $DIR/iter_not_returning_iterator.rs:49:5 + --> $DIR/iter_not_returning_iterator.rs:50:5 | LL | fn iter(&self) -> Self::I; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ From 94fe6719de0846b2ea88ff2dc517074c8c68c334 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 6 Jan 2022 13:06:24 +0000 Subject: [PATCH 26/72] Allow running lintcheck with a renamed rust-clippy dir --- lintcheck/src/main.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 53e669254cfea..9af8dcc7726f0 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -665,16 +665,6 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { logs_modified < clippy_modified } -fn is_in_clippy_root() -> bool { - if let Ok(pb) = std::env::current_dir() { - if let Some(file) = pb.file_name() { - return file == PathBuf::from("rust-clippy"); - } - } - - false -} - /// lintchecks `main()` function /// /// # Panics @@ -683,7 +673,7 @@ fn is_in_clippy_root() -> bool { /// or if lintcheck is executed from the wrong directory (aka none-repo-root) pub fn main() { // assert that we launch lintcheck from the repo root (via cargo lintcheck) - if !is_in_clippy_root() { + if std::fs::metadata("lintcheck/Cargo.toml").is_err() { eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively."); std::process::exit(3); } From c34e3f0f83660e02a7545ac52f4c8023044868c2 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 18 Nov 2021 22:33:49 +0000 Subject: [PATCH 27/72] Update clippy for associated item changes --- clippy_lints/src/len_zero.rs | 12 +++++++----- clippy_lints/src/non_copy_const.rs | 11 ++++++----- clippy_lints/src/use_self.rs | 7 +++---- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 20e6220ec7d3a..64f6d62fbdcd8 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -214,14 +214,14 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items { let mut current_and_super_traits = DefIdSet::default(); fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx); + let is_empty = sym!(is_empty); let is_empty_method_found = current_and_super_traits .iter() - .flat_map(|&i| cx.tcx.associated_items(i).in_definition_order()) + .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(is_empty)) .any(|i| { i.kind == ty::AssocKind::Fn && i.fn_has_self_parameter - && i.ident.name == sym!(is_empty) && cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1 }); @@ -458,7 +458,7 @@ fn is_empty_array(expr: &Expr<'_>) -> bool { fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { - if item.kind == ty::AssocKind::Fn && item.ident.name.as_str() == "is_empty" { + if item.kind == ty::AssocKind::Fn { let sig = cx.tcx.fn_sig(item.def_id); let ty = sig.skip_binder(); ty.inputs().len() == 1 @@ -469,10 +469,11 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks the inherent impl's items for an `is_empty(self)` method. fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { + let is_empty = sym!(is_empty); cx.tcx.inherent_impls(id).iter().any(|imp| { cx.tcx .associated_items(*imp) - .in_definition_order() + .filter_by_name_unhygienic(is_empty) .any(|item| is_is_empty(cx, item)) }) } @@ -480,9 +481,10 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); match ty.kind() { ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| { + let is_empty = sym!(is_empty); cx.tcx .associated_items(principal.def_id()) - .in_definition_order() + .filter_by_name_unhygienic(is_empty) .any(|item| is_is_empty(cx, item)) }), ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 074ba9e92ba4d..7d2ff083b7e07 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -12,11 +12,10 @@ use rustc_hir::def_id::DefId; use rustc_hir::{ BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, }; -use rustc_infer::traits::specialization_graph; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::mir::interpret::{ConstValue, ErrorHandled}; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, AssocKind, Const, Ty}; +use rustc_middle::ty::{self, Const, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; @@ -293,8 +292,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // Lint a trait impl item only when the definition is a generic type, // assuming an assoc const is not meant to be an interior mutable type. if let Some(of_trait_def_id) = of_trait_ref.trait_def_id(); - if let Some(of_assoc_item) = specialization_graph::Node::Trait(of_trait_def_id) - .item(cx.tcx, impl_item.ident, AssocKind::Const, of_trait_def_id); + if let Some(of_assoc_item) = cx + .tcx + .associated_item(impl_item.def_id) + .trait_item_def_id; if cx .tcx .layout_of(cx.tcx.param_env(of_trait_def_id).and( @@ -303,7 +304,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // and, in that case, the definition is *not* generic. cx.tcx.normalize_erasing_regions( cx.tcx.param_env(of_trait_def_id), - cx.tcx.type_of(of_assoc_item.def_id), + cx.tcx.type_of(of_assoc_item), ), )) .is_err(); diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 059f7f647f88f..a86db58741eb6 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -13,7 +13,6 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::AssocKind; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -143,10 +142,10 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // trait, not in the impl of the trait. let trait_method = cx .tcx - .associated_items(impl_trait_ref.def_id) - .find_by_name_and_kind(cx.tcx, impl_item.ident, AssocKind::Fn, impl_trait_ref.def_id) + .associated_item(impl_item.def_id) + .trait_item_def_id .expect("impl method matches a trait method"); - let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id); + let trait_method_sig = cx.tcx.fn_sig(trait_method); let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig); // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the From 366234a515ab6538c83e5b58481b2c45abd72d76 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sat, 8 Jan 2022 09:09:52 -0500 Subject: [PATCH 28/72] Add `unnecessary_to_owned` "Known problems" section --- clippy_lints/src/methods/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4e33b2ff14cde..6572c7111b333 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1895,6 +1895,11 @@ declare_clippy_lint! { /// ### Why is this bad? /// The unnecessary calls result in useless allocations. /// + /// ### Known problems + /// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an + /// owned copy of a resource and the resource is later used mutably. See + /// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148). + /// /// ### Example /// ```rust /// let path = std::path::Path::new("x"); From 3ea52086fd74e7b83bec302baff09eda619a02f3 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Sat, 8 Jan 2022 14:12:53 -0800 Subject: [PATCH 29/72] Set binary-dep-depinfo in .cargo/config.toml Fixes #8248 According to https://doc.rust-lang.org/cargo/reference/unstable this seems to be the right place to set it, and it does fix the build for me. I haven't removed the other `rustflags` because perhaps it's needed on different cargo/rust versions? --- .cargo/config.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.cargo/config.toml b/.cargo/config.toml index 688473f2f9bfc..c9ae0a961b644 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -8,3 +8,6 @@ collect-metadata = "test --test dogfood --features metadata-collector-lint -- ru # -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"] target-dir = "target" + +[unstable] +binary-dep-depinfo = true From 1288b80affc300819a38cf56de9d4776f3e50e1f Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 18 Dec 2021 20:07:58 +0800 Subject: [PATCH 30/72] rustc_metadata: Optimize and document module children decoding --- clippy_utils/src/lib.rs | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9179e67c4f4ee..bd6851d1fbba2 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -82,7 +82,6 @@ use rustc_hir::{ TraitItemKind, TraitRef, TyKind, UnOp, ArrayLen }; use rustc_lint::{LateContext, Level, Lint, LintContext}; -use rustc_middle::hir::exports::Export; use rustc_middle::hir::map::Map; use rustc_middle::hir::place::PlaceBase; use rustc_middle::ty as rustc_ty; @@ -523,10 +522,21 @@ 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<&'tcx Export> { - tcx.item_children(def_id) - .iter() - .find(|item| item.ident.name.as_str() == name) + fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option { + match tcx.def_kind(def_id) { + DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx + .item_children(def_id) + .iter() + .find(|item| item.ident.name.as_str() == name) + .map(|child| child.res.expect_non_local()), + DefKind::Impl => tcx + .associated_item_def_ids(def_id) + .iter() + .copied() + .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name) + .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)), + _ => None, + } } let (krate, first, path) = match *path { @@ -543,15 +553,12 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res { let last = path .iter() .copied() - // `get_def_path` seems to generate these empty segments for extern blocks. - // We can just ignore them. - .filter(|segment| !segment.is_empty()) // for each segment, find the child item - .try_fold(first, |item, segment| { - let def_id = item.res.def_id(); + .try_fold(first, |res, segment| { + let def_id = res.def_id(); if let Some(item) = item_child_by_name(tcx, def_id, segment) { Some(item) - } else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { + } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { // it is not a child item so check inherent impl items tcx.inherent_impls(def_id) .iter() @@ -560,7 +567,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res { None } }); - try_res!(last).res.expect_non_local() + try_res!(last).expect_non_local() } /// Convenience function to get the `DefId` of a trait by path. From c8ea0420cb6ce2262a93a919c2bb70378f5cd3ae Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 23 Dec 2021 16:12:34 +0800 Subject: [PATCH 31/72] rustc_metadata: Rename `item_children(_untracked)` to `module_children(_untracked)` And `each_child_of_item` to `for_each_module_child` --- clippy_lints/src/macro_use.rs | 2 +- clippy_lints/src/utils/internal_lints.rs | 4 ++-- clippy_utils/src/lib.rs | 2 +- tests/ui/macro_use_imports.fixed | 2 +- tests/ui/macro_use_imports.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 50d80e6a1d224..41f5a913b316e 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { if let Res::Def(DefKind::Mod, id) = path.res; if !id.is_local(); then { - for kid in cx.tcx.item_children(id).iter() { + for kid in cx.tcx.module_children(id).iter() { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { let span = mac_attr.span; let def_path = cx.tcx.def_path_str(mac_id); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index e98dcd3cf983b..7d196af7a53f4 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -924,7 +924,7 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { let lang_item_path = cx.get_def_path(*item_def_id); if path_syms.starts_with(&lang_item_path) { if let [item] = &path_syms[lang_item_path.len()..] { - for child in cx.tcx.item_children(*item_def_id) { + for child in cx.tcx.module_children(*item_def_id) { if child.ident.name == *item { return true; } @@ -984,7 +984,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { if let Some(def_id) = path_to_res(cx, module).opt_def_id() { - for item in cx.tcx.item_children(def_id).iter() { + for item in cx.tcx.module_children(def_id).iter() { if_chain! { if let Res::Def(DefKind::Const, item_def_id) = item.res; let ty = cx.tcx.type_of(item_def_id); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index bd6851d1fbba2..91ebc7ea89cc0 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -525,7 +525,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 { match tcx.def_kind(def_id) { DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx - .item_children(def_id) + .module_children(def_id) .iter() .find(|item| item.ident.name.as_str() == name) .map(|child| child.res.expect_non_local()), diff --git a/tests/ui/macro_use_imports.fixed b/tests/ui/macro_use_imports.fixed index 9171558f3a2d7..306ea50258da0 100644 --- a/tests/ui/macro_use_imports.fixed +++ b/tests/ui/macro_use_imports.fixed @@ -40,7 +40,7 @@ mod a { } } -// issue #7015, ICE due to calling `item_children` with local `DefId` +// issue #7015, ICE due to calling `module_children` with local `DefId` #[macro_use] use a as b; diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index cd01fd43f6d32..e26a7545ea6f8 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -40,7 +40,7 @@ mod a { } } -// issue #7015, ICE due to calling `item_children` with local `DefId` +// issue #7015, ICE due to calling `module_children` with local `DefId` #[macro_use] use a as b; From f690978023d7465d05abe9a1288100f192e74193 Mon Sep 17 00:00:00 2001 From: dswij Date: Thu, 6 Jan 2022 18:43:44 +0800 Subject: [PATCH 32/72] cover trait for `trait_duplication_in_bounds` --- clippy_lints/src/trait_bounds.rs | 68 ++++++++++++++++++--- tests/ui/trait_duplication_in_bounds.rs | 37 +++++++++++ tests/ui/trait_duplication_in_bounds.stderr | 34 ++++++++++- 3 files changed, 128 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index f2848ad379093..c91b24462c028 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -3,10 +3,14 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{SpanlessEq, SpanlessHash}; use core::hash::{Hash, Hasher}; use if_chain::if_chain; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; -use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, Ty, TyKind, WherePredicate}; +use rustc_hir::def::Res; +use rustc_hir::{ + GenericBound, Generics, Item, ItemKind, Node, ParamName, Path, PathSegment, QPath, TraitItem, Ty, TyKind, + WherePredicate, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -84,6 +88,46 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { self.check_type_repetition(cx, gen); check_trait_bound_duplication(cx, gen); } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) { + let Generics { where_clause, .. } = &item.generics; + let mut self_bounds_set = FxHashSet::default(); + + for predicate in where_clause.predicates { + if_chain! { + if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if !bound_predicate.span.from_expansion(); + if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; + if let Some(PathSegment { res: Some(Res::SelfTy(Some(def_id), _)), .. }) = segments.first(); + + if let Some( + Node::Item( + Item { + kind: ItemKind::Trait(_, _, _, self_bounds, _), + .. } + ) + ) = cx.tcx.hir().get_if_local(*def_id); + then { + if self_bounds_set.is_empty() { + for bound in self_bounds.iter() { + let Some((self_res, _)) = get_trait_res_span_from_bound(bound) else { continue }; + self_bounds_set.insert(self_res); + } + } + + bound_predicate + .bounds + .iter() + .filter_map(get_trait_res_span_from_bound) + .for_each(|(trait_item_res, span)| { + if self_bounds_set.get(&trait_item_res).is_some() { + emit_lint(cx, span); + } + }); + } + } + } + } } fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> { @@ -198,17 +242,21 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { if let Some((_, span_direct)) = trait_resolutions_direct .iter() .find(|(res_direct, _)| *res_direct == res_where) { - span_lint_and_help( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - *span_direct, - "this trait bound is already specified in the where clause", - None, - "consider removing this trait bound", - ); + emit_lint(cx, *span_direct); } } } } } } + +fn emit_lint(cx: &LateContext<'_>, span: Span) { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + span, + "this trait bound is already specified in the where clause", + None, + "consider removing this trait bound", + ); +} diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index cb2b0054e352b..3e49012627389 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -28,4 +28,41 @@ where unimplemented!(); } +trait T: Default { + fn f() + where + Self: Default; +} + +trait U: Default { + fn f() + where + Self: Clone; +} + +trait ZZ: Default { + fn f() + where + Self: Default + Clone; +} + +trait BadTrait: Default + Clone { + fn f() + where + Self: Default + Clone; +} + +#[derive(Default, Clone)] +struct Life {} + +impl T for Life { + // this should not warn + fn f() {} +} + +impl U for Life { + // this should not warn + fn f() {} +} + fn main() {} diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr index 027e1c7520412..6326139c18743 100644 --- a/tests/ui/trait_duplication_in_bounds.stderr +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -19,5 +19,37 @@ LL | fn bad_foo(arg0: T, arg1: Z) | = help: consider removing this trait bound -error: aborting due to 2 previous errors +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:34:15 + | +LL | Self: Default; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:46:15 + | +LL | Self: Default + Clone; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:52:15 + | +LL | Self: Default + Clone; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:52:25 + | +LL | Self: Default + Clone; + | ^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 6 previous errors From a6f80fccb19a52305ff88d960b4bb4e74df3d8b2 Mon Sep 17 00:00:00 2001 From: bors Date: Sun, 9 Jan 2022 14:27:36 +0000 Subject: [PATCH 33/72] new lint: `single_char_lifetime_names` This pull request adds a lint against single character lifetime names, as they might not divulge enough information about the purpose of the lifetime. This can make code harder to understand. I placed this in `restriction` rather than `pedantic` (as suggested in #8233) since most of the Rust ecosystem already uses single character lifetime names (to my knowledge, at least) and since single character lifetime names aren't incorrect. I'd be happy to change this upon request, however. Fixes #8233. - [x] Followed lint naming conventions - [x] Added passing UI tests (including committed `.stderr` file) - [x] `cargo test` passes locally - [x] Executed `cargo dev update_lints` - [x] Added lint documentation - [x] Run `cargo dev fmt` changelog: new lint: [`single_char_lifetime_names`] --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_restriction.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/single_char_lifetime_names.rs | 63 +++++++++++++++++++ tests/ui/single_char_lifetime_names.rs | 43 +++++++++++++ tests/ui/single_char_lifetime_names.stderr | 43 +++++++++++++ 7 files changed, 154 insertions(+) create mode 100644 clippy_lints/src/single_char_lifetime_names.rs create mode 100644 tests/ui/single_char_lifetime_names.rs create mode 100644 tests/ui/single_char_lifetime_names.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 27bac4718b6c3..83fd8396bc72f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3253,6 +3253,7 @@ Released 2018-09-13 [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names [`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str +[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 002122793f3b6..f9241d943b2cd 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -435,6 +435,7 @@ store.register_lints(&[ shadow::SHADOW_REUSE, shadow::SHADOW_SAME, shadow::SHADOW_UNRELATED, + single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES, single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index eab389a9bd892..e7e2798da7da9 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -54,6 +54,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(shadow::SHADOW_REUSE), LintId::of(shadow::SHADOW_SAME), LintId::of(shadow::SHADOW_UNRELATED), + LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES), LintId::of(strings::STRING_ADD), LintId::of(strings::STRING_SLICE), LintId::of(strings::STRING_TO_STRING), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4687a1e28797..c31acb5f4ef84 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -351,6 +351,7 @@ mod self_named_constructors; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; +mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; mod slow_vector_initialization; @@ -858,6 +859,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields)); + store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/single_char_lifetime_names.rs b/clippy_lints/src/single_char_lifetime_names.rs new file mode 100644 index 0000000000000..ee82666b5affe --- /dev/null +++ b/clippy_lints/src/single_char_lifetime_names.rs @@ -0,0 +1,63 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{GenericParam, GenericParamKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for lifetimes with names which are one character + /// long. + /// + /// ### Why is this bad? + /// A single character is likely not enough to express the + /// purpose of a lifetime. Using a longer name can make code + /// easier to understand, especially for those who are new to + /// Rust. + /// + /// ### Known problems + /// Rust programmers and learning resources tend to use single + /// character lifetimes, so this lint is at odds with the + /// ecosystem at large. In addition, the lifetime's purpose may + /// be obvious or, rarely, expressible in one character. + /// + /// ### Example + /// ```rust + /// struct DiagnosticCtx<'a> { + /// source: &'a str, + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct DiagnosticCtx<'src> { + /// source: &'src str, + /// } + /// ``` + #[clippy::version = "1.59.0"] + pub SINGLE_CHAR_LIFETIME_NAMES, + restriction, + "warns against single-character lifetime names" +} + +declare_lint_pass!(SingleCharLifetimeNames => [SINGLE_CHAR_LIFETIME_NAMES]); + +impl EarlyLintPass for SingleCharLifetimeNames { + fn check_generic_param(&mut self, ctx: &EarlyContext<'_>, param: &GenericParam) { + if in_external_macro(ctx.sess, param.ident.span) { + return; + } + + if let GenericParamKind::Lifetime = param.kind { + if !param.is_placeholder && param.ident.as_str().len() <= 2 { + span_lint_and_help( + ctx, + SINGLE_CHAR_LIFETIME_NAMES, + param.ident.span, + "single-character lifetime names are likely uninformative", + None, + "use a more informative name", + ); + } + } + } +} diff --git a/tests/ui/single_char_lifetime_names.rs b/tests/ui/single_char_lifetime_names.rs new file mode 100644 index 0000000000000..261d8bc7260ca --- /dev/null +++ b/tests/ui/single_char_lifetime_names.rs @@ -0,0 +1,43 @@ +#![warn(clippy::single_char_lifetime_names)] + +// Lifetimes should only be linted when they're introduced +struct DiagnosticCtx<'a, 'b> +where + 'a: 'b, +{ + _source: &'a str, + _unit: &'b (), +} + +// Only the lifetimes on the `impl`'s generics should be linted +impl<'a, 'b> DiagnosticCtx<'a, 'b> { + fn new(source: &'a str, unit: &'b ()) -> DiagnosticCtx<'a, 'b> { + Self { + _source: source, + _unit: unit, + } + } +} + +// No lifetimes should be linted here +impl<'src, 'unit> DiagnosticCtx<'src, 'unit> { + fn new_pass(source: &'src str, unit: &'unit ()) -> DiagnosticCtx<'src, 'unit> { + Self { + _source: source, + _unit: unit, + } + } +} + +// Only 'a should be linted here +fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) { + base.split_once(other) + .map(|(left, right)| (left, Some(right))) + .unwrap_or((base, None)) +} + +fn main() { + let src = "loop {}"; + let unit = (); + DiagnosticCtx::new(src, &unit); +} diff --git a/tests/ui/single_char_lifetime_names.stderr b/tests/ui/single_char_lifetime_names.stderr new file mode 100644 index 0000000000000..013b64f46a8ce --- /dev/null +++ b/tests/ui/single_char_lifetime_names.stderr @@ -0,0 +1,43 @@ +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:4:22 + | +LL | struct DiagnosticCtx<'a, 'b> + | ^^ + | + = note: `-D clippy::single-char-lifetime-names` implied by `-D warnings` + = help: use a more informative name + +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:4:26 + | +LL | struct DiagnosticCtx<'a, 'b> + | ^^ + | + = help: use a more informative name + +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:13:6 + | +LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { + | ^^ + | + = help: use a more informative name + +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:13:10 + | +LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { + | ^^ + | + = help: use a more informative name + +error: single-character lifetime names are likely uninformative + --> $DIR/single_char_lifetime_names.rs:33:15 + | +LL | fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) { + | ^^ + | + = help: use a more informative name + +error: aborting due to 5 previous errors + From 16d848891d07043c14cc5b7a692c42d6926c32eb Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 9 Jan 2022 18:16:05 -0600 Subject: [PATCH 34/72] Combine internal features in clippy_utils --- .github/workflows/clippy.yml | 2 +- .github/workflows/clippy_bors.yml | 2 +- clippy_lints/Cargo.toml | 4 ++-- clippy_utils/Cargo.toml | 3 +-- clippy_utils/src/diagnostics.rs | 2 +- clippy_utils/src/paths.rs | 30 +++++++++++++++--------------- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 3d8c39408a924..a14e530df4ec5 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -59,7 +59,7 @@ jobs: working-directory: clippy_lints - name: Test clippy_utils - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal working-directory: clippy_utils - name: Test rustc_tools_util diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 8b644aa28176b..06522606347d8 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -122,7 +122,7 @@ jobs: working-directory: clippy_lints - name: Test clippy_utils - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal working-directory: clippy_utils - name: Test rustc_tools_util diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 0661c2803864c..639138ac9c52d 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -30,8 +30,8 @@ url = { version = "2.2", features = ["serde"] } [features] deny-warnings = ["clippy_utils/deny-warnings"] # build clippy with internal lints enabled, off by default -internal-lints = ["clippy_utils/internal-lints"] -metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"] +internal-lints = ["clippy_utils/internal"] +metadata-collector-lint = ["serde_json", "clippy_utils/internal"] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index af1e2eb4dd11f..565dd1faa3d7f 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -11,8 +11,7 @@ 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/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index d47b002ad7aca..ca222c3d66995 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/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/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 27db53a6e6d58..c06a1f826fa2a 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -5,16 +5,16 @@ //! See 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 @@ -45,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"]; @@ -71,9 +71,9 @@ 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"]; @@ -85,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"]; @@ -179,17 +179,17 @@ pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "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"]; From e2ce4f9462c1d65b4dce084186c0e532c27fe5a3 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 9 Jan 2022 18:22:38 -0600 Subject: [PATCH 35/72] Combine internal features in clippy_lints --- .cargo/config.toml | 2 +- .github/workflows/clippy.yml | 6 ++-- .github/workflows/clippy_bors.yml | 6 ++-- Cargo.toml | 3 +- clippy_dev/src/update_lints.rs | 2 +- clippy_lints/Cargo.toml | 3 +- clippy_lints/src/lib.register_lints.rs | 28 +++++++++---------- clippy_lints/src/lib.rs | 13 ++++----- clippy_lints/src/utils/conf.rs | 2 +- clippy_lints/src/utils/internal_lints.rs | 2 +- .../internal_lints/metadata_collector.rs | 3 +- clippy_lints/src/utils/mod.rs | 2 +- tests/compile-test.rs | 2 +- tests/dogfood.rs | 10 +++---- 14 files changed, 39 insertions(+), 45 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index c9ae0a961b644..f3dd9275a42be 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,7 +2,7 @@ uitest = "test --test compile-test" dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- " -collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored" +collect-metadata = "test --test dogfood --features internal -- run_metadata_collection_lint --ignored" [build] # -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index a14e530df4ec5..8b5e814b1772e 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -49,13 +49,13 @@ jobs: echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - name: Build - run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo build --features deny-warnings,internal - name: Test - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal - name: Test clippy_lints - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal working-directory: clippy_lints - name: Test clippy_utils diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 06522606347d8..eb045ec30dc31 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -112,13 +112,13 @@ jobs: echo "$SYSROOT/bin" >> $GITHUB_PATH - name: Build - run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo build --features deny-warnings,internal - name: Test - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal - name: Test clippy_lints - run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint + run: cargo test --features deny-warnings,internal working-directory: clippy_lints - name: Test clippy_utils diff --git a/Cargo.toml b/Cargo.toml index 79a7ec92071c0..cb49dd84c39a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,8 +57,7 @@ rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } [features] deny-warnings = ["clippy_lints/deny-warnings"] integration = ["tempfile"] -internal-lints = ["clippy_lints/internal-lints"] -metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"] +internal = ["clippy_lints/internal"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 8dd073ef405a1..d368ef1f46a2a 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -321,7 +321,7 @@ fn gen_register_lint_list<'a>( for (is_public, module_name, lint_name) in details { if !is_public { - output.push_str(" #[cfg(feature = \"internal-lints\")]\n"); + output.push_str(" #[cfg(feature = \"internal\")]\n"); } output.push_str(&format!(" {}::{},\n", module_name, lint_name)); } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 639138ac9c52d..e9f87b60ece43 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -30,8 +30,7 @@ url = { version = "2.2", features = ["serde"] } [features] deny-warnings = ["clippy_utils/deny-warnings"] # build clippy with internal lints enabled, off by default -internal-lints = ["clippy_utils/internal"] -metadata-collector-lint = ["serde_json", "clippy_utils/internal"] +internal = ["clippy_utils/internal", "serde_json"] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index f9241d943b2cd..832178c2db5e5 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -3,33 +3,33 @@ // Manual edits will be overwritten. store.register_lints(&[ - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::CLIPPY_LINTS_INTERNAL, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::COMPILER_LINT_FUNCTIONS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::DEFAULT_LINT, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::IF_CHAIN_STYLE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::INTERNING_DEFINED_SYMBOL, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::INVALID_PATHS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::LINT_WITHOUT_LINT_PASS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::OUTER_EXPN_EXPN_DATA, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::PRODUCE_ICE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::UNNECESSARY_SYMBOL_STR, absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, approx_const::APPROX_CONSTANT, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c31acb5f4ef84..0d765c2bcde1b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -153,12 +153,9 @@ macro_rules! declare_clippy_lint { }; } -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] mod deprecated_lints; -#[cfg_attr( - any(feature = "internal-lints", feature = "metadata-collector-lint"), - allow(clippy::missing_clippy_version_attribute) -)] +#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; // begin lints modules, do not remove this comment, it’s used in `update_lints` @@ -472,7 +469,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: include!("lib.register_restriction.rs"); include!("lib.register_pedantic.rs"); - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] include!("lib.register_internal.rs"); include!("lib.register_all.rs"); @@ -484,7 +481,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: include!("lib.register_cargo.rs"); include!("lib.register_nursery.rs"); - #[cfg(feature = "metadata-collector-lint")] + #[cfg(feature = "internal")] { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new())); @@ -493,7 +490,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: } // all the internal lints - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] { store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 9c83d30eb9cc1..4e1c538c1c20a 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -113,7 +113,7 @@ macro_rules! define_Conf { } } - #[cfg(feature = "metadata-collector-lint")] + #[cfg(feature = "internal")] pub mod metadata { use crate::utils::internal_lints::metadata_collector::ClippyConfiguration; diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 165004e5b413b..67faca70d5cd6 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -35,7 +35,7 @@ use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] pub mod metadata_collector; declare_clippy_lint! { diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 7707eebd6223e..02f5d45cc18f4 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -1,8 +1,7 @@ //! This lint is used to collect metadata about clippy lints. This metadata is exported as a json //! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html) //! -//! This module and therefor the entire lint is guarded by a feature flag called -//! `metadata-collector-lint` +//! This module and therefore the entire lint is guarded by a feature flag called `internal` //! //! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index b67448e3a5740..dc385ebacba66 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1,5 +1,5 @@ pub mod author; pub mod conf; pub mod inspector; -#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))] +#[cfg(feature = "internal")] pub mod internal_lints; diff --git a/tests/compile-test.rs b/tests/compile-test.rs index d602d2042ee7f..762b67fc5854d 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -15,7 +15,7 @@ use std::path::{Path, PathBuf}; mod cargo; // whether to run internal tests or not -const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints"); +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); /// All crates used in UI tests are listed here static TEST_DEPENDENCIES: &[&str] = &[ diff --git a/tests/dogfood.rs b/tests/dogfood.rs index a37cdfed126f6..6f7de9c67a9b7 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -42,8 +42,8 @@ fn dogfood_clippy() { .args(&["-D", "clippy::pedantic"]) .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - // internal lints only exist if we build with the internal-lints feature - if cfg!(feature = "internal-lints") { + // internal lints only exist if we build with the internal feature + if cfg!(feature = "internal") { command.args(&["-D", "clippy::internal"]); } @@ -180,7 +180,7 @@ fn dogfood_subprojects() { #[test] #[ignore] -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] fn run_metadata_collection_lint() { use std::fs::File; use std::time::SystemTime; @@ -233,8 +233,8 @@ fn run_clippy_for_project(project: &str) { .args(&["-D", "clippy::pedantic"]) .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - // internal lints only exist if we build with the internal-lints feature - if cfg!(feature = "internal-lints") { + // internal lints only exist if we build with the internal feature + if cfg!(feature = "internal") { command.args(&["-D", "clippy::internal"]); } From f4dc348ad580cb5958f724a1a5ac7b538ef35515 Mon Sep 17 00:00:00 2001 From: dswij Date: Mon, 10 Jan 2022 12:45:01 +0800 Subject: [PATCH 36/72] `trait_duplication_in_bounds` Update description and add test --- clippy_lints/src/trait_bounds.rs | 29 ++++++++++++--------- tests/ui/trait_duplication_in_bounds.rs | 8 ++++++ tests/ui/trait_duplication_in_bounds.stderr | 24 +++++++++++------ 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index c91b24462c028..6369aafe3f973 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -121,7 +121,14 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { .filter_map(get_trait_res_span_from_bound) .for_each(|(trait_item_res, span)| { if self_bounds_set.get(&trait_item_res).is_some() { - emit_lint(cx, span); + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + span, + "this trait bound is already specified in trait declaration", + None, + "consider removing this trait bound", + ); } }); } @@ -242,21 +249,17 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { if let Some((_, span_direct)) = trait_resolutions_direct .iter() .find(|(res_direct, _)| *res_direct == res_where) { - emit_lint(cx, *span_direct); + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + *span_direct, + "this trait bound is already specified in the where clause", + None, + "consider removing this trait bound", + ); } } } } } } - -fn emit_lint(cx: &LateContext<'_>, span: Span) { - span_lint_and_help( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - span, - "this trait bound is already specified in the where clause", - None, - "consider removing this trait bound", - ); -} diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index 3e49012627389..2edb202892afc 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -41,6 +41,8 @@ trait U: Default { } trait ZZ: Default { + fn g(); + fn h(); fn f() where Self: Default + Clone; @@ -50,6 +52,12 @@ trait BadTrait: Default + Clone { fn f() where Self: Default + Clone; + fn g() + where + Self: Default; + fn h() + where + Self: Copy; } #[derive(Default, Clone)] diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr index 6326139c18743..e0c7a7ec618ed 100644 --- a/tests/ui/trait_duplication_in_bounds.stderr +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -19,7 +19,7 @@ LL | fn bad_foo(arg0: T, arg1: Z) | = help: consider removing this trait bound -error: this trait bound is already specified in the where clause +error: this trait bound is already specified in trait declaration --> $DIR/trait_duplication_in_bounds.rs:34:15 | LL | Self: Default; @@ -27,29 +27,37 @@ LL | Self: Default; | = help: consider removing this trait bound -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:46:15 +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:48:15 | LL | Self: Default + Clone; | ^^^^^^^ | = help: consider removing this trait bound -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:52:15 +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:54:15 | LL | Self: Default + Clone; | ^^^^^^^ | = help: consider removing this trait bound -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:52:25 +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:54:25 | LL | Self: Default + Clone; | ^^^^^ | = help: consider removing this trait bound -error: aborting due to 6 previous errors +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:57:15 + | +LL | Self: Default; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 7 previous errors From cf86cee4fe68a8bc6919eaba6689f6c31cfa1b5f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 10 Jan 2022 23:36:13 +0900 Subject: [PATCH 37/72] Downgrade mutex_atomic to nursery --- clippy_lints/src/lib.register_all.rs | 1 - clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.register_perf.rs | 1 - clippy_lints/src/mutex_atomic.rs | 2 +- tests/ui/mutex_atomic.rs | 1 + tests/ui/mutex_atomic.stderr | 14 +++++++------- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 944411087e951..47b85caa5803e 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -204,7 +204,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index e3cf067001834..d21d9506ede11 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -17,6 +17,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(let_if_seq::USELESS_LET_IF_SEQ), LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), + LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs index 2ea0b696f1feb..f9ffd4cf829fa 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/clippy_lints/src/lib.register_perf.rs @@ -19,7 +19,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(methods::SINGLE_CHAR_PATTERN), LintId::of(methods::UNNECESSARY_TO_OWNED), LintId::of(misc::CMP_OWNED), - LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 816377fe65e9f..73823779e493d 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub MUTEX_ATOMIC, - perf, + nursery, "using a mutex where an atomic value could be used instead" } diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs index b9d78b7f47924..71063d9ce4634 100644 --- a/tests/ui/mutex_atomic.rs +++ b/tests/ui/mutex_atomic.rs @@ -1,5 +1,6 @@ #![warn(clippy::all)] #![warn(clippy::mutex_integer)] +#![warn(clippy::mutex_atomic)] fn main() { use std::sync::Mutex; diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index a3511ba708a88..ef7ad9b757614 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,5 +1,5 @@ error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:6:5 + --> $DIR/mutex_atomic.rs:7:5 | LL | Mutex::new(true); | ^^^^^^^^^^^^^^^^ @@ -7,31 +7,31 @@ LL | Mutex::new(true); = note: `-D clippy::mutex-atomic` implied by `-D warnings` error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:7:5 + --> $DIR/mutex_atomic.rs:8:5 | LL | Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:8:5 + --> $DIR/mutex_atomic.rs:9:5 | LL | Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:10:5 + --> $DIR/mutex_atomic.rs:11:5 | LL | Mutex::new(&x as *const u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:11:5 + --> $DIR/mutex_atomic.rs:12:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:12:5 + --> $DIR/mutex_atomic.rs:13:5 | LL | Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | Mutex::new(0u32); = note: `-D clippy::mutex-integer` implied by `-D warnings` error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:13:5 + --> $DIR/mutex_atomic.rs:14:5 | LL | Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ From 5f143c603eb643e1da9ccab60d53d10cdc7e0e06 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 11 Jan 2022 02:17:14 +0900 Subject: [PATCH 38/72] Warn disallowed_methods and disallowed_types by default --- clippy_lints/src/disallowed_methods.rs | 5 ++++- clippy_lints/src/disallowed_types.rs | 5 ++++- clippy_lints/src/lib.register_all.rs | 2 ++ clippy_lints/src/lib.register_nursery.rs | 2 -- clippy_lints/src/lib.register_style.rs | 2 ++ 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 6d4065907fb45..6d5b1efe270c4 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -11,6 +11,9 @@ declare_clippy_lint! { /// ### What it does /// Denies the configured methods and functions in clippy.toml /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// methods are defined in the clippy.toml file. + /// /// ### Why is this bad? /// Some methods are undesirable in certain contexts, and it's beneficial to /// lint for them as needed. @@ -49,7 +52,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.49.0"] pub DISALLOWED_METHODS, - nursery, + style, "use of a disallowed method call" } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index eaed40327136f..ea4b49b46fe9f 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -14,6 +14,9 @@ declare_clippy_lint! { /// ### What it does /// Denies the configured types in clippy.toml. /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// types are defined in the clippy.toml file. + /// /// ### Why is this bad? /// Some types are undesirable in certain contexts. /// @@ -44,7 +47,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.55.0"] pub DISALLOWED_TYPES, - nursery, + style, "use of disallowed types" } #[derive(Clone, Debug)] diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 944411087e951..5c7835db1aacc 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -37,6 +37,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(disallowed_methods::DISALLOWED_METHODS), + LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), LintId::of(doc::NEEDLESS_DOCTEST_MAIN), LintId::of(double_comparison::DOUBLE_COMPARISONS), diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index e3cf067001834..4d4317cc98432 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -6,8 +6,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(copies::BRANCHES_SHARING_CODE), - LintId::of(disallowed_methods::DISALLOWED_METHODS), - LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(equatable_if_let::EQUATABLE_IF_LET), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 1a0b869d40adb..57de34f41ace7 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -16,6 +16,8 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), + LintId::of(disallowed_methods::DISALLOWED_METHODS), + LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), LintId::of(doc::NEEDLESS_DOCTEST_MAIN), LintId::of(enum_variants::ENUM_VARIANT_NAMES), From e66ecf6f0e137196e67daf0ba0982766a2c91175 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 10 Jan 2022 11:05:05 -0600 Subject: [PATCH 39/72] Run dogfood on windows I believe this is possible as of rust-lang/rust-clippy#7631 --- tests/dogfood.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 6f7de9c67a9b7..17033f6e908f2 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -3,8 +3,6 @@ //! //! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context -// Dogfood cannot run on Windows -#![cfg(not(windows))] #![feature(once_cell)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] From 7cf4f44e561acd2c69b9a4fd1b5e97ff59a6bbbe Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 10 Jan 2022 14:30:45 -0600 Subject: [PATCH 40/72] Fix output capturing --- clippy_dev/src/fmt.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 9ceadee58ea59..625fef23afb84 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -141,8 +141,11 @@ fn exec( println!("{}", format_command(&program, &dir, args)); } - let child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?; - let output = child.wait_with_output()?; + let output = Command::new(&program) + .current_dir(&dir) + .args(args.iter()) + .output() + .unwrap(); let success = output.status.success(); if !context.check && !success { From 51dbbf3c4c67446e3ae81e2488f48bfce8dac59f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 10 Jan 2022 13:20:46 -0600 Subject: [PATCH 41/72] Refactor test utils --- tests/cargo/mod.rs | 4 ---- tests/compile-test.rs | 6 ++++-- tests/dogfood.rs | 27 ++++++++++----------------- tests/test_utils/mod.rs | 13 +++++++++++++ 4 files changed, 27 insertions(+), 23 deletions(-) delete mode 100644 tests/cargo/mod.rs create mode 100644 tests/test_utils/mod.rs diff --git a/tests/cargo/mod.rs b/tests/cargo/mod.rs deleted file mode 100644 index 4dbe71e4b6ad6..0000000000000 --- a/tests/cargo/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[must_use] -pub fn is_rustc_test_suite() -> bool { - option_env!("RUSTC_TEST_SUITE").is_some() -} diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 762b67fc5854d..531890c863f5e 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,4 +1,5 @@ #![feature(test)] // compiletest_rs requires this attribute +#![feature(once_cell)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] @@ -11,8 +12,9 @@ use std::ffi::{OsStr, OsString}; use std::fs; use std::io; use std::path::{Path, PathBuf}; +use test_utils::IS_RUSTC_TEST_SUITE; -mod cargo; +mod test_utils; // whether to run internal tests or not const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); @@ -304,7 +306,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(result) } - if cargo::is_rustc_test_suite() { + if IS_RUSTC_TEST_SUITE { return; } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 17033f6e908f2..42e1628f6422e 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -7,28 +7,21 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] -use std::lazy::SyncLazy; use std::path::PathBuf; use std::process::Command; +use test_utils::{CARGO_CLIPPY_PATH, IS_RUSTC_TEST_SUITE}; -mod cargo; - -static CLIPPY_PATH: SyncLazy = SyncLazy::new(|| { - let mut path = std::env::current_exe().unwrap(); - assert!(path.pop()); // deps - path.set_file_name("cargo-clippy"); - path -}); +mod test_utils; #[test] fn dogfood_clippy() { // run clippy on itself and fail the test if lint warnings are reported - if cargo::is_rustc_test_suite() { + if IS_RUSTC_TEST_SUITE { return; } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let mut command = Command::new(&*CLIPPY_PATH); + let mut command = Command::new(&*CARGO_CLIPPY_PATH); command .current_dir(root_dir) .env("CARGO_INCREMENTAL", "0") @@ -55,7 +48,7 @@ fn dogfood_clippy() { } fn test_no_deps_ignores_path_deps_in_workspaces() { - if cargo::is_rustc_test_suite() { + if IS_RUSTC_TEST_SUITE { return; } let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -74,7 +67,7 @@ fn test_no_deps_ignores_path_deps_in_workspaces() { // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. - let output = Command::new(&*CLIPPY_PATH) + let output = Command::new(&*CARGO_CLIPPY_PATH) .current_dir(&cwd) .env("CARGO_INCREMENTAL", "0") .arg("clippy") @@ -93,7 +86,7 @@ fn test_no_deps_ignores_path_deps_in_workspaces() { let lint_path_dep = || { // Test that without the `--no-deps` argument, `path_dep` is linted. - let output = Command::new(&*CLIPPY_PATH) + let output = Command::new(&*CARGO_CLIPPY_PATH) .current_dir(&cwd) .env("CARGO_INCREMENTAL", "0") .arg("clippy") @@ -119,7 +112,7 @@ fn test_no_deps_ignores_path_deps_in_workspaces() { lint_path_dep(); let successful_build = || { - let output = Command::new(&*CLIPPY_PATH) + let output = Command::new(&*CARGO_CLIPPY_PATH) .current_dir(&cwd) .env("CARGO_INCREMENTAL", "0") .arg("clippy") @@ -153,7 +146,7 @@ fn test_no_deps_ignores_path_deps_in_workspaces() { #[test] fn dogfood_subprojects() { // run clippy on remaining subprojects and fail the test if lint warnings are reported - if cargo::is_rustc_test_suite() { + if IS_RUSTC_TEST_SUITE { return; } @@ -218,7 +211,7 @@ fn run_metadata_collection_lint() { fn run_clippy_for_project(project: &str) { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let mut command = Command::new(&*CLIPPY_PATH); + let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); command .current_dir(root_dir.join(project)) diff --git a/tests/test_utils/mod.rs b/tests/test_utils/mod.rs new file mode 100644 index 0000000000000..8a4de3f6def90 --- /dev/null +++ b/tests/test_utils/mod.rs @@ -0,0 +1,13 @@ +#![allow(dead_code)] // see https://github.com/rust-lang/rust/issues/46379 + +use std::lazy::SyncLazy; +use std::path::PathBuf; + +pub static CARGO_CLIPPY_PATH: SyncLazy = SyncLazy::new(|| { + let mut path = std::env::current_exe().unwrap(); + assert!(path.pop()); // deps + path.set_file_name("cargo-clippy"); + path +}); + +pub const IS_RUSTC_TEST_SUITE: bool = option_env!("RUSTC_TEST_SUITE").is_some(); From d356fb9fa20198d770fc7e05bb71aea00875b3ea Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 10 Jan 2022 13:53:02 -0600 Subject: [PATCH 42/72] Use `rustup which rustfmt` --- clippy_dev/src/fmt.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 625fef23afb84..d513a229b7e38 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -3,7 +3,7 @@ use itertools::Itertools; use shell_escape::escape; use std::ffi::{OsStr, OsString}; use std::path::Path; -use std::process::{self, Command}; +use std::process::{self, Command, Stdio}; use std::{fs, io}; use walkdir::WalkDir; @@ -31,6 +31,7 @@ impl From for CliError { struct FmtContext { check: bool, verbose: bool, + rustfmt_path: String, } // the "main" function of cargo dev fmt @@ -102,7 +103,23 @@ Please revert the changes to Cargo.tomls first." } } - let context = FmtContext { check, verbose }; + let output = Command::new("rustup") + .args(["which", "rustfmt"]) + .stderr(Stdio::inherit()) + .output() + .expect("error running `rustup which rustfmt`"); + if !output.status.success() { + eprintln!("`rustup which rustfmt` did not execute successfully"); + process::exit(1); + } + let mut rustfmt_path = String::from_utf8(output.stdout).expect("invalid rustfmt path"); + rustfmt_path.truncate(rustfmt_path.trim_end().len()); + + let context = FmtContext { + check, + verbose, + rustfmt_path, + }; let result = try_run(&context); let code = match result { Ok(true) => 0, @@ -142,6 +159,7 @@ fn exec( } let output = Command::new(&program) + .env("RUSTFMT", &context.rustfmt_path) .current_dir(&dir) .args(args.iter()) .output() @@ -162,7 +180,6 @@ fn exec( fn cargo_fmt(context: &FmtContext, path: &Path) -> Result { let mut args = vec!["fmt", "--all"]; if context.check { - args.push("--"); args.push("--check"); } let success = exec(context, "cargo", path, &args)?; @@ -203,7 +220,7 @@ fn rustfmt(context: &FmtContext, paths: impl Iterator) -> Resul } args.extend(paths); - let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?; + let success = exec(context, &context.rustfmt_path, std::env::current_dir()?, &args)?; Ok(success) } From 920e9f0c4b1aa0921d4dd5e22ea727a31dd8e2cc Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 10 Jan 2022 15:27:00 -0600 Subject: [PATCH 43/72] Move workspace test --- .github/workflows/clippy.yml | 8 -- .github/workflows/clippy_bors.yml | 8 -- .gitignore | 1 - tests/dogfood.rs | 111 +----------------- tests/workspace.rs | 107 +++++++++++++++++ .../workspace_test}/Cargo.toml | 2 +- .../workspace_test}/build.rs | 0 .../workspace_test}/path_dep/Cargo.toml | 0 .../workspace_test}/path_dep/src/lib.rs | 0 .../workspace_test}/src/main.rs | 0 .../workspace_test}/subcrate/Cargo.toml | 0 .../workspace_test}/subcrate/src/lib.rs | 0 12 files changed, 109 insertions(+), 128 deletions(-) create mode 100644 tests/workspace.rs rename {clippy_workspace_tests => tests/workspace_test}/Cargo.toml (71%) rename {clippy_workspace_tests => tests/workspace_test}/build.rs (100%) rename {clippy_workspace_tests => tests/workspace_test}/path_dep/Cargo.toml (100%) rename {clippy_workspace_tests => tests/workspace_test}/path_dep/src/lib.rs (100%) rename {clippy_workspace_tests => tests/workspace_test}/src/main.rs (100%) rename {clippy_workspace_tests => tests/workspace_test}/subcrate/Cargo.toml (100%) rename {clippy_workspace_tests => tests/workspace_test}/subcrate/src/lib.rs (100%) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 8b5e814b1772e..116ae031bb719 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -70,14 +70,6 @@ jobs: run: cargo test --features deny-warnings working-directory: clippy_dev - - name: Test cargo-clippy - run: ../target/debug/cargo-clippy - working-directory: clippy_workspace_tests - - - name: Test cargo-clippy --fix - run: ../target/debug/cargo-clippy clippy --fix - working-directory: clippy_workspace_tests - - name: Test clippy-driver run: bash .github/driver.sh env: diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index eb045ec30dc31..fe02d9cfc8499 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -133,14 +133,6 @@ jobs: run: cargo test --features deny-warnings working-directory: clippy_dev - - name: Test cargo-clippy - run: ../target/debug/cargo-clippy - working-directory: clippy_workspace_tests - - - name: Test cargo-clippy --fix - run: ../target/debug/cargo-clippy clippy --fix - working-directory: clippy_workspace_tests - - name: Test clippy-driver run: bash .github/driver.sh env: diff --git a/.gitignore b/.gitignore index e82a0ec4765bb..3e50c45a9b63e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,6 @@ out /target /clippy_lints/target /clippy_utils/target -/clippy_workspace_tests/target /clippy_dev/target /lintcheck/target /rustc_tools_util/target diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 42e1628f6422e..6665bb21784b8 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -47,102 +47,6 @@ fn dogfood_clippy() { assert!(output.status.success()); } -fn test_no_deps_ignores_path_deps_in_workspaces() { - if IS_RUSTC_TEST_SUITE { - return; - } - let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let target_dir = root.join("target").join("dogfood"); - let cwd = root.join("clippy_workspace_tests"); - - // Make sure we start with a clean state - Command::new("cargo") - .current_dir(&cwd) - .env("CARGO_TARGET_DIR", &target_dir) - .arg("clean") - .args(&["-p", "subcrate"]) - .args(&["-p", "path_dep"]) - .output() - .unwrap(); - - // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. - // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. - let output = Command::new(&*CARGO_CLIPPY_PATH) - .current_dir(&cwd) - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .args(&["-p", "subcrate"]) - .arg("--no-deps") - .arg("--") - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .args(&["--cfg", r#"feature="primary_package_test""#]) - .output() - .unwrap(); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(output.status.success()); - - let lint_path_dep = || { - // Test that without the `--no-deps` argument, `path_dep` is linted. - let output = Command::new(&*CARGO_CLIPPY_PATH) - .current_dir(&cwd) - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .args(&["-p", "subcrate"]) - .arg("--") - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .args(&["--cfg", r#"feature="primary_package_test""#]) - .output() - .unwrap(); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(!output.status.success()); - assert!( - String::from_utf8(output.stderr) - .unwrap() - .contains("error: empty `loop {}` wastes CPU cycles") - ); - }; - - // Make sure Cargo is aware of the removal of `--no-deps`. - lint_path_dep(); - - let successful_build = || { - let output = Command::new(&*CARGO_CLIPPY_PATH) - .current_dir(&cwd) - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .args(&["-p", "subcrate"]) - .arg("--") - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap(); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(output.status.success()); - - output - }; - - // Trigger a sucessful build, so Cargo would like to cache the build result. - successful_build(); - - // Make sure there's no spurious rebuild when nothing changes. - let stderr = String::from_utf8(successful_build().stderr).unwrap(); - assert!(!stderr.contains("Compiling")); - assert!(!stderr.contains("Checking")); - assert!(stderr.contains("Finished")); - - // Make sure Cargo is aware of the new `--cfg` flag. - lint_path_dep(); -} - #[test] fn dogfood_subprojects() { // run clippy on remaining subprojects and fail the test if lint warnings are reported @@ -151,22 +55,9 @@ fn dogfood_subprojects() { } // NOTE: `path_dep` crate is omitted on purpose here - for project in &[ - "clippy_workspace_tests", - "clippy_workspace_tests/src", - "clippy_workspace_tests/subcrate", - "clippy_workspace_tests/subcrate/src", - "clippy_dev", - "clippy_lints", - "clippy_utils", - "rustc_tools_util", - ] { + for project in &["clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { run_clippy_for_project(project); } - - // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the - // same time, so we test this immediately after the dogfood for workspaces. - test_no_deps_ignores_path_deps_in_workspaces(); } #[test] diff --git a/tests/workspace.rs b/tests/workspace.rs new file mode 100644 index 0000000000000..677b4a4d56999 --- /dev/null +++ b/tests/workspace.rs @@ -0,0 +1,107 @@ +#![feature(once_cell)] + +use std::path::PathBuf; +use std::process::Command; +use test_utils::{CARGO_CLIPPY_PATH, IS_RUSTC_TEST_SUITE}; + +mod test_utils; + +#[test] +fn test_no_deps_ignores_path_deps_in_workspaces() { + if IS_RUSTC_TEST_SUITE { + return; + } + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let target_dir = root.join("target").join("workspace_test"); + let cwd = root.join("tests/workspace_test"); + + // Make sure we start with a clean state + Command::new("cargo") + .current_dir(&cwd) + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clean") + .args(&["-p", "subcrate"]) + .args(&["-p", "path_dep"]) + .output() + .unwrap(); + + // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. + // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. + let output = Command::new(&*CARGO_CLIPPY_PATH) + .current_dir(&cwd) + .env("CARGO_INCREMENTAL", "0") + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--no-deps") + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); + + let lint_path_dep = || { + // Test that without the `--no-deps` argument, `path_dep` is linted. + let output = Command::new(&*CARGO_CLIPPY_PATH) + .current_dir(&cwd) + .env("CARGO_INCREMENTAL", "0") + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(!output.status.success()); + assert!( + String::from_utf8(output.stderr) + .unwrap() + .contains("error: empty `loop {}` wastes CPU cycles") + ); + }; + + // Make sure Cargo is aware of the removal of `--no-deps`. + lint_path_dep(); + + let successful_build = || { + let output = Command::new(&*CARGO_CLIPPY_PATH) + .current_dir(&cwd) + .env("CARGO_INCREMENTAL", "0") + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); + + output + }; + + // Trigger a sucessful build, so Cargo would like to cache the build result. + successful_build(); + + // Make sure there's no spurious rebuild when nothing changes. + let stderr = String::from_utf8(successful_build().stderr).unwrap(); + assert!(!stderr.contains("Compiling")); + assert!(!stderr.contains("Checking")); + assert!(stderr.contains("Finished")); + + // Make sure Cargo is aware of the new `--cfg` flag. + lint_path_dep(); +} diff --git a/clippy_workspace_tests/Cargo.toml b/tests/workspace_test/Cargo.toml similarity index 71% rename from clippy_workspace_tests/Cargo.toml rename to tests/workspace_test/Cargo.toml index 7a235b215d38d..bf5b4ca5288a4 100644 --- a/clippy_workspace_tests/Cargo.toml +++ b/tests/workspace_test/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "clippy_workspace_tests" +name = "workspace_test" version = "0.1.0" edition = "2018" diff --git a/clippy_workspace_tests/build.rs b/tests/workspace_test/build.rs similarity index 100% rename from clippy_workspace_tests/build.rs rename to tests/workspace_test/build.rs diff --git a/clippy_workspace_tests/path_dep/Cargo.toml b/tests/workspace_test/path_dep/Cargo.toml similarity index 100% rename from clippy_workspace_tests/path_dep/Cargo.toml rename to tests/workspace_test/path_dep/Cargo.toml diff --git a/clippy_workspace_tests/path_dep/src/lib.rs b/tests/workspace_test/path_dep/src/lib.rs similarity index 100% rename from clippy_workspace_tests/path_dep/src/lib.rs rename to tests/workspace_test/path_dep/src/lib.rs diff --git a/clippy_workspace_tests/src/main.rs b/tests/workspace_test/src/main.rs similarity index 100% rename from clippy_workspace_tests/src/main.rs rename to tests/workspace_test/src/main.rs diff --git a/clippy_workspace_tests/subcrate/Cargo.toml b/tests/workspace_test/subcrate/Cargo.toml similarity index 100% rename from clippy_workspace_tests/subcrate/Cargo.toml rename to tests/workspace_test/subcrate/Cargo.toml diff --git a/clippy_workspace_tests/subcrate/src/lib.rs b/tests/workspace_test/subcrate/src/lib.rs similarity index 100% rename from clippy_workspace_tests/subcrate/src/lib.rs rename to tests/workspace_test/subcrate/src/lib.rs From 4a549338451cae037fcbb4c4a6a608132e61a305 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 10 Jan 2022 14:03:55 -0600 Subject: [PATCH 44/72] Merge dogfood tests The two dogfood tests cannot be run concurrently since they use the same target directory. --- tests/dogfood.rs | 49 +++++++----------------------------------------- 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 6665bb21784b8..67af9d05bf402 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -9,54 +9,19 @@ use std::path::PathBuf; use std::process::Command; -use test_utils::{CARGO_CLIPPY_PATH, IS_RUSTC_TEST_SUITE}; +use test_utils::IS_RUSTC_TEST_SUITE; mod test_utils; #[test] fn dogfood_clippy() { - // run clippy on itself and fail the test if lint warnings are reported - if IS_RUSTC_TEST_SUITE { - return; - } - let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - - let mut command = Command::new(&*CARGO_CLIPPY_PATH); - command - .current_dir(root_dir) - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .arg("--all-targets") - .arg("--all-features") - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) - .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - - // internal lints only exist if we build with the internal feature - if cfg!(feature = "internal") { - command.args(&["-D", "clippy::internal"]); - } - - let output = command.output().unwrap(); - - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(output.status.success()); -} - -#[test] -fn dogfood_subprojects() { - // run clippy on remaining subprojects and fail the test if lint warnings are reported if IS_RUSTC_TEST_SUITE { return; } - // NOTE: `path_dep` crate is omitted on purpose here - for project in &["clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { - run_clippy_for_project(project); + // "" is the root package + for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { + run_clippy_for_package(package); } } @@ -73,7 +38,7 @@ fn run_metadata_collection_lint() { // Run collection as is std::env::set_var("ENABLE_METADATA_COLLECTION", "1"); - run_clippy_for_project("clippy_lints"); + run_clippy_for_package("clippy_lints"); // Check if cargo caching got in the way if let Ok(file) = File::open(metadata_output_path) { @@ -96,10 +61,10 @@ fn run_metadata_collection_lint() { .unwrap(); // Running the collection again - run_clippy_for_project("clippy_lints"); + run_clippy_for_package("clippy_lints"); } -fn run_clippy_for_project(project: &str) { +fn run_clippy_for_package(project: &str) { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); From 21343ab2a5c89dd881cd34986eb85487513f47d1 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 10 Jan 2022 14:05:38 -0600 Subject: [PATCH 45/72] Remove rustfmt component check This was more valuable when we used the latest nightly without specifying the toolchain version. --- tests/fmt.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/fmt.rs b/tests/fmt.rs index 383702dd439cf..0defd45b68b06 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -10,14 +10,6 @@ fn fmt() { return; } - // Skip this test if nightly rustfmt is unavailable - let rustup_output = Command::new("rustup").args(&["component", "list"]).output().unwrap(); - assert!(rustup_output.status.success()); - let component_output = String::from_utf8_lossy(&rustup_output.stdout); - if !component_output.contains("rustfmt") { - return; - } - let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let output = Command::new("cargo") .current_dir(root_dir) From 01ef7c79322ff86a6e585d379cd0b48108e4a2a4 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 10 Jan 2022 17:47:56 -0600 Subject: [PATCH 46/72] Fix dogfood --- clippy_utils/src/paths.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index c06a1f826fa2a..288c56e9fd737 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -117,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"]; From 3298de7f661b1f91dec5faef8eb6d36a6b30ffa0 Mon Sep 17 00:00:00 2001 From: Federico Guerinoni Date: Sun, 2 Jan 2022 14:26:44 +0100 Subject: [PATCH 47/72] Add borrow_as_ptr lint Closes: #6995 Signed-off-by: Federico Guerinoni Co-authored-by: Paolo Barbolini --- CHANGELOG.md | 1 + README.md | 2 +- clippy_lints/src/borrow_as_ptr.rs | 97 +++++++++++++++++++ clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_utils/src/msrvs.rs | 1 + tests/ui/as_conversions.rs | 1 + tests/ui/as_conversions.stderr | 6 +- tests/ui/borrow_as_ptr.fixed | 10 ++ tests/ui/borrow_as_ptr.rs | 10 ++ tests/ui/borrow_as_ptr.stderr | 16 +++ tests/ui/borrow_as_ptr_no_std.fixed | 22 +++++ tests/ui/borrow_as_ptr_no_std.rs | 22 +++++ tests/ui/borrow_as_ptr_no_std.stderr | 16 +++ tests/ui/cast_alignment.rs | 8 +- tests/ui/cast_alignment.stderr | 8 +- tests/ui/cast_ref_to_mut.rs | 2 +- tests/ui/mutex_atomic.rs | 1 + tests/ui/mutex_atomic.stderr | 14 +-- tests/ui/or_fun_call.fixed | 2 +- tests/ui/or_fun_call.rs | 2 +- tests/ui/transmute.rs | 2 +- tests/ui/transmute_ptr_to_ptr.rs | 1 + tests/ui/transmute_ptr_to_ptr.stderr | 12 +-- .../transmutes_expressible_as_ptr_casts.fixed | 3 +- .../ui/transmutes_expressible_as_ptr_casts.rs | 3 +- ...transmutes_expressible_as_ptr_casts.stderr | 16 +-- tests/ui/unnecessary_cast_fixable.fixed | 7 +- tests/ui/unnecessary_cast_fixable.rs | 7 +- tests/ui/unnecessary_cast_fixable.stderr | 34 +++---- tests/ui/vtable_address_comparisons.rs | 2 + tests/ui/vtable_address_comparisons.stderr | 20 ++-- tests/ui/zero_offset.rs | 1 + tests/ui/zero_offset.stderr | 16 +-- 35 files changed, 294 insertions(+), 75 deletions(-) create mode 100644 clippy_lints/src/borrow_as_ptr.rs create mode 100644 tests/ui/borrow_as_ptr.fixed create mode 100644 tests/ui/borrow_as_ptr.rs create mode 100644 tests/ui/borrow_as_ptr.stderr create mode 100644 tests/ui/borrow_as_ptr_no_std.fixed create mode 100644 tests/ui/borrow_as_ptr_no_std.rs create mode 100644 tests/ui/borrow_as_ptr_no_std.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 83fd8396bc72f..e521995a18864 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2887,6 +2887,7 @@ Released 2018-09-13 [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison +[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection diff --git a/README.md b/README.md index 1bbd89e7822e8..de72e8d124d45 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 500 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/clippy_lints/src/borrow_as_ptr.rs b/clippy_lints/src/borrow_as_ptr.rs new file mode 100644 index 0000000000000..b8f5217af2b7d --- /dev/null +++ b/clippy_lints/src/borrow_as_ptr.rs @@ -0,0 +1,97 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_no_std_crate; +use clippy_utils::source::snippet_opt; +use clippy_utils::{meets_msrv, msrvs}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the usage of `&expr as *const T` or + /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or + /// `ptr::addr_of_mut` instead. + /// + /// ### Why is this bad? + /// This would improve readability and avoid creating a reference + /// that points to an uninitialized value or unaligned place. + /// Read the `ptr::addr_of` docs for more information. + /// + /// ### Example + /// ```rust + /// let val = 1; + /// let p = &val as *const i32; + /// + /// let mut val_mut = 1; + /// let p_mut = &mut val_mut as *mut i32; + /// ``` + /// Use instead: + /// ```rust + /// let val = 1; + /// let p = std::ptr::addr_of!(val); + /// + /// let mut val_mut = 1; + /// let p_mut = std::ptr::addr_of_mut!(val_mut); + /// ``` + #[clippy::version = "1.60.0"] + pub BORROW_AS_PTR, + pedantic, + "borrowing just to cast to a raw pointer" +} + +impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]); + +pub struct BorrowAsPtr { + msrv: Option, +} + +impl BorrowAsPtr { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::BORROW_AS_PTR) { + return; + } + + if expr.span.from_expansion() { + return; + } + + if_chain! { + if let ExprKind::Cast(left_expr, ty) = &expr.kind; + if let TyKind::Ptr(_) = ty.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind; + + then { + let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; + let macro_name = match mutability { + Mutability::Not => "addr_of", + Mutability::Mut => "addr_of_mut", + }; + + span_lint_and_sugg( + cx, + BORROW_AS_PTR, + expr.span, + "borrow as raw pointer", + "try", + format!( + "{}::ptr::{}!({})", + core_or_std, + macro_name, + snippet_opt(cx, e.span).unwrap() + ), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 832178c2db5e5..09e4f6ae27504 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -59,6 +59,7 @@ store.register_lints(&[ bool_assert_comparison::BOOL_ASSERT_COMPARISON, booleans::LOGIC_BUG, booleans::NONMINIMAL_BOOL, + borrow_as_ptr::BORROW_AS_PTR, bytecount::NAIVE_BYTECOUNT, cargo_common_metadata::CARGO_COMMON_METADATA, case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 70a4a62437890..1744b7c825078 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -7,6 +7,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(bit_mask::VERBOSE_BIT_MASK), + LintId::of(borrow_as_ptr::BORROW_AS_PTR), LintId::of(bytecount::NAIVE_BYTECOUNT), LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), LintId::of(casts::CAST_LOSSLESS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0d765c2bcde1b..732df4d2b8be0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -174,6 +174,7 @@ mod blacklisted_name; mod blocks_in_if_conditions; mod bool_assert_comparison; mod booleans; +mod borrow_as_ptr; mod bytecount; mod cargo_common_metadata; mod case_sensitive_file_extension_comparisons; @@ -857,6 +858,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields)); store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); + store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 0cec7d6a5e402..af677d8b00f7b 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -14,6 +14,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,53,0 { OR_PATTERNS } 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/tests/ui/as_conversions.rs b/tests/ui/as_conversions.rs index cd745feec6d8a..ba4394defbf20 100644 --- a/tests/ui/as_conversions.rs +++ b/tests/ui/as_conversions.rs @@ -1,6 +1,7 @@ // aux-build:macro_rules.rs #![warn(clippy::as_conversions)] +#![allow(clippy::borrow_as_ptr)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/as_conversions.stderr b/tests/ui/as_conversions.stderr index f5f75d3aee04a..d11b56171b072 100644 --- a/tests/ui/as_conversions.stderr +++ b/tests/ui/as_conversions.stderr @@ -1,5 +1,5 @@ error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:14:13 + --> $DIR/as_conversions.rs:15:13 | LL | let i = 0u32 as u64; | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let i = 0u32 as u64; = help: consider using a safe wrapper for this conversion error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:16:13 + --> $DIR/as_conversions.rs:17:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let j = &i as *const u64 as *mut u64; = help: consider using a safe wrapper for this conversion error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:16:13 + --> $DIR/as_conversions.rs:17:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/borrow_as_ptr.fixed b/tests/ui/borrow_as_ptr.fixed new file mode 100644 index 0000000000000..ff5c6a8c3774b --- /dev/null +++ b/tests/ui/borrow_as_ptr.fixed @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::borrow_as_ptr)] + +fn main() { + let val = 1; + let _p = std::ptr::addr_of!(val); + + let mut val_mut = 1; + let _p_mut = std::ptr::addr_of_mut!(val_mut); +} diff --git a/tests/ui/borrow_as_ptr.rs b/tests/ui/borrow_as_ptr.rs new file mode 100644 index 0000000000000..0f62ec6ee58b4 --- /dev/null +++ b/tests/ui/borrow_as_ptr.rs @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::borrow_as_ptr)] + +fn main() { + let val = 1; + let _p = &val as *const i32; + + let mut val_mut = 1; + let _p_mut = &mut val_mut as *mut i32; +} diff --git a/tests/ui/borrow_as_ptr.stderr b/tests/ui/borrow_as_ptr.stderr new file mode 100644 index 0000000000000..be1ed73305674 --- /dev/null +++ b/tests/ui/borrow_as_ptr.stderr @@ -0,0 +1,16 @@ +error: borrow as raw pointer + --> $DIR/borrow_as_ptr.rs:6:14 + | +LL | let _p = &val as *const i32; + | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of!(val)` + | + = note: `-D clippy::borrow-as-ptr` implied by `-D warnings` + +error: borrow as raw pointer + --> $DIR/borrow_as_ptr.rs:9:18 + | +LL | let _p_mut = &mut val_mut as *mut i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/borrow_as_ptr_no_std.fixed b/tests/ui/borrow_as_ptr_no_std.fixed new file mode 100644 index 0000000000000..eaba3b1c20c8d --- /dev/null +++ b/tests/ui/borrow_as_ptr_no_std.fixed @@ -0,0 +1,22 @@ +// run-rustfix +#![warn(clippy::borrow_as_ptr)] +#![feature(lang_items, start, libc)] +#![no_std] + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let val = 1; + let _p = core::ptr::addr_of!(val); + + let mut val_mut = 1; + let _p_mut = core::ptr::addr_of_mut!(val_mut); + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/borrow_as_ptr_no_std.rs b/tests/ui/borrow_as_ptr_no_std.rs new file mode 100644 index 0000000000000..d83f9d1f875ba --- /dev/null +++ b/tests/ui/borrow_as_ptr_no_std.rs @@ -0,0 +1,22 @@ +// run-rustfix +#![warn(clippy::borrow_as_ptr)] +#![feature(lang_items, start, libc)] +#![no_std] + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let val = 1; + let _p = &val as *const i32; + + let mut val_mut = 1; + let _p_mut = &mut val_mut as *mut i32; + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/borrow_as_ptr_no_std.stderr b/tests/ui/borrow_as_ptr_no_std.stderr new file mode 100644 index 0000000000000..84c8ba7d07f1e --- /dev/null +++ b/tests/ui/borrow_as_ptr_no_std.stderr @@ -0,0 +1,16 @@ +error: borrow as raw pointer + --> $DIR/borrow_as_ptr_no_std.rs:9:14 + | +LL | let _p = &val as *const i32; + | ^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of!(val)` + | + = note: `-D clippy::borrow-as-ptr` implied by `-D warnings` + +error: borrow as raw pointer + --> $DIR/borrow_as_ptr_no_std.rs:12:18 + | +LL | let _p_mut = &mut val_mut as *mut i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of_mut!(val_mut)` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_alignment.rs index d011e84b115a7..659591fffbecd 100644 --- a/tests/ui/cast_alignment.rs +++ b/tests/ui/cast_alignment.rs @@ -4,7 +4,13 @@ extern crate libc; #[warn(clippy::cast_ptr_alignment)] -#[allow(clippy::no_effect, clippy::unnecessary_operation, clippy::cast_lossless)] +#[allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::cast_lossless, + clippy::borrow_as_ptr +)] + fn main() { /* These should be warned against */ diff --git a/tests/ui/cast_alignment.stderr b/tests/ui/cast_alignment.stderr index 7998b787b91fb..aedd368445554 100644 --- a/tests/ui/cast_alignment.stderr +++ b/tests/ui/cast_alignment.stderr @@ -1,5 +1,5 @@ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:12:5 + --> $DIR/cast_alignment.rs:18:5 | LL | (&1u8 as *const u8) as *const u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,19 +7,19 @@ LL | (&1u8 as *const u8) as *const u16; = note: `-D clippy::cast-ptr-alignment` implied by `-D warnings` error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:13:5 + --> $DIR/cast_alignment.rs:19:5 | LL | (&mut 1u8 as *mut u8) as *mut u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:16:5 + --> $DIR/cast_alignment.rs:22:5 | LL | (&1u8 as *const u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:17:5 + --> $DIR/cast_alignment.rs:23:5 | LL | (&mut 1u8 as *mut u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/cast_ref_to_mut.rs b/tests/ui/cast_ref_to_mut.rs index 089e5cfabe4b9..c48a734ba32c2 100644 --- a/tests/ui/cast_ref_to_mut.rs +++ b/tests/ui/cast_ref_to_mut.rs @@ -1,5 +1,5 @@ #![warn(clippy::cast_ref_to_mut)] -#![allow(clippy::no_effect)] +#![allow(clippy::no_effect, clippy::borrow_as_ptr)] extern "C" { // N.B., mutability can be easily incorrect in FFI calls -- as diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs index 71063d9ce4634..47b3dad398977 100644 --- a/tests/ui/mutex_atomic.rs +++ b/tests/ui/mutex_atomic.rs @@ -1,6 +1,7 @@ #![warn(clippy::all)] #![warn(clippy::mutex_integer)] #![warn(clippy::mutex_atomic)] +#![allow(clippy::borrow_as_ptr)] fn main() { use std::sync::Mutex; diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index ef7ad9b757614..262028a8723a3 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,5 +1,5 @@ error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:7:5 + --> $DIR/mutex_atomic.rs:8:5 | LL | Mutex::new(true); | ^^^^^^^^^^^^^^^^ @@ -7,31 +7,31 @@ LL | Mutex::new(true); = note: `-D clippy::mutex-atomic` implied by `-D warnings` error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:8:5 + --> $DIR/mutex_atomic.rs:9:5 | LL | Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:9:5 + --> $DIR/mutex_atomic.rs:10:5 | LL | Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:11:5 + --> $DIR/mutex_atomic.rs:12:5 | LL | Mutex::new(&x as *const u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:12:5 + --> $DIR/mutex_atomic.rs:13:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:13:5 + --> $DIR/mutex_atomic.rs:14:5 | LL | Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | Mutex::new(0u32); = note: `-D clippy::mutex-integer` implied by `-D warnings` error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - --> $DIR/mutex_atomic.rs:14:5 + --> $DIR/mutex_atomic.rs:15:5 | LL | Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index d6d6ab49734e7..87cdb3ace47cb 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -2,7 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 8eadc6ce3b47a..3f69cef301c8b 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -2,7 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 6a7037d8f3826..9b681a79aae7a 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] +#![allow(dead_code, clippy::borrow_as_ptr)] extern crate core; diff --git a/tests/ui/transmute_ptr_to_ptr.rs b/tests/ui/transmute_ptr_to_ptr.rs index 9e213aab68c57..f06ffab5d9be9 100644 --- a/tests/ui/transmute_ptr_to_ptr.rs +++ b/tests/ui/transmute_ptr_to_ptr.rs @@ -1,4 +1,5 @@ #![warn(clippy::transmute_ptr_to_ptr)] +#![allow(clippy::borrow_as_ptr)] // Make sure we can modify lifetimes, which is one of the recommended uses // of transmute diff --git a/tests/ui/transmute_ptr_to_ptr.stderr b/tests/ui/transmute_ptr_to_ptr.stderr index 4d1b8fcc199e8..49a8a3347e404 100644 --- a/tests/ui/transmute_ptr_to_ptr.stderr +++ b/tests/ui/transmute_ptr_to_ptr.stderr @@ -1,5 +1,5 @@ error: transmute from a pointer to a pointer - --> $DIR/transmute_ptr_to_ptr.rs:29:29 + --> $DIR/transmute_ptr_to_ptr.rs:30:29 | LL | let _: *const f32 = std::mem::transmute(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32` @@ -7,31 +7,31 @@ LL | let _: *const f32 = std::mem::transmute(ptr); = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmute_ptr_to_ptr.rs:30:27 + --> $DIR/transmute_ptr_to_ptr.rs:31:27 | LL | let _: *mut f32 = std::mem::transmute(mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:32:23 + --> $DIR/transmute_ptr_to_ptr.rs:33:23 | LL | let _: &f32 = std::mem::transmute(&1u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:33:23 + --> $DIR/transmute_ptr_to_ptr.rs:34:23 | LL | let _: &f64 = std::mem::transmute(&1f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:36:27 + --> $DIR/transmute_ptr_to_ptr.rs:37:27 | LL | let _: &mut f32 = std::mem::transmute(&mut 1u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:37:37 + --> $DIR/transmute_ptr_to_ptr.rs:38:37 | LL | let _: &GenericParam = std::mem::transmute(&GenericParam { t: 1u32 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam as *const GenericParam)` diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/tests/ui/transmutes_expressible_as_ptr_casts.fixed index b6f1e83181ccb..9ae0ed0b13f1e 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -4,8 +4,7 @@ // would otherwise be responsible for #![warn(clippy::useless_transmute)] #![warn(clippy::transmute_ptr_to_ptr)] -#![allow(unused_unsafe)] -#![allow(dead_code)] +#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)] use std::mem::{size_of, transmute}; diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.rs b/tests/ui/transmutes_expressible_as_ptr_casts.rs index 0205d1ece60d5..985cf9a075d31 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -4,8 +4,7 @@ // would otherwise be responsible for #![warn(clippy::useless_transmute)] #![warn(clippy::transmute_ptr_to_ptr)] -#![allow(unused_unsafe)] -#![allow(dead_code)] +#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)] use std::mem::{size_of, transmute}; diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/tests/ui/transmutes_expressible_as_ptr_casts.stderr index 1157b179317e2..e8496a325d6dc 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -1,5 +1,5 @@ error: transmute from an integer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:19:39 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:18:39 | LL | let _ptr_i32_transmute = unsafe { transmute::(usize::MAX) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32` @@ -7,7 +7,7 @@ LL | let _ptr_i32_transmute = unsafe { transmute::(usize: = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:23:38 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:22:38 | LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8` @@ -15,13 +15,13 @@ LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:29:46 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:28:46 | LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]` error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:35:50 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:34:50 | LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize` @@ -29,25 +29,25 @@ LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, us = note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:41:41 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:40:41 | LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:49:41 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:48:41 | LL | let _usize_ptr_transmute = unsafe { transmute:: u8, *const usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:53:49 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:52:49 | LL | let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:65:14 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:14 | LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8` diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index bda0f2c47cdd5..3332f49c80c97 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -1,7 +1,12 @@ // run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::nonstandard_macro_braces, + clippy::borrow_as_ptr +)] fn main() { // casting integer literal to float is unnecessary diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index f7a4f2a5988fd..ec01e93877927 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -1,7 +1,12 @@ // run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::nonstandard_macro_braces, + clippy::borrow_as_ptr +)] fn main() { // casting integer literal to float is unnecessary diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 3695a8f819c4a..a281143281b54 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -1,5 +1,5 @@ error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:8:5 + --> $DIR/unnecessary_cast_fixable.rs:13:5 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` @@ -7,97 +7,97 @@ LL | 100 as f32; = note: `-D clippy::unnecessary-cast` implied by `-D warnings` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:9:5 + --> $DIR/unnecessary_cast_fixable.rs:14:5 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:10:5 + --> $DIR/unnecessary_cast_fixable.rs:15:5 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:11:13 + --> $DIR/unnecessary_cast_fixable.rs:16:13 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:12:13 + --> $DIR/unnecessary_cast_fixable.rs:17:13 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:13:13 + --> $DIR/unnecessary_cast_fixable.rs:18:13 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:14:5 + --> $DIR/unnecessary_cast_fixable.rs:19:5 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:15:5 + --> $DIR/unnecessary_cast_fixable.rs:20:5 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:27:5 + --> $DIR/unnecessary_cast_fixable.rs:32:5 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:28:5 + --> $DIR/unnecessary_cast_fixable.rs:33:5 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:29:5 + --> $DIR/unnecessary_cast_fixable.rs:34:5 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:30:5 + --> $DIR/unnecessary_cast_fixable.rs:35:5 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:31:5 + --> $DIR/unnecessary_cast_fixable.rs:36:5 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:33:5 + --> $DIR/unnecessary_cast_fixable.rs:38:5 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:34:5 + --> $DIR/unnecessary_cast_fixable.rs:39:5 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:38:13 + --> $DIR/unnecessary_cast_fixable.rs:43:13 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:39:13 + --> $DIR/unnecessary_cast_fixable.rs:44:13 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` diff --git a/tests/ui/vtable_address_comparisons.rs b/tests/ui/vtable_address_comparisons.rs index c91d96ee18a31..a9a4a0f5a6b5c 100644 --- a/tests/ui/vtable_address_comparisons.rs +++ b/tests/ui/vtable_address_comparisons.rs @@ -4,6 +4,8 @@ use std::rc::Rc; use std::sync::Arc; #[warn(clippy::vtable_address_comparisons)] +#[allow(clippy::borrow_as_ptr)] + fn main() { let a: *const dyn Debug = &1 as &dyn Debug; let b: *const dyn Debug = &1 as &dyn Debug; diff --git a/tests/ui/vtable_address_comparisons.stderr b/tests/ui/vtable_address_comparisons.stderr index 76bd57217d784..2f1be61e5df72 100644 --- a/tests/ui/vtable_address_comparisons.stderr +++ b/tests/ui/vtable_address_comparisons.stderr @@ -1,5 +1,5 @@ error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:12:13 + --> $DIR/vtable_address_comparisons.rs:14:13 | LL | let _ = a == b; | ^^^^^^ @@ -8,7 +8,7 @@ LL | let _ = a == b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:13:13 + --> $DIR/vtable_address_comparisons.rs:15:13 | LL | let _ = a != b; | ^^^^^^ @@ -16,7 +16,7 @@ LL | let _ = a != b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:14:13 + --> $DIR/vtable_address_comparisons.rs:16:13 | LL | let _ = a < b; | ^^^^^ @@ -24,7 +24,7 @@ LL | let _ = a < b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:15:13 + --> $DIR/vtable_address_comparisons.rs:17:13 | LL | let _ = a <= b; | ^^^^^^ @@ -32,7 +32,7 @@ LL | let _ = a <= b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:16:13 + --> $DIR/vtable_address_comparisons.rs:18:13 | LL | let _ = a > b; | ^^^^^ @@ -40,7 +40,7 @@ LL | let _ = a > b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:17:13 + --> $DIR/vtable_address_comparisons.rs:19:13 | LL | let _ = a >= b; | ^^^^^^ @@ -48,7 +48,7 @@ LL | let _ = a >= b; = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:18:5 + --> $DIR/vtable_address_comparisons.rs:20:5 | LL | ptr::eq(a, b); | ^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | ptr::eq(a, b); = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:22:5 + --> $DIR/vtable_address_comparisons.rs:24:5 | LL | ptr::eq(a, b); | ^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | ptr::eq(a, b); = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:25:5 + --> $DIR/vtable_address_comparisons.rs:27:5 | LL | Rc::ptr_eq(&a, &a); | ^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | Rc::ptr_eq(&a, &a); = help: consider extracting and comparing data pointers only error: comparing trait object pointers compares a non-unique vtable address - --> $DIR/vtable_address_comparisons.rs:28:5 + --> $DIR/vtable_address_comparisons.rs:30:5 | LL | Arc::ptr_eq(&a, &a); | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/zero_offset.rs b/tests/ui/zero_offset.rs index 6c190a4c86c48..fd9ac1fa7663b 100644 --- a/tests/ui/zero_offset.rs +++ b/tests/ui/zero_offset.rs @@ -1,3 +1,4 @@ +#[allow(clippy::borrow_as_ptr)] fn main() { unsafe { let m = &mut () as *mut (); diff --git a/tests/ui/zero_offset.stderr b/tests/ui/zero_offset.stderr index b12c8e9a73c6d..481a446571ab0 100644 --- a/tests/ui/zero_offset.stderr +++ b/tests/ui/zero_offset.stderr @@ -1,5 +1,5 @@ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:4:9 + --> $DIR/zero_offset.rs:5:9 | LL | m.offset(0); | ^^^^^^^^^^^ @@ -7,43 +7,43 @@ LL | m.offset(0); = note: `#[deny(clippy::zst_offset)]` on by default error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:5:9 + --> $DIR/zero_offset.rs:6:9 | LL | m.wrapping_add(0); | ^^^^^^^^^^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:6:9 + --> $DIR/zero_offset.rs:7:9 | LL | m.sub(0); | ^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:7:9 + --> $DIR/zero_offset.rs:8:9 | LL | m.wrapping_sub(0); | ^^^^^^^^^^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:10:9 + --> $DIR/zero_offset.rs:11:9 | LL | c.offset(0); | ^^^^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:11:9 + --> $DIR/zero_offset.rs:12:9 | LL | c.wrapping_add(0); | ^^^^^^^^^^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:12:9 + --> $DIR/zero_offset.rs:13:9 | LL | c.sub(0); | ^^^^^^^^ error: offset calculation on zero-sized value - --> $DIR/zero_offset.rs:13:9 + --> $DIR/zero_offset.rs:14:9 | LL | c.wrapping_sub(0); | ^^^^^^^^^^^^^^^^^ From fd97d792f952618ee5f64e6e25cb8841742a9f4f Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sat, 27 Nov 2021 17:10:19 +0100 Subject: [PATCH 48/72] `README`: `clippy-driver` is not a replacement for `rustc` Currently, `clippy-driver` may run codegen, but this is an implementation detail. See https://github.com/rust-lang/rust-clippy/issues/8035. Signed-off-by: Miguel Ojeda --- README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 1bbd89e7822e8..f603b16d1a92c 100644 --- a/README.md +++ b/README.md @@ -98,22 +98,18 @@ If you want to run Clippy **only** on the given crate, use the `--no-deps` optio cargo clippy -p example -- --no-deps ``` -### As a rustc replacement (`clippy-driver`) +### Using `clippy-driver` -Clippy can also be used in projects that do not use cargo. To do so, you will need to replace -your `rustc` compilation commands with `clippy-driver`. For example, if your project runs: - -```terminal -rustc --edition 2018 -Cpanic=abort foo.rs -``` - -Then, to enable Clippy, you will need to call: +Clippy can also be used in projects that do not use cargo. To do so, run `clippy-driver` +with the same arguments you use for `rustc`. For example: ```terminal clippy-driver --edition 2018 -Cpanic=abort foo.rs ``` -Note that `rustc` will still run, i.e. it will still emit the output files it normally does. +Note that `clippy-driver` is designed for running Clippy only and should not be used as a general +replacement for `rustc`. `clippy-driver` may produce artifacts that are not optimized as expected, +for example. ### Travis CI From 3d4fdedd7ee52ad660ccc0f9f713aeac9b21b8d0 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 14 Dec 2021 18:13:14 +0100 Subject: [PATCH 49/72] `README`: mention `clippy-driver` on usage list Removes the "compiled from source" bit, which is confusing. Signed-off-by: Miguel Ojeda --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f603b16d1a92c..ffdc636cadf22 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ Table of contents: ## Usage -Below are instructions on how to use Clippy as a subcommand, compiled from source -or in Travis CI. +Below are instructions on how to use Clippy as a cargo subcommand, +in projects that do not use cargo, or in Travis CI. ### As a cargo subcommand (`cargo clippy`) From dda2aef64fb5b4903a28e5d4fb8d63483642cc6f Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 2 Jan 2022 22:37:05 -0500 Subject: [PATCH 50/72] Store a `Symbol` instead of an `Ident` in `VariantDef`/`FieldDef` The field is also renamed from `ident` to `name. In most cases, we don't actually need the `Span`. A new `ident` method is added to `VariantDef` and `FieldDef`, which constructs the full `Ident` using `tcx.def_ident_span()`. This method is used in the cases where we actually need an `Ident`. This makes incremental compilation properly track changes to the `Span`, without all of the invalidations caused by storing a `Span` directly via an `Ident`. --- clippy_lints/src/default.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 2 +- clippy_lints/src/inconsistent_struct_constructor.rs | 2 +- clippy_lints/src/matches.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index a0b137efe221a..6422f5aabe5e2 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -198,7 +198,7 @@ impl LateLintPass<'_> for Default { let ext_with_default = !variant .fields .iter() - .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name)); + .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name)); let field_list = assigned_fields .into_iter() diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 3573ea5f02671..15215ac15cdb9 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -161,7 +161,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { fields_def .iter() .find_map(|f_def| { - if f_def.ident == field.ident + if f_def.ident(self.cx.tcx) == field.ident { Some(self.cx.tcx.type_of(f_def.did)) } else { None } }); diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 1debdef9d86c7..388bb3727f96c 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -76,7 +76,7 @@ impl LateLintPass<'_> for InconsistentStructConstructor { then { let mut def_order_map = FxHashMap::default(); for (idx, field) in variant.fields.iter().enumerate() { - def_order_map.insert(field.ident.name, idx); + def_order_map.insert(field.name, idx); } if is_consistent_order(fields, &def_order_map) { diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 22970507f964c..5fa8f249e701e 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1136,7 +1136,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) s.push_str("::"); s }, - variant.ident.name, + variant.name, match variant.ctor_kind { CtorKind::Fn if variant.fields.len() == 1 => "(_)", CtorKind::Fn => "(..)", From 02ec39b2ff69ce3275724e234ebcb72d310766ca Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 11 Jan 2022 09:52:23 -0600 Subject: [PATCH 51/72] Stop using in_band_lifetimes --- clippy_lints/src/bool_assert_comparison.rs | 2 +- ...se_sensitive_file_extension_comparisons.rs | 2 +- clippy_lints/src/casts/cast_ptr_alignment.rs | 2 +- clippy_lints/src/casts/cast_ref_to_mut.rs | 2 +- clippy_lints/src/casts/char_lit_as_u8.rs | 2 +- clippy_lints/src/casts/ptr_as_ptr.rs | 2 +- clippy_lints/src/copies.rs | 14 +++---- clippy_lints/src/default.rs | 4 +- clippy_lints/src/default_numeric_fallback.rs | 2 +- clippy_lints/src/dereference.rs | 10 ++--- clippy_lints/src/entry.rs | 10 ++--- clippy_lints/src/equatable_if_let.rs | 2 +- clippy_lints/src/erasing_op.rs | 10 ++++- clippy_lints/src/from_over_into.rs | 2 +- clippy_lints/src/from_str_radix_10.rs | 2 +- clippy_lints/src/functions/must_use.rs | 6 +-- .../src/functions/not_unsafe_ptr_arg_deref.rs | 6 +-- clippy_lints/src/functions/result_unit_err.rs | 6 +-- .../src/functions/too_many_arguments.rs | 12 ++---- clippy_lints/src/functions/too_many_lines.rs | 4 +- clippy_lints/src/if_then_some_else_none.rs | 2 +- clippy_lints/src/implicit_return.rs | 4 +- .../src/inconsistent_struct_constructor.rs | 2 +- clippy_lints/src/index_refutable_slice.rs | 2 +- .../src/iter_not_returning_iterator.rs | 2 +- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/lib.rs | 1 - clippy_lints/src/loops/empty_loop.rs | 2 +- .../src/loops/explicit_into_iter_loop.rs | 2 +- clippy_lints/src/loops/manual_memcpy.rs | 2 +- clippy_lints/src/loops/mut_range_bound.rs | 2 +- clippy_lints/src/loops/needless_collect.rs | 4 +- clippy_lints/src/loops/never_loop.rs | 4 +- clippy_lints/src/loops/while_let_loop.rs | 2 +- .../src/loops/while_let_on_iterator.rs | 14 +++---- clippy_lints/src/manual_assert.rs | 2 +- clippy_lints/src/manual_map.rs | 17 +++++--- clippy_lints/src/manual_ok_or.rs | 2 +- clippy_lints/src/manual_unwrap_or.rs | 2 +- clippy_lints/src/match_str_case_mismatch.rs | 2 +- clippy_lints/src/matches.rs | 12 ++++-- clippy_lints/src/methods/expect_fun_call.rs | 2 +- .../methods/from_iter_instead_of_collect.rs | 2 +- clippy_lints/src/methods/str_splitn.rs | 4 +- .../src/methods/unnecessary_iter_cloned.rs | 10 ++--- .../src/methods/unnecessary_to_owned.rs | 29 +++++++------- clippy_lints/src/misc.rs | 2 +- clippy_lints/src/needless_for_each.rs | 2 +- clippy_lints/src/needless_late_init.rs | 2 +- clippy_lints/src/no_effect.rs | 4 +- .../src/non_octal_unix_permissions.rs | 2 +- clippy_lints/src/octal_escapes.rs | 4 +- clippy_lints/src/ptr_eq.rs | 2 +- clippy_lints/src/redundant_slicing.rs | 2 +- clippy_lints/src/return_self_not_must_use.rs | 2 +- .../src/semicolon_if_nothing_returned.rs | 2 +- clippy_lints/src/size_of_in_element_count.rs | 7 +++- clippy_lints/src/strings.rs | 4 +- clippy_lints/src/strlen_on_c_strings.rs | 2 +- .../src/suspicious_operation_groupings.rs | 4 +- clippy_lints/src/trailing_empty_array.rs | 2 +- .../src/undocumented_unsafe_blocks.rs | 4 +- clippy_lints/src/undropped_manually_drops.rs | 2 +- clippy_lints/src/uninit_vec.rs | 4 +- clippy_lints/src/unit_hash.rs | 2 +- .../internal_lints/metadata_collector.rs | 12 +++--- clippy_lints/src/vec_init_then_push.rs | 2 +- clippy_utils/src/consts.rs | 2 +- clippy_utils/src/eager_or_lazy.rs | 13 +++++-- clippy_utils/src/lib.rs | 39 ++++++++++--------- clippy_utils/src/macros.rs | 4 +- clippy_utils/src/qualify_min_const_fn.rs | 25 ++++++++---- clippy_utils/src/source.rs | 2 +- clippy_utils/src/sugg.rs | 4 +- clippy_utils/src/ty.rs | 4 +- clippy_utils/src/visitors.rs | 8 ++-- doc/common_tools_writing_lints.md | 2 +- 77 files changed, 216 insertions(+), 189 deletions(-) diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index ea2cfb0bc0828..c50e214be288d 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -42,7 +42,7 @@ fn is_bool_lit(e: &Expr<'_>) -> bool { ) && !e.span.from_expansion() } -fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { +fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(e); cx.tcx diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index 3f286dd9e2fca..e8f39cd37090d 100644 --- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -67,7 +67,7 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: & None } -impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons { +impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons { fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) { span_lint_and_help( diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 248b35b024ee1..b9de5510455b9 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -9,7 +9,7 @@ use rustc_span::symbol::sym; use super::CAST_PTR_ALIGNMENT; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { if is_hir_ty_cfg_dependant(cx, cast_to) { return; diff --git a/clippy_lints/src/casts/cast_ref_to_mut.rs b/clippy_lints/src/casts/cast_ref_to_mut.rs index d9bf1ea58b97b..15f2f81f4079e 100644 --- a/clippy_lints/src/casts/cast_ref_to_mut.rs +++ b/clippy_lints/src/casts/cast_ref_to_mut.rs @@ -6,7 +6,7 @@ use rustc_middle::ty; use super::CAST_REF_TO_MUT; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind; if let ExprKind::Cast(e, t) = &e.kind; diff --git a/clippy_lints/src/casts/char_lit_as_u8.rs b/clippy_lints/src/casts/char_lit_as_u8.rs index 099a0de881ff0..7cc406018dbe0 100644 --- a/clippy_lints/src/casts/char_lit_as_u8.rs +++ b/clippy_lints/src/casts/char_lit_as_u8.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::{self, UintTy}; use super::CHAR_LIT_AS_U8; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::Cast(e, _) = &expr.kind; if let ExprKind::Lit(l) = &e.kind; diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index 3132d3a5cf097..fb04f93fbcf97 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -12,7 +12,7 @@ use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option) { if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) { return; } diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index d07bc23235b0f..73ce656ad1514 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -316,7 +316,7 @@ struct BlockEqual { /// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to /// abort any further processing and avoid duplicate lint triggers. -fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option { +fn scan_block_for_eq(cx: &LateContext<'_>, blocks: &[&Block<'_>]) -> Option { let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; let mut expr_eq = true; @@ -385,11 +385,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option< }) } -fn check_for_warn_of_moved_symbol( - cx: &LateContext<'tcx>, - symbols: &FxHashSet, - if_expr: &'tcx Expr<'_>, -) -> bool { +fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet, if_expr: &Expr<'_>) -> bool { get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| { let ignore_span = block.span.shrink_to_lo().to(if_expr.span); @@ -419,13 +415,13 @@ fn check_for_warn_of_moved_symbol( } fn emit_branches_sharing_code_lint( - cx: &LateContext<'tcx>, + cx: &LateContext<'_>, start_stmts: usize, end_stmts: usize, lint_end: bool, warn_about_moved_symbol: bool, - blocks: &[&Block<'tcx>], - if_expr: &'tcx Expr<'_>, + blocks: &[&Block<'_>], + if_expr: &Expr<'_>, ) { if start_stmts == 0 && !lint_end { return; diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index a0b137efe221a..4274943a3d16a 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -77,7 +77,7 @@ pub struct Default { impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); -impl LateLintPass<'_> for Default { +impl<'tcx> LateLintPass<'tcx> for Default { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if !expr.span.from_expansion(); @@ -110,7 +110,7 @@ impl LateLintPass<'_> for Default { } #[allow(clippy::too_many_lines)] - fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding let stmts_head = match block.stmts { diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 3573ea5f02671..e5131c497aeeb 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -54,7 +54,7 @@ declare_clippy_lint! { declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); -impl LateLintPass<'_> for DefaultNumericFallback { +impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { let mut visitor = NumericFallbackVisitor::new(cx); visitor.visit_body(body); diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index fa2b348591be4..bf077a212fd0f 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -355,7 +355,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { } } -fn try_parse_ref_op( +fn try_parse_ref_op<'tcx>( tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, @@ -387,7 +387,7 @@ fn try_parse_ref_op( // Checks whether the type for a deref call actually changed the type, not just the mutability of // the reference. -fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { +fn deref_method_same_type(result_ty: Ty<'_>, arg_ty: Ty<'_>) -> bool { match (result_ty.kind(), arg_ty.kind()) { (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty), @@ -457,7 +457,7 @@ fn is_linted_explicit_deref_position(parent: Option>, child_id: HirId, } /// Adjustments are sometimes made in the parent block rather than the expression itself. -fn find_adjustments( +fn find_adjustments<'tcx>( tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, @@ -499,7 +499,7 @@ fn find_adjustments( } #[allow(clippy::needless_pass_by_value)] -fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) { +fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) { match state { State::DerefMethod { ty_changed_count, @@ -568,7 +568,7 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat } impl Dereferencing { - fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) { + fn check_local_usage(&mut self, cx: &LateContext<'_>, e: &Expr<'_>, local: HirId) { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 3d92eb16870e3..3ce239273e252 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -233,7 +233,7 @@ struct ContainsExpr<'tcx> { key: &'tcx Expr<'tcx>, call_ctxt: SyntaxContext, } -fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> { +fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> { let mut negated = false; let expr = peel_hir_expr_while(expr, |e| match e.kind { ExprKind::Unary(UnOp::Not, e) => { @@ -280,7 +280,7 @@ struct InsertExpr<'tcx> { key: &'tcx Expr<'tcx>, value: &'tcx Expr<'tcx>, } -fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { +fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind { let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) { @@ -301,7 +301,7 @@ enum Edit<'tcx> { /// An insertion into the map. Insertion(Insertion<'tcx>), } -impl Edit<'tcx> { +impl<'tcx> Edit<'tcx> { fn as_insertion(self) -> Option> { if let Self::Insertion(i) = self { Some(i) } else { None } } @@ -532,7 +532,7 @@ struct InsertSearchResults<'tcx> { allow_insert_closure: bool, is_single_insert: bool, } -impl InsertSearchResults<'tcx> { +impl<'tcx> InsertSearchResults<'tcx> { fn as_single_insertion(&self) -> Option> { self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap()) } @@ -633,7 +633,7 @@ impl InsertSearchResults<'tcx> { } } -fn find_insert_calls( +fn find_insert_calls<'tcx>( cx: &LateContext<'tcx>, contains_expr: &ContainsExpr<'tcx>, expr: &'tcx Expr<'_>, diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index 06d128f5527b5..cf47e581ccb48 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { } } -fn is_structural_partial_eq(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool { +fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool { if let Some(def_id) = cx.tcx.lang_items().eq_trait() { implements_trait(cx, ty, def_id, &[other.into()]) } else { diff --git a/clippy_lints/src/erasing_op.rs b/clippy_lints/src/erasing_op.rs index bb6acd8c5dd4b..c1a84973c4211 100644 --- a/clippy_lints/src/erasing_op.rs +++ b/clippy_lints/src/erasing_op.rs @@ -50,13 +50,19 @@ impl<'tcx> LateLintPass<'tcx> for ErasingOp { } } -fn different_types(tck: &TypeckResults<'tcx>, input: &'tcx Expr<'_>, output: &'tcx Expr<'_>) -> bool { +fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool { let input_ty = tck.expr_ty(input).peel_refs(); let output_ty = tck.expr_ty(output).peel_refs(); !same_type_and_consts(input_ty, output_ty) } -fn check(cx: &LateContext<'cx>, tck: &TypeckResults<'cx>, op: &Expr<'cx>, other: &Expr<'cx>, parent: &Expr<'cx>) { +fn check<'tcx>( + cx: &LateContext<'tcx>, + tck: &TypeckResults<'tcx>, + op: &Expr<'tcx>, + other: &Expr<'tcx>, + parent: &Expr<'tcx>, +) { if constant_simple(cx, tck, op) == Some(Constant::Int(0)) { if different_types(tck, other, parent) { return; diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 866ff216f84a2..5ece2cc5ac4f7 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -53,7 +53,7 @@ impl FromOverInto { impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); -impl LateLintPass<'_> for FromOverInto { +impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) { return; diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 73e800073b03f..57b0751320521 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]); -impl LateLintPass<'tcx> for FromStrRadix10 { +impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { if_chain! { if let ExprKind::Call(maybe_path, arguments) = &exp.kind; diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 77d08081c07f1..f2b4aefaead52 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -18,7 +18,7 @@ use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; -pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { +pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = must_use_attr(attrs); if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { @@ -40,7 +40,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { } } -pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { +pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -62,7 +62,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem< } } -pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index f83789bb2199e..6d829a18b2e09 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -9,7 +9,7 @@ use clippy_utils::{iter_input_pats, path_to_local}; use super::NOT_UNSAFE_PTR_ARG_DEREF; -pub(super) fn check_fn( +pub(super) fn check_fn<'tcx>( cx: &LateContext<'tcx>, kind: intravisit::FnKind<'tcx>, decl: &'tcx hir::FnDecl<'tcx>, @@ -25,14 +25,14 @@ pub(super) fn check_fn( check_raw_ptr(cx, unsafety, decl, body, cx.tcx.hir().local_def_id(hir_id)); } -pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind { let body = cx.tcx.hir().body(eid); check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.def_id); } } -fn check_raw_ptr( +fn check_raw_ptr<'tcx>( cx: &LateContext<'tcx>, unsafety: hir::Unsafety, decl: &'tcx hir::FnDecl<'tcx>, diff --git a/clippy_lints/src/functions/result_unit_err.rs b/clippy_lints/src/functions/result_unit_err.rs index 71f6f87ae602b..73f08a0498973 100644 --- a/clippy_lints/src/functions/result_unit_err.rs +++ b/clippy_lints/src/functions/result_unit_err.rs @@ -13,7 +13,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use super::RESULT_UNIT_ERR; -pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { +pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -23,7 +23,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { } } -pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { +pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -33,7 +33,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem< } } -pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { +pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs index 63a14d8d4cde8..3af960491ed01 100644 --- a/clippy_lints/src/functions/too_many_arguments.rs +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -9,9 +9,9 @@ use clippy_utils::is_trait_impl_item; use super::TOO_MANY_ARGUMENTS; pub(super) fn check_fn( - cx: &LateContext<'tcx>, - kind: intravisit::FnKind<'tcx>, - decl: &'tcx hir::FnDecl<'_>, + cx: &LateContext<'_>, + kind: intravisit::FnKind<'_>, + decl: &hir::FnDecl<'_>, span: Span, hir_id: hir::HirId, too_many_arguments_threshold: u64, @@ -39,11 +39,7 @@ pub(super) fn check_fn( } } -pub(super) fn check_trait_item( - cx: &LateContext<'tcx>, - item: &'tcx hir::TraitItem<'_>, - too_many_arguments_threshold: u64, -) { +pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>, too_many_arguments_threshold: u64) { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { // don't lint extern functions decls, it's not their fault if sig.header.abi == Abi::Rust { diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index 65efbbab41a46..54bdea7ea25d6 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -11,9 +11,9 @@ use super::TOO_MANY_LINES; pub(super) fn check_fn( cx: &LateContext<'_>, - kind: FnKind<'tcx>, + kind: FnKind<'_>, span: Span, - body: &'tcx hir::Body<'_>, + body: &hir::Body<'_>, too_many_lines_threshold: u64, ) { // Closures must be contained in a parent body, which will be checked for `too_many_lines`. diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 16e5c5ca603db..9525c163ece17 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -55,7 +55,7 @@ impl IfThenSomeElseNone { impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); -impl LateLintPass<'_> for IfThenSomeElseNone { +impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) { return; diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 07caeada80d00..d650d6e9a8587 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -94,8 +94,8 @@ fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option { } fn lint_implicit_returns( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, + cx: &LateContext<'_>, + expr: &Expr<'_>, // The context of the function body. ctxt: SyntaxContext, // Whether the expression is from a macro expansion. diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 1debdef9d86c7..9e03065e7fbfe 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -63,7 +63,7 @@ declare_clippy_lint! { declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); -impl LateLintPass<'_> for InconsistentStructConstructor { +impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { if !expr.span.from_expansion(); diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 69f1c90beec5d..073313e2bad4e 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -69,7 +69,7 @@ impl IndexRefutableSlice { impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); -impl LateLintPass<'_> for IndexRefutableSlice { +impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index 017a8a779d942..d3bdc819a9f2b 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -39,7 +39,7 @@ declare_clippy_lint! { declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]); -impl LateLintPass<'_> for IterNotReturningIterator { +impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { let name = item.ident.name.as_str(); if matches!(name, "iter" | "iter_mut") { diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 20e6220ec7d3a..265c24b1c976d 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -245,7 +245,7 @@ enum LenOutput<'tcx> { Option(DefId), Result(DefId, Ty<'tcx>), } -fn parse_len_output(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option> { +fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option> { match *sig.output().kind() { ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral), ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did) => { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0d765c2bcde1b..175a35e7994c1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -4,7 +4,6 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(drain_filter)] -#![feature(in_band_lifetimes)] #![feature(iter_intersperse)] #![feature(let_else)] #![feature(once_cell)] diff --git a/clippy_lints/src/loops/empty_loop.rs b/clippy_lints/src/loops/empty_loop.rs index dda09fecdf90f..823cf0f43221c 100644 --- a/clippy_lints/src/loops/empty_loop.rs +++ b/clippy_lints/src/loops/empty_loop.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_in_panic_handler, is_no_std_crate}; use rustc_hir::{Block, Expr}; use rustc_lint::LateContext; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) { if loop_block.stmts.is_empty() && loop_block.expr.is_none() && !is_in_panic_handler(cx, expr) { let msg = "empty `loop {}` wastes CPU cycles"; let help = if is_no_std_crate(cx) { diff --git a/clippy_lints/src/loops/explicit_into_iter_loop.rs b/clippy_lints/src/loops/explicit_into_iter_loop.rs index 1bab0d99b695c..17246cc5426ae 100644 --- a/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -8,7 +8,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::TyS; use rustc_span::symbol::sym; -pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) { let self_ty = cx.typeck_results().expr_ty(self_arg); let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg); if !(TyS::same_type(self_ty, self_ty_adjusted) && is_trait_method(cx, call_expr, sym::IntoIterator)) { diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index c62fa5e998bd4..c439f701da1b8 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -203,7 +203,7 @@ fn build_manual_memcpy_suggestion<'tcx>( #[derive(Clone)] struct MinifyingSugg<'a>(Sugg<'a>); -impl Display for MinifyingSugg<'a> { +impl<'a> Display for MinifyingSugg<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index aedf0844937d1..37a57d8feb1d3 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -147,7 +147,7 @@ impl BreakAfterExprVisitor { } } -impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor { +impl<'tcx> intravisit::Visitor<'tcx> for BreakAfterExprVisitor { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index ba895f35faa26..6248680aa6216 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -339,8 +339,8 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>( } } -fn get_captured_ids(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>) -> HirIdSet { - fn get_captured_ids_recursive(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>, set: &mut HirIdSet) { +fn get_captured_ids(cx: &LateContext<'_>, ty: &'_ TyS<'_>) -> HirIdSet { + fn get_captured_ids_recursive(cx: &LateContext<'_>, ty: &'_ TyS<'_>, set: &mut HirIdSet) { match ty.kind() { ty::Adt(_, generics) => { for generic in *generics { diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index a3aa6be6afd64..bb1b3e2a1ec6a 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -10,8 +10,8 @@ use rustc_span::Span; use std::iter::{once, Iterator}; pub(super) fn check( - cx: &LateContext<'tcx>, - block: &'tcx Block<'_>, + cx: &LateContext<'_>, + block: &Block<'_>, loop_id: HirId, span: Span, for_loop: Option<&ForLoop<'_>>, diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index 4dcd5c87722ee..8f57df0be6bd4 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -7,7 +7,7 @@ use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { // extract the expression from the first statement (if any) in a block let inner_stmt_expr = extract_expr_from_first_stmt(loop_block); // or extract the first expression (if any) from the block diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 942d6ca6b4a3a..750328d1d01a5 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -13,7 +13,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_span::{symbol::sym, Symbol}; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! { if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr); // check for `Some(..)` pattern @@ -191,7 +191,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie /// Strips off all field and path expressions. This will return true if a field or path has been /// skipped. Used to skip them after failing to check for equality. -fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { +fn skip_fields_and_path<'tcx>(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { let mut e = expr; let e = loop { match e.kind { @@ -204,13 +204,13 @@ fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool } /// Checks if the given expression uses the iterator. -fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool { +fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool { struct V<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, iter_expr: &'b IterExpr, uses_iter: bool, } - impl Visitor<'tcx> for V<'_, '_, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -245,7 +245,7 @@ fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr } #[allow(clippy::too_many_lines)] -fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool { +fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &Expr<'_>) -> bool { struct AfterLoopVisitor<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, iter_expr: &'b IterExpr, @@ -253,7 +253,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: after_loop: bool, used_iter: bool, } - impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { + impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -292,7 +292,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: found_local: bool, used_after: bool, } - impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { + impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index e3a34b22e3252..26b53ab5d6837 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); -impl LateLintPass<'_> for ManualAssert { +impl<'tcx> LateLintPass<'tcx> for ManualAssert { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if_chain! { if let ExprKind::If(cond, then, None) = expr.kind; diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 34a70ca76c6a2..8475e367b09fe 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualMap => [MANUAL_MAP]); -impl LateLintPass<'_> for ManualMap { +impl<'tcx> LateLintPass<'tcx> for ManualMap { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) { @@ -219,7 +219,7 @@ impl LateLintPass<'_> for ManualMap { // Checks whether the expression could be passed as a function, or whether a closure is needed. // Returns the function to be passed to `map` if it exists. -fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) if path_to_local_id(arg, binding) @@ -251,8 +251,13 @@ struct SomeExpr<'tcx> { // Try to parse into a recognized `Option` pattern. // i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { - fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option> { +fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { + fn f<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ref_count: usize, + ctxt: SyntaxContext, + ) -> Option> { match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), @@ -269,7 +274,7 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon } // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. -fn get_some_expr( +fn get_some_expr<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, needs_unsafe_block: bool, @@ -306,6 +311,6 @@ fn get_some_expr( } // Checks for the `None` value. -fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) } diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index b60e2dc366b41..bd083e3e9e201 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]); -impl LateLintPass<'_> for ManualOkOr { +impl<'tcx> LateLintPass<'tcx> for ManualOkOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) { if in_external_macro(cx.sess(), scrutinee.span) { return; diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index aac3c6e0de2bb..b3a91d9f18f5d 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); -impl LateLintPass<'_> for ManualUnwrapOr { +impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { return; diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/match_str_case_mismatch.rs index 2c0fc218ca07c..1fc7eb7214284 100644 --- a/clippy_lints/src/match_str_case_mismatch.rs +++ b/clippy_lints/src/match_str_case_mismatch.rs @@ -55,7 +55,7 @@ enum CaseMethod { AsciiUppercase, } -impl LateLintPass<'_> for MatchStrCaseMismatch { +impl<'tcx> LateLintPass<'tcx> for MatchStrCaseMismatch { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if !in_external_macro(cx.tcx.sess, expr.span); diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 0d693499bc67f..66c1886710f15 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -999,7 +999,7 @@ enum CommonPrefixSearcher<'a> { Path(&'a [PathSegment<'a>]), Mixed, } -impl CommonPrefixSearcher<'a> { +impl<'a> CommonPrefixSearcher<'a> { fn with_path(&mut self, path: &'a [PathSegment<'a>]) { match path { [path @ .., _] => self.with_prefix(path), @@ -1804,11 +1804,15 @@ mod redundant_pattern_match { /// Checks if the drop order for a type matters. Some std types implement drop solely to /// deallocate memory. For these types, and composites containing them, changing the drop order /// won't result in any observable side effects. - fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) } - fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet>) -> bool { + fn type_needs_ordered_drop_inner<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + seen: &mut FxHashSet>, + ) -> bool { if !seen.insert(ty) { return false; } @@ -1870,7 +1874,7 @@ mod redundant_pattern_match { // Checks if there are any temporaries created in the given expression for which drop order // matters. - fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, res: bool, diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 866eaff5b1d76..0f39470f34262 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -14,7 +14,7 @@ use super::EXPECT_FUN_CALL; /// Checks for the `EXPECT_FUN_CALL` lint. #[allow(clippy::too_many_lines)] -pub(super) fn check( +pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_span: Span, diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 8ea9312c0f707..6436e28a63c52 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp } } -fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String { +fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> String { fn strip_angle_brackets(s: &str) -> Option<&str> { s.strip_prefix('<')?.strip_suffix('>') } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 70f20da1d6db7..514bdadc442e3 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -124,7 +124,7 @@ struct IterUsage { } #[allow(clippy::too_many_lines)] -fn parse_iter_usage( +fn parse_iter_usage<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, mut iter: impl Iterator)>, @@ -281,7 +281,7 @@ pub(super) fn check_needless_splitn( } } -fn check_iter( +fn check_iter<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, mut iter: impl Iterator)>, diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 4f589a6d3187e..5999245ea7d00 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -12,7 +12,7 @@ use rustc_span::{sym, Symbol}; use super::UNNECESSARY_TO_OWNED; -pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, receiver: &'tcx Expr<'tcx>) -> bool { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); if let Some(callee_def_id) = fn_def_id(cx, parent); @@ -30,10 +30,10 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol /// include this code directly is so that it can be called from /// `unnecessary_into_owned::check_into_iter_call_arg`. pub fn check_for_loop_iter( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, + cx: &LateContext<'_>, + expr: &Expr<'_>, method_name: Symbol, - receiver: &'tcx Expr<'tcx>, + receiver: &Expr<'_>, cloned_before_iter: bool, ) -> bool { if_chain! { @@ -101,7 +101,7 @@ pub fn check_for_loop_iter( /// The core logic of `check_for_loop_iter` above, this function wraps a use of /// `CloneOrCopyVisitor`. -fn clone_or_copy_needed( +fn clone_or_copy_needed<'tcx>( cx: &LateContext<'tcx>, pat: &Pat<'tcx>, body: &'tcx Expr<'tcx>, diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 7e17f163b2126..e5b6d296b2d25 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -16,7 +16,7 @@ use std::cmp::max; use super::UNNECESSARY_TO_OWNED; -pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) { +pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let [receiver] = args; @@ -44,11 +44,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol /// call of a `to_owned`-like function is unnecessary. #[allow(clippy::too_many_lines)] fn check_addr_of_expr( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, + cx: &LateContext<'_>, + expr: &Expr<'_>, method_name: Symbol, method_def_id: DefId, - receiver: &'tcx Expr<'tcx>, + receiver: &Expr<'_>, ) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); @@ -171,12 +171,7 @@ fn check_addr_of_expr( /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its /// call of a `to_owned`-like function is unnecessary. -fn check_into_iter_call_arg( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, - method_name: Symbol, - receiver: &'tcx Expr<'tcx>, -) -> bool { +fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); if let Some(callee_def_id) = fn_def_id(cx, parent); @@ -221,7 +216,7 @@ fn check_into_iter_call_arg( /// Checks whether `expr` is an argument in a function call and, if so, determines whether its call /// of a `to_owned`-like function is unnecessary. -fn check_other_call_arg( +fn check_other_call_arg<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, @@ -287,7 +282,7 @@ fn check_other_call_arg( /// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such /// expression found (if any) along with the immediately prior expression. -fn skip_addr_of_ancestors( +fn skip_addr_of_ancestors<'tcx>( cx: &LateContext<'tcx>, mut expr: &'tcx Expr<'tcx>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { @@ -303,7 +298,7 @@ fn skip_addr_of_ancestors( /// Checks whether an expression is a function or method call and, if so, returns its `DefId`, /// `Substs`, and arguments. -fn get_callee_substs_and_args( +fn get_callee_substs_and_args<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, ) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> { @@ -328,7 +323,7 @@ fn get_callee_substs_and_args( } /// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type. -fn get_input_traits_and_projections( +fn get_input_traits_and_projections<'tcx>( cx: &LateContext<'tcx>, callee_def_id: DefId, input: Ty<'tcx>, @@ -368,7 +363,11 @@ fn get_input_traits_and_projections( } /// Composes two substitutions by applying the latter to the types of the former. -fn compose_substs(cx: &LateContext<'tcx>, left: &[GenericArg<'tcx>], right: SubstsRef<'tcx>) -> Vec> { +fn compose_substs<'tcx>( + cx: &LateContext<'tcx>, + left: &[GenericArg<'tcx>], + right: SubstsRef<'tcx>, +) -> Vec> { left.iter() .map(|arg| { if let GenericArgKind::Type(arg_ty) = arg.unpack() { diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 401dc27811dc3..21b3f81d5d981 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -717,7 +717,7 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) } } -fn check_binary( +fn check_binary<'a>( cx: &LateContext<'a>, expr: &Expr<'_>, cmp: &rustc_span::source_map::Spanned, diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 0c1da0351739a..19d58f7474b0a 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -48,7 +48,7 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); -impl LateLintPass<'_> for NeedlessForEach { +impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { let expr = match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 094a3f111ba5a..9957afcbf04aa 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -330,7 +330,7 @@ fn check<'tcx>( Some(()) } -impl LateLintPass<'tcx> for NeedlessLateInit { +impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 9d5babc5de840..5bf8a1ba1ca30 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { } } -fn check_no_effect(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> bool { +fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { if let StmtKind::Semi(expr) = stmt.kind { if has_no_effect(cx, expr) { span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); @@ -155,7 +155,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } -fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { +fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if_chain! { if let StmtKind::Semi(expr) = stmt.kind; if let Some(reduced) = reduce_expression(cx, expr); diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 4b57dbc4c412a..e46fee4cac5ee 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(NonOctalUnixPermissions => [NON_OCTAL_UNIX_PERMISSIONS]); -impl LateLintPass<'_> for NonOctalUnixPermissions { +impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { match &expr.kind { ExprKind::MethodCall(path, _, [func, param], _) => { diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index 9c97143764545..e0da12f77fcc7 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]); impl EarlyLintPass for OctalEscapes { - fn check_expr(&mut self, cx: &EarlyContext<'tcx>, expr: &Expr) { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if in_external_macro(cx.sess, expr.span) { return; } @@ -65,7 +65,7 @@ impl EarlyLintPass for OctalEscapes { } } -fn check_lit(cx: &EarlyContext<'tcx>, lit: &Lit, span: Span, is_string: bool) { +fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { let contents = lit.symbol.as_str(); let mut iter = contents.char_indices().peekable(); let mut found = vec![]; diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs index 3c126fc1ca69a..2bec93ac60605 100644 --- a/clippy_lints/src/ptr_eq.rs +++ b/clippy_lints/src/ptr_eq.rs @@ -39,7 +39,7 @@ declare_lint_pass!(PtrEq => [PTR_EQ]); static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers"; -impl LateLintPass<'_> for PtrEq { +impl<'tcx> LateLintPass<'tcx> for PtrEq { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.from_expansion() { return; diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index b2bd0103d1114..7c88b42ea3199 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -42,7 +42,7 @@ declare_clippy_lint! { declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]); -impl LateLintPass<'_> for RedundantSlicing { +impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.from_expansion() { return; diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 935bbc37d75d0..5dafd08cf3be0 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -66,7 +66,7 @@ declare_clippy_lint! { declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]); -fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalDefId, span: Span, hir_id: HirId) { +fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, span: Span, hir_id: HirId) { if_chain! { // If it comes from an external macro, better ignore it. if !in_external_macro(cx.sess(), span); diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 0b3bbbc815580..729694da46d5c 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED]); -impl LateLintPass<'_> for SemicolonIfNothingReturned { +impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if_chain! { if !block.span.from_expansion(); diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index df1e85afdd799..9b195f3c0a228 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); -fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option> { +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option> { match expr.kind { ExprKind::Call(count_func, _func_args) => { if_chain! { @@ -64,7 +64,10 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) } } -fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { +fn get_pointee_ty_and_count_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, +) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { const FUNCTIONS: [&[&str]; 8] = [ &paths::PTR_COPY_NONOVERLAPPING, &paths::PTR_COPY, diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index ad8e72ad764e1..b4a71aefd437f 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -381,7 +381,7 @@ declare_clippy_lint! { declare_lint_pass!(StrToString => [STR_TO_STRING]); -impl LateLintPass<'_> for StrToString { +impl<'tcx> LateLintPass<'tcx> for StrToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; @@ -431,7 +431,7 @@ declare_clippy_lint! { declare_lint_pass!(StringToString => [STRING_TO_STRING]); -impl LateLintPass<'_> for StringToString { +impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs index fee01fb0bd186..d6e948a75607b 100644 --- a/clippy_lints/src/strlen_on_c_strings.rs +++ b/clippy_lints/src/strlen_on_c_strings.rs @@ -39,7 +39,7 @@ declare_clippy_lint! { declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]); -impl LateLintPass<'tcx> for StrlenOnCStrings { +impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if !expr.span.from_expansion(); diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index faf43fd9fc1ad..ca725918e873e 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -355,7 +355,7 @@ struct BinaryOp<'exprs> { right: &'exprs Expr, } -impl BinaryOp<'exprs> { +impl<'exprs> BinaryOp<'exprs> { fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self { Self { op, span, left, right } } @@ -419,7 +419,7 @@ fn chained_binops(kind: &ExprKind) -> Option>> { } } -fn chained_binops_helper(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option>> { +fn chained_binops_helper<'expr>(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option>> { match (&left_outer.kind, &right_outer.kind) { ( ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs index 47c0a84cd4630..bf1cbf4f6929d 100644 --- a/clippy_lints/src/trailing_empty_array.rs +++ b/clippy_lints/src/trailing_empty_array.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { } } -fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { +fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if_chain! { // First check if last field is an array if let ItemKind::Struct(data, _) = &item.kind; diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 3d3b4a6679dd1..697ed267e2f35 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -113,8 +113,8 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { } } -impl<'hir> Visitor<'hir> for UndocumentedUnsafeBlocks { - type Map = Map<'hir>; +impl<'v> Visitor<'v> for UndocumentedUnsafeBlocks { + type Map = Map<'v>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs index c58fa67a02388..7557e14d11f52 100644 --- a/clippy_lints/src/undropped_manually_drops.rs +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); -impl LateLintPass<'tcx> for UndroppedManuallyDrops { +impl<'tcx> LateLintPass<'tcx> for UndroppedManuallyDrops { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some([arg_0, ..]) = match_function_call(cx, expr, &paths::DROP) { let ty = cx.typeck_results().expr_ty(arg_0); diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 46cc76b150e4a..2ffaf24f942ae 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for UninitVec { } } -fn handle_uninit_vec_pair( +fn handle_uninit_vec_pair<'tcx>( cx: &LateContext<'tcx>, maybe_init_or_reserve: &'tcx Stmt<'tcx>, maybe_set_len: &'tcx Expr<'tcx>, @@ -196,7 +196,7 @@ fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_> } /// Returns self if the expression is `Vec::set_len()` -fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> { +fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> { // peel unsafe blocks in `unsafe { vec.set_len() }` let expr = peel_hir_expr_while(expr, |e| { if let ExprKind::Block(block, _) = e.kind { diff --git a/clippy_lints/src/unit_hash.rs b/clippy_lints/src/unit_hash.rs index 26b4e0f58a870..dcf8a9d7c84d3 100644 --- a/clippy_lints/src/unit_hash.rs +++ b/clippy_lints/src/unit_hash.rs @@ -46,7 +46,7 @@ declare_clippy_lint! { } declare_lint_pass!(UnitHash => [UNIT_HASH]); -impl LateLintPass<'tcx> for UnitHash { +impl<'tcx> LateLintPass<'tcx> for UnitHash { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 02f5d45cc18f4..4e46d79dc0870 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -577,7 +577,7 @@ fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String { fn get_lint_group_and_level_or_lint( cx: &LateContext<'_>, lint_name: &str, - item: &'hir Item<'_>, + item: &Item<'_>, ) -> Option<(String, &'static str)> { let result = cx .lint_store @@ -696,20 +696,20 @@ fn extract_emission_info<'hir>( } /// Resolves the possible lints that this expression could reference -fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec { +fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec { let mut resolver = LintResolver::new(cx); resolver.visit_expr(expr); resolver.lints } /// This function tries to resolve the linked applicability to the given expression. -fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option { +fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option { let mut resolver = ApplicabilityResolver::new(cx); resolver.visit_expr(expr); resolver.complete() } -fn check_is_multi_part(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool { +fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool { if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind { let mut scanner = IsMultiSpanScanner::new(cx); intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id)); @@ -824,7 +824,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { } /// This returns the parent local node if the expression is a reference one -fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> { +fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> { if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind { if let hir::def::Res::Local(local_hir) = path.res { return get_parent_local_hir_id(cx, local_hir); @@ -834,7 +834,7 @@ fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Opti None } -fn get_parent_local_hir_id(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { +fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { let map = cx.tcx.hir(); match map.find(map.get_parent_node(hir_id)) { diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 1bc0eb6303c01..43474da3450e2 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -77,7 +77,7 @@ impl VecPushSearcher { } } -impl LateLintPass<'_> for VecInitThenPush { +impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { self.searcher = None; } diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 5c024612f8eba..34c5af848a6db 100644 --- a/clippy_utils/src/consts.rs +++ b/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/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 61e529a6079c9..c3936ec95d46d 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/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/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 16bcffb8df9e7..9f2018385f380 100644 --- a/clippy_utils/src/lib.rs +++ b/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)] @@ -128,7 +127,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) => { @@ -277,7 +276,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 { @@ -293,7 +296,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, @@ -370,7 +373,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, @@ -378,7 +381,7 @@ pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> } } -pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator> { +pub fn get_qpath_generic_tys<'tcx>(path: &QPath<'tcx>) -> impl Iterator> { get_qpath_generics(path) .map_or([].as_ref(), |a| a.args) .iter() @@ -767,7 +770,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], @@ -842,7 +845,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 @@ -942,7 +945,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> { +pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, // Stack of potential break targets contained in the expression. @@ -955,7 +958,7 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> /// mutable reference. captures: HirIdMap, } - impl Visitor<'tcx> for V<'_, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -1212,7 +1215,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( @@ -1720,7 +1723,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( _, &[ @@ -1824,7 +1827,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> { +pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option> { let mut child_id = expr.hir_id; let mut iter = tcx.hir().parent_iter(child_id); loop { @@ -2030,8 +2033,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 { @@ -2054,7 +2057,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 => { @@ -2068,7 +2071,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) => { @@ -2151,7 +2154,7 @@ impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> { static TEST_ITEM_NAMES_CACHE: SyncOnceCell>>> = 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>> = cache.lock().unwrap(); match map.entry(module) { diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 2bf43aeb995a4..a3e336d701cab 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -283,7 +283,7 @@ fn find_assert_within_debug_assert<'a>( found } -fn is_assert_arg(cx: &LateContext<'_>, expr: &'a Expr<'a>, assert_expn: ExpnId) -> bool { +fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool { if !expr.span.from_expansion() { return true; } @@ -322,7 +322,7 @@ pub struct FormatArgsExpn<'tcx> { pub specs: Vec<&'tcx Expr<'tcx>>, } -impl FormatArgsExpn<'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 { macro_backtrace(expr.span).find(|macro_call| { diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 1a4da1627b784..d6d43e93e7a41 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/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), @@ -211,7 +217,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)) => { @@ -240,7 +251,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) { @@ -250,7 +261,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; @@ -275,7 +286,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/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index d928317259da0..dbad607c58ea3 100644 --- a/clippy_utils/src/source.rs +++ b/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/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 92662c59226a2..87bc8232dde3a 100644 --- a/clippy_utils/src/sugg.rs +++ b/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/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 6d191d4a59bde..72317447159a9 100644 --- a/clippy_utils/src/ty.rs +++ b/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/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 4bfd3c64b9c36..b60cd4736f32a 100644 --- a/clippy_utils/src/visitors.rs +++ b/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>(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, diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index c7e51d53f511d..207b0be154885 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -60,7 +60,7 @@ Two noticeable items here: Starting with an `expr`, you can check whether it is calling a specific method `some_method`: ```rust -impl LateLintPass<'_> for MyStructLint { +impl<'tcx> LateLintPass<'tcx> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { // Check our expr is calling a method From 8680a44c0f883516ccc73bd6e174c2f25307791f Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 11 Jan 2022 15:46:53 +0100 Subject: [PATCH 52/72] Partially stabilize `maybe_uninit_extra` This covers: impl MaybeUninit { pub unsafe fn assume_init_read(&self) -> T { ... } pub unsafe fn assume_init_drop(&mut self) { ... } } It does not cover the const-ness of `write` under `const_maybe_uninit_write` nor the const-ness of `assume_init_read` (this commit adds `const_maybe_uninit_assume_init_read` for that). FCP: https://github.com/rust-lang/rust/issues/63567#issuecomment-958590287. Signed-off-by: Miguel Ojeda --- library/alloc/src/lib.rs | 1 - library/core/src/mem/maybe_uninit.rs | 10 ++++------ library/core/tests/lib.rs | 2 +- library/std/src/lib.rs | 1 - 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 7e663fab16af5..1cbc2b65f4dbd 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -119,7 +119,6 @@ #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(layout_for_ptr)] -#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_slice)] #![cfg_attr(test, feature(new_uninit))] #![feature(nonnull_slice_from_raw_parts)] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 3b0e4a31db1c8..dd2f73063566e 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -330,7 +330,7 @@ impl MaybeUninit { /// # Examples /// /// ```no_run - /// #![feature(maybe_uninit_uninit_array, maybe_uninit_extra, maybe_uninit_slice)] + /// #![feature(maybe_uninit_uninit_array, maybe_uninit_slice)] /// /// use std::mem::MaybeUninit; /// @@ -662,7 +662,6 @@ impl MaybeUninit { /// Correct usage of this method: /// /// ```rust - /// #![feature(maybe_uninit_extra)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::::uninit(); @@ -683,7 +682,6 @@ impl MaybeUninit { /// *Incorrect* usage of this method: /// /// ```rust,no_run - /// #![feature(maybe_uninit_extra)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::>>::uninit(); @@ -693,8 +691,8 @@ impl MaybeUninit { /// // We now created two copies of the same vector, leading to a double-free ⚠️ when /// // they both get dropped! /// ``` - #[unstable(feature = "maybe_uninit_extra", issue = "63567")] - #[rustc_const_unstable(feature = "maybe_uninit_extra", issue = "63567")] + #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_maybe_uninit_assume_init_read", issue = "63567")] #[inline(always)] #[track_caller] pub const unsafe fn assume_init_read(&self) -> T { @@ -728,7 +726,7 @@ impl MaybeUninit { /// /// [`assume_init`]: MaybeUninit::assume_init /// [`Vec`]: ../../std/vec/struct.Vec.html - #[unstable(feature = "maybe_uninit_extra", issue = "63567")] + #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] pub unsafe fn assume_init_drop(&mut self) { // SAFETY: the caller must guarantee that `self` is initialized and // satisfies all invariants of `T`. diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index ec700346ac91d..841c114063dc1 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -15,6 +15,7 @@ #![feature(const_convert)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_maybe_uninit_assume_init)] +#![feature(const_maybe_uninit_assume_init_read)] #![feature(const_num_from_num)] #![feature(const_ptr_read)] #![feature(const_ptr_write)] @@ -46,7 +47,6 @@ #![feature(slice_take)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_array_assume_init)] -#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(numfmt)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 4ba4e2a528e60..1721e16f3a686 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -297,7 +297,6 @@ #![feature(llvm_asm)] #![feature(log_syntax)] #![feature(map_try_insert)] -#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_write_slice)] From 7e989ec07a2725c8ba88f2030ef7099bb44f08e6 Mon Sep 17 00:00:00 2001 From: Pradyumna Rahul Date: Mon, 10 Jan 2022 21:44:10 +0530 Subject: [PATCH 53/72] Improve documentation for `borrowed-box` lint --- clippy_lints/src/types/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 481e595743585..9d57505e55ed6 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -167,8 +167,9 @@ declare_clippy_lint! { /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// ### Why is this bad? - /// Any `&Box` can also be a `&T`, which is more - /// general. + /// A `&Box` parameter requires the function caller to box `T` first before passing it to a function. + /// Using `&T` defines a concrete type for the parameter and generalizes the function, this would also + /// auto-deref to `&T` at the function call site if passed a `&Box`. /// /// ### Example /// ```rust,ignore From 40f33a7a4c3cf928576429a89bca522b94b081cd Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 11 Jan 2022 20:57:40 -0800 Subject: [PATCH 54/72] Deduplicate box deref and regular deref suggestions --- compiler/rustc_typeck/src/check/demand.rs | 35 ++++++------------- .../ui/infinite/infinite-autoderef.stderr | 9 +++-- src/test/ui/occurs-check-2.stderr | 9 +++-- src/test/ui/occurs-check.stderr | 9 +++-- src/test/ui/span/coerce-suggestions.stderr | 9 +++-- .../ui/suggestions/boxed-variant-field.rs | 2 +- .../ui/suggestions/boxed-variant-field.stderr | 8 ++--- src/test/ui/terr-sorts.stderr | 2 +- 8 files changed, 40 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index b7e276b69656f..62179821d233d 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -31,7 +31,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: TypeError<'tcx>, ) { self.annotate_expected_due_to_let_ty(err, expr, error); - self.suggest_box_deref(err, expr, expected, expr_ty); self.suggest_compatible_variants(err, expr, expected, expr_ty); self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr); if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { @@ -259,23 +258,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn suggest_box_deref( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - expr_ty: Ty<'tcx>, - ) { - if expr_ty.is_box() && expr_ty.boxed_ty() == expected { - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "try dereferencing the `Box`", - "*".to_string(), - Applicability::MachineApplicable, - ); - } - } - /// If the expected type is an enum (Issue #55250) with any variants whose /// sole field is of the found type, suggest such variants. (Issue #42764) fn suggest_compatible_variants( @@ -857,14 +839,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, false, )); - } else if self.infcx.type_is_copy_modulo_regions( - self.param_env, - expected, - sp, - ) { - // For this suggestion to make sense, the type would need to be `Copy`. + } + + // For this suggestion to make sense, the type would need to be `Copy`, + // or we have to be moving out of a `Box` + if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) + || checked_ty.is_box() + { if let Ok(code) = sm.span_to_snippet(expr.span) { - let message = if checked_ty.is_region_ptr() { + let message = if checked_ty.is_box() { + "consider unboxing the value" + } else if checked_ty.is_region_ptr() { "consider dereferencing the borrow" } else { "consider dereferencing the type" diff --git a/src/test/ui/infinite/infinite-autoderef.stderr b/src/test/ui/infinite/infinite-autoderef.stderr index 03e4718f5dfef..2d29f0592e14b 100644 --- a/src/test/ui/infinite/infinite-autoderef.stderr +++ b/src/test/ui/infinite/infinite-autoderef.stderr @@ -2,9 +2,12 @@ error[E0308]: mismatched types --> $DIR/infinite-autoderef.rs:20:13 | LL | x = Box::new(x); - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | cyclic type of infinite size + | ^^^^^^^^^^^ cyclic type of infinite size + | +help: consider unboxing the value + | +LL | x = *Box::new(x); + | + error[E0055]: reached the recursion limit while auto-dereferencing `Foo` --> $DIR/infinite-autoderef.rs:25:5 diff --git a/src/test/ui/occurs-check-2.stderr b/src/test/ui/occurs-check-2.stderr index dcbfc81b4d519..b68c3fa5bcc9c 100644 --- a/src/test/ui/occurs-check-2.stderr +++ b/src/test/ui/occurs-check-2.stderr @@ -2,9 +2,12 @@ error[E0308]: mismatched types --> $DIR/occurs-check-2.rs:7:9 | LL | f = Box::new(g); - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | cyclic type of infinite size + | ^^^^^^^^^^^ cyclic type of infinite size + | +help: consider unboxing the value + | +LL | f = *Box::new(g); + | + error: aborting due to previous error diff --git a/src/test/ui/occurs-check.stderr b/src/test/ui/occurs-check.stderr index 3e1ef2e719ad5..fdbbdc3abb377 100644 --- a/src/test/ui/occurs-check.stderr +++ b/src/test/ui/occurs-check.stderr @@ -2,9 +2,12 @@ error[E0308]: mismatched types --> $DIR/occurs-check.rs:5:9 | LL | f = Box::new(f); - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | cyclic type of infinite size + | ^^^^^^^^^^^ cyclic type of infinite size + | +help: consider unboxing the value + | +LL | f = *Box::new(f); + | + error: aborting due to previous error diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index 74caae8645c1d..5a964c5d5cc66 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -38,9 +38,12 @@ error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:17:9 | LL | f = Box::new(f); - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | cyclic type of infinite size + | ^^^^^^^^^^^ cyclic type of infinite size + | +help: consider unboxing the value + | +LL | f = *Box::new(f); + | + error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:21:9 diff --git a/src/test/ui/suggestions/boxed-variant-field.rs b/src/test/ui/suggestions/boxed-variant-field.rs index 9b9e70a675fb1..e79be2f6127c1 100644 --- a/src/test/ui/suggestions/boxed-variant-field.rs +++ b/src/test/ui/suggestions/boxed-variant-field.rs @@ -8,7 +8,7 @@ fn foo(x: Ty) -> Ty { Ty::Unit => Ty::Unit, Ty::List(elem) => foo(elem), //~^ ERROR mismatched types - //~| HELP try dereferencing the `Box` + //~| HELP consider unboxing the value //~| HELP try wrapping } } diff --git a/src/test/ui/suggestions/boxed-variant-field.stderr b/src/test/ui/suggestions/boxed-variant-field.stderr index e865b993a4c17..4df8e7b20b791 100644 --- a/src/test/ui/suggestions/boxed-variant-field.stderr +++ b/src/test/ui/suggestions/boxed-variant-field.stderr @@ -6,14 +6,14 @@ LL | Ty::List(elem) => foo(elem), | = note: expected enum `Ty` found struct `Box` -help: try dereferencing the `Box` - | -LL | Ty::List(elem) => foo(*elem), - | + help: try wrapping the expression in `Ty::List` | LL | Ty::List(elem) => foo(Ty::List(elem)), | +++++++++ + +help: consider unboxing the value + | +LL | Ty::List(elem) => foo(*elem), + | + error: aborting due to previous error diff --git a/src/test/ui/terr-sorts.stderr b/src/test/ui/terr-sorts.stderr index 65d90678040d5..34d4d9eaded8a 100644 --- a/src/test/ui/terr-sorts.stderr +++ b/src/test/ui/terr-sorts.stderr @@ -6,7 +6,7 @@ LL | want_foo(b); | = note: expected struct `Foo` found struct `Box` -help: try dereferencing the `Box` +help: consider unboxing the value | LL | want_foo(*b); | + From d32277d78edfec2c8293fb887aff1783d4595653 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 12 Jan 2022 00:25:42 -0500 Subject: [PATCH 55/72] Don't lint `deref_addrof` when the two operations occur in different expansions --- clippy_lints/src/reference.rs | 1 + tests/ui/deref_addrof.fixed | 2 ++ tests/ui/deref_addrof.rs | 2 ++ tests/ui/deref_addrof.stderr | 4 ++-- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 22ae7a291d00e..b24483723700c 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -50,6 +50,7 @@ impl EarlyLintPass for DerefAddrOf { if_chain! { if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind; if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind; + if deref_target.span.ctxt() == e.span.ctxt(); if !addrof_target.span.from_expansion(); then { let mut applicability = Applicability::MachineApplicable; diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 0029fc673f110..2f489deb1ee1f 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -37,6 +37,8 @@ fn main() { let b = &a; let b = *aref; + + let _ = unsafe { *core::ptr::addr_of!(a) }; } #[rustfmt::skip] diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index f2f02dd5e723d..49f360b9a7f9e 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -37,6 +37,8 @@ fn main() { let b = *&&a; let b = **&aref; + + let _ = unsafe { *core::ptr::addr_of!(a) }; } #[rustfmt::skip] diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 5bc1cbfa21510..75371fcdb9677 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -49,7 +49,7 @@ LL | let b = **&aref; | ^^^^^^ help: try this: `aref` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:45:9 + --> $DIR/deref_addrof.rs:47:9 | LL | *& $visitor | ^^^^^^^^^^^ help: try this: `$visitor` @@ -60,7 +60,7 @@ LL | m!(self) = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:52:9 + --> $DIR/deref_addrof.rs:54:9 | LL | *& mut $visitor | ^^^^^^^^^^^^^^^ help: try this: `$visitor` From 956db072a877284e8d057dae717cf8253505a4ff Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 8 Dec 2021 17:44:50 +0000 Subject: [PATCH 56/72] Don't fall back to crate-level opaque type definitions. That would just hide bugs, as it works accidentally if the opaque type is defined at the crate level. --- compiler/rustc_infer/src/infer/mod.rs | 18 +++---- .../rustc_infer/src/infer/opaque_types.rs | 50 ++++++++++--------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 04e04e297cdd2..d3664e53447d0 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -10,7 +10,6 @@ pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine}; -use hir::def_id::CRATE_DEF_ID; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::Rollback; @@ -291,7 +290,12 @@ pub struct InferCtxt<'a, 'tcx> { /// The `DefId` of the item in whose context we are performing inference or typeck. /// It is used to check whether an opaque type use is a defining use. - pub defining_use_anchor: LocalDefId, + /// + /// If it is `None`, we can't resolve opaque types here and need to bubble up + /// the obligation. This frequently happens for + /// short lived InferCtxt within queries. The opaque type obligations are forwarded + /// to the outside until the end up in an `InferCtxt` for typeck or borrowck. + pub defining_use_anchor: Option, /// During type-checking/inference of a body, `in_progress_typeck_results` /// contains a reference to the typeck results being built up, which are @@ -547,7 +551,7 @@ impl<'tcx> fmt::Display for FixupError<'tcx> { pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, fresh_typeck_results: Option>>, - defining_use_anchor: LocalDefId, + defining_use_anchor: Option, } pub trait TyCtxtInferExt<'tcx> { @@ -556,11 +560,7 @@ pub trait TyCtxtInferExt<'tcx> { impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> { - InferCtxtBuilder { - tcx: self, - defining_use_anchor: CRATE_DEF_ID, - fresh_typeck_results: None, - } + InferCtxtBuilder { tcx: self, defining_use_anchor: None, fresh_typeck_results: None } } } @@ -580,7 +580,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// (via `with_fresh_in_progress_typeck_results`) and for the inference context used /// in mir borrowck. pub fn with_opaque_type_inference(mut self, defining_use_anchor: LocalDefId) -> Self { - self.defining_use_anchor = defining_use_anchor; + self.defining_use_anchor = Some(defining_use_anchor); self } diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index c2ef0b41e27bf..11153fbe7896b 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -328,6 +328,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }, }); } + + fn opaque_type_origin(&self, def_id: LocalDefId) -> Option { + let tcx = self.tcx; + let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let parent_def_id = self.defining_use_anchor?; + let item_kind = &tcx.hir().expect_item(def_id).kind; + let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { + span_bug!( + tcx.def_span(def_id), + "weird opaque type: {:#?}", + item_kind + ) + }; + let in_definition_scope = match *origin { + // Async `impl Trait` + hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id, + // Anonymous `impl Trait` + hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, + // Named `type Foo = impl Bar;` + hir::OpaqueTyOrigin::TyAlias => { + may_define_opaque_type(tcx, parent_def_id, opaque_hir_id) + } + }; + in_definition_scope.then_some(*origin) + } } // Visitor that requires that (almost) all regions in the type visited outlive @@ -459,31 +484,10 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { // } // ``` if let Some(def_id) = def_id.as_local() { - let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let parent_def_id = self.infcx.defining_use_anchor; - let item_kind = &tcx.hir().expect_item(def_id).kind; - let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { - span_bug!( - self.value_span, - "weird opaque type: {:#?}, {:#?}", - ty.kind(), - item_kind - ) - }; - let in_definition_scope = match *origin { - // Async `impl Trait` - hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id, - // Anonymous `impl Trait` - hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, - // Named `type Foo = impl Bar;` - hir::OpaqueTyOrigin::TyAlias => { - may_define_opaque_type(tcx, parent_def_id, opaque_hir_id) - } - }; - if in_definition_scope { + if let Some(origin) = self.infcx.opaque_type_origin(def_id) { let opaque_type_key = OpaqueTypeKey { def_id: def_id.to_def_id(), substs }; - return self.fold_opaque_ty(ty, opaque_type_key, *origin); + return self.fold_opaque_ty(ty, opaque_type_key, origin); } debug!( From 166737f3cbbe2157454c759cb56662c941c6031c Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Sun, 2 Jan 2022 23:34:10 +0100 Subject: [PATCH 57/72] Add manual_bits lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_style.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/manual_bits.rs | 107 +++++++++++++++++ clippy_utils/src/msrvs.rs | 2 +- tests/ui/manual_bits.fixed | 48 ++++++++ tests/ui/manual_bits.rs | 48 ++++++++ tests/ui/manual_bits.stderr | 154 +++++++++++++++++++++++++ 10 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/manual_bits.rs create mode 100644 tests/ui/manual_bits.fixed create mode 100644 tests/ui/manual_bits.rs create mode 100644 tests/ui/manual_bits.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index e521995a18864..8f4da9a382792 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3071,6 +3071,7 @@ Released 2018-09-13 [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn +[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 0cab28cfafacd..26fb4259952b6 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -115,6 +115,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(loops::WHILE_LET_ON_ITERATOR), LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(manual_bits::MANUAL_BITS), LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_strip::MANUAL_STRIP), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 09e4f6ae27504..746bdb19c3d92 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -226,6 +226,7 @@ store.register_lints(&[ main_recursion::MAIN_RECURSION, manual_assert::MANUAL_ASSERT, manual_async_fn::MANUAL_ASYNC_FN, + manual_bits::MANUAL_BITS, manual_map::MANUAL_MAP, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_ok_or::MANUAL_OK_OR, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 57de34f41ace7..05211476ff230 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -43,6 +43,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(loops::WHILE_LET_ON_ITERATOR), LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(manual_bits::MANUAL_BITS), LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(map_clone::MAP_CLONE), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 80d979362f8a4..79e9882fef4c4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -261,6 +261,7 @@ mod macro_use; mod main_recursion; mod manual_assert; mod manual_async_fn; +mod manual_bits; mod manual_map; mod manual_non_exhaustive; mod manual_ok_or; @@ -858,6 +859,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields)); store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv))); + store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs new file mode 100644 index 0000000000000..50bf2527e39a8 --- /dev/null +++ b/clippy_lints/src/manual_bits.rs @@ -0,0 +1,107 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::{match_def_path, meets_msrv, msrvs, paths}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for uses of `std::mem::size_of::() * 8` when + /// `T::BITS` is available. + /// + /// ### Why is this bad? + /// Can be written as the shorter `T::BITS`. + /// + /// ### Example + /// ```rust + /// std::mem::size_of::() * 8; + /// ``` + /// Use instead: + /// ```rust + /// usize::BITS; + /// ``` + #[clippy::version = "1.60.0"] + pub MANUAL_BITS, + style, + "manual implementation of `size_of::() * 8` can be simplified with `T::BITS`" +} + +#[derive(Clone)] +pub struct ManualBits { + msrv: Option, +} + +impl ManualBits { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualBits => [MANUAL_BITS]); + +impl<'tcx> LateLintPass<'tcx> for ManualBits { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::MANUAL_BITS) { + return; + } + + if_chain! { + if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind; + if let BinOpKind::Mul = &bin_op.node; + if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr); + if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); + if let ExprKind::Lit(lit) = &other_expr.kind; + if let LitKind::Int(8, _) = lit.node; + + then { + span_lint_and_sugg( + cx, + MANUAL_BITS, + expr.span, + "usage of `mem::size_of::()` to obtain the size of `T` in bits", + "consider using", + format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn get_one_size_of_ty<'tcx>( + cx: &LateContext<'tcx>, + expr1: &'tcx Expr<'_>, + expr2: &'tcx Expr<'_>, +) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>, &'tcx Expr<'tcx>)> { + match (get_size_of_ty(cx, expr1), get_size_of_ty(cx, expr2)) { + (Some((real_ty, resolved_ty)), None) => Some((real_ty, resolved_ty, expr2)), + (None, Some((real_ty, resolved_ty))) => Some((real_ty, resolved_ty, expr1)), + _ => None, + } +} + +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> { + if_chain! { + if let ExprKind::Call(count_func, _func_args) = expr.kind; + if let ExprKind::Path(ref count_func_qpath) = count_func.kind; + + if let QPath::Resolved(_, count_func_path) = count_func_qpath; + if let Some(segment_zero) = count_func_path.segments.get(0); + if let Some(args) = segment_zero.args; + if let Some(GenericArg::Type(real_ty)) = args.args.get(0); + + if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::MEM_SIZE_OF); + then { + cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (real_ty, resolved_ty)) + } else { + None + } + } +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index af677d8b00f7b..a5b409ad96bbb 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -12,7 +12,7 @@ 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 } diff --git a/tests/ui/manual_bits.fixed b/tests/ui/manual_bits.fixed new file mode 100644 index 0000000000000..4f1b19b75b8a1 --- /dev/null +++ b/tests/ui/manual_bits.fixed @@ -0,0 +1,48 @@ +// run-rustfix + +#![warn(clippy::manual_bits)] +#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] + +use std::mem::{size_of, size_of_val}; + +fn main() { + i8::BITS; + i16::BITS; + i32::BITS; + i64::BITS; + i128::BITS; + isize::BITS; + + u8::BITS; + u16::BITS; + u32::BITS; + u64::BITS; + u128::BITS; + usize::BITS; + + i8::BITS; + i16::BITS; + i32::BITS; + i64::BITS; + i128::BITS; + isize::BITS; + + u8::BITS; + u16::BITS; + u32::BITS; + u64::BITS; + u128::BITS; + usize::BITS; + + size_of::() * 4; + 4 * size_of::(); + size_of::() * 8; + 8 * size_of::(); + + size_of_val(&0u32) * 8; + + type Word = u32; + Word::BITS; + type Bool = bool; + size_of::() * 8; +} diff --git a/tests/ui/manual_bits.rs b/tests/ui/manual_bits.rs new file mode 100644 index 0000000000000..f8a01313e6ad0 --- /dev/null +++ b/tests/ui/manual_bits.rs @@ -0,0 +1,48 @@ +// run-rustfix + +#![warn(clippy::manual_bits)] +#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] + +use std::mem::{size_of, size_of_val}; + +fn main() { + size_of::() * 8; + size_of::() * 8; + size_of::() * 8; + size_of::() * 8; + size_of::() * 8; + size_of::() * 8; + + size_of::() * 8; + size_of::() * 8; + size_of::() * 8; + size_of::() * 8; + size_of::() * 8; + size_of::() * 8; + + 8 * size_of::(); + 8 * size_of::(); + 8 * size_of::(); + 8 * size_of::(); + 8 * size_of::(); + 8 * size_of::(); + + 8 * size_of::(); + 8 * size_of::(); + 8 * size_of::(); + 8 * size_of::(); + 8 * size_of::(); + 8 * size_of::(); + + size_of::() * 4; + 4 * size_of::(); + size_of::() * 8; + 8 * size_of::(); + + size_of_val(&0u32) * 8; + + type Word = u32; + size_of::() * 8; + type Bool = bool; + size_of::() * 8; +} diff --git a/tests/ui/manual_bits.stderr b/tests/ui/manual_bits.stderr new file mode 100644 index 0000000000000..c4f5af2dcb0ec --- /dev/null +++ b/tests/ui/manual_bits.stderr @@ -0,0 +1,154 @@ +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:9:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` + | + = note: `-D clippy::manual-bits` implied by `-D warnings` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:10:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:11:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:12:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:13:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:14:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:16:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:17:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:18:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:19:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:20:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:21:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:23:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:24:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:25:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:26:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:27:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:28:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:30:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:31:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:32:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:33:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:34:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:35:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:45:5 + | +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS` + +error: aborting due to 25 previous errors + From 91581f6d5e0cc442397067363052f1e60f0d0941 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 9 Dec 2021 19:11:40 +0000 Subject: [PATCH 58/72] Resolve primitive impls in clippy_utils::path_to_res --- clippy_utils/src/lib.rs | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9f2018385f380..44f634b651943 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -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, + def, lang_items, 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, Target, + TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -525,18 +525,34 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res { .iter() .find(|item| item.ident.name.as_str() == name) } + fn find_primitive(tcx: TyCtxt<'_>, name: &str) -> Option { + 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 { + 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() From 04eb27aeaf283415011d7786fa4091f0ceaf8a11 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 9 Dec 2021 20:42:44 +0000 Subject: [PATCH 59/72] Use method name from conf::DisallowedMethod Since def_path_str returns e.g. "core::f32::::clamp" for "f32::clamp" --- clippy_lints/src/disallowed_methods.rs | 29 ++++++++----------- clippy_lints/src/utils/conf.rs | 8 +++++ .../toml_disallowed_methods/clippy.toml | 2 ++ .../conf_disallowed_methods.rs | 7 ++++- .../conf_disallowed_methods.stderr | 14 ++++++++- 5 files changed, 41 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 6d5b1efe270c4..73c00d97020be 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -59,7 +59,7 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedMethods { conf_disallowed: Vec, - disallowed: DefIdMap>, + disallowed: DefIdMap, } impl DisallowedMethods { @@ -75,17 +75,10 @@ impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_crate(&mut self, cx: &LateContext<'_>) { - for conf in &self.conf_disallowed { - let (path, reason) = match conf { - conf::DisallowedMethod::Simple(path) => (path, None), - conf::DisallowedMethod::WithReason { path, reason } => ( - path, - reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)), - ), - }; - let segs: Vec<_> = path.split("::").collect(); + for (index, conf) in self.conf_disallowed.iter().enumerate() { + let segs: Vec<_> = conf.path().split("::").collect(); if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) { - self.disallowed.insert(id, reason); + self.disallowed.insert(id, index); } } } @@ -95,15 +88,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { Some(def_id) => def_id, None => return, }; - let reason = match self.disallowed.get(&def_id) { - Some(reason) => reason, + let conf = match self.disallowed.get(&def_id) { + Some(&index) => &self.conf_disallowed[index], None => return, }; - let func_path = cx.tcx.def_path_str(def_id); - let msg = format!("use of a disallowed method `{}`", func_path); + let msg = format!("use of a disallowed method `{}`", conf.path()); span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { - if let Some(reason) = reason { - diag.note(reason); + if let conf::DisallowedMethod::WithReason { + reason: Some(reason), .. + } = conf + { + diag.note(&format!("{} (from clippy.toml)", reason)); } }); } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 4e1c538c1c20a..d6deb50cc9073 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -23,6 +23,14 @@ pub enum DisallowedMethod { WithReason { path: String, reason: Option }, } +impl DisallowedMethod { + pub fn path(&self) -> &str { + let (Self::Simple(path) | Self::WithReason { path, .. }) = self; + + path + } +} + /// A single disallowed type, used by the `DISALLOWED_TYPES` lint. #[derive(Clone, Debug, Deserialize)] #[serde(untagged)] diff --git a/tests/ui-toml/toml_disallowed_methods/clippy.toml b/tests/ui-toml/toml_disallowed_methods/clippy.toml index f1d4a4619c5dc..c902d21123dce 100644 --- a/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -1,6 +1,8 @@ disallowed-methods = [ # just a string is shorthand for path only "std::iter::Iterator::sum", + "f32::clamp", + "slice::sort_unstable", # can give path and reason with an inline table { path = "regex::Regex::is_match", reason = "no matching allowed" }, # can use an inline table but omit reason diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index cb449b45bde85..338b3b5b28f42 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -7,6 +7,11 @@ fn main() { let re = Regex::new(r"ab.*c").unwrap(); re.is_match("abc"); - let a = vec![1, 2, 3, 4]; + let mut a = vec![1, 2, 3, 4]; a.iter().sum::(); + + a.sort_unstable(); + + let _ = 2.0f32.clamp(3.0f32, 4.0f32); + let _ = 2.0f64.clamp(3.0f64, 4.0f64); } diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 999ead10d5182..5533676aea287 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -20,5 +20,17 @@ error: use of a disallowed method `std::iter::Iterator::sum` LL | a.iter().sum::(); | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: use of a disallowed method `slice::sort_unstable` + --> $DIR/conf_disallowed_methods.rs:13:5 + | +LL | a.sort_unstable(); + | ^^^^^^^^^^^^^^^^^ + +error: use of a disallowed method `f32::clamp` + --> $DIR/conf_disallowed_methods.rs:15:13 + | +LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors From 90bf72c2cd358eb0770164412d2428cfa0d708e1 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 12 Jan 2022 10:46:05 -0600 Subject: [PATCH 60/72] Only run dogfood on linux in CI --- .github/workflows/clippy_bors.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index fe02d9cfc8499..989667037c1cb 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -115,8 +115,13 @@ jobs: run: cargo build --features deny-warnings,internal - name: Test + if: runner.os == 'Linux' run: cargo test --features deny-warnings,internal + - name: Test + if: runner.os != 'Linux' + run: cargo test --features deny-warnings,internal -- --skip dogfood + - name: Test clippy_lints run: cargo test --features deny-warnings,internal working-directory: clippy_lints From 062db10c12d5a1f75dbda667dd955095cfe16057 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 12 Jan 2022 12:33:47 -0500 Subject: [PATCH 61/72] Add `manual_memcpy_test` for `VecDeque` --- tests/ui/manual_memcpy/without_loop_counters.rs | 11 +++++++++++ tests/ui/manual_memcpy/without_loop_counters.stderr | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/ui/manual_memcpy/without_loop_counters.rs b/tests/ui/manual_memcpy/without_loop_counters.rs index 0083f94798fe4..ea0535d076b81 100644 --- a/tests/ui/manual_memcpy/without_loop_counters.rs +++ b/tests/ui/manual_memcpy/without_loop_counters.rs @@ -113,6 +113,17 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { for i in 0.. { dst[i] = src[i]; } + + // VecDeque - ideally this would work, but would require something like `range_as_slices` + let mut dst = std::collections::VecDeque::from_iter([0; 5]); + let src = std::collections::VecDeque::from_iter([0, 1, 2, 3, 4]); + for i in 0..dst.len() { + dst[i] = src[i]; + } + let src = vec![0, 1, 2, 3, 4]; + for i in 0..dst.len() { + dst[i] = src[i]; + } } #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] diff --git a/tests/ui/manual_memcpy/without_loop_counters.stderr b/tests/ui/manual_memcpy/without_loop_counters.stderr index 654dee32a1e84..c163ae061dfc9 100644 --- a/tests/ui/manual_memcpy/without_loop_counters.stderr +++ b/tests/ui/manual_memcpy/without_loop_counters.stderr @@ -104,7 +104,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..0].copy_from_slice(&src[..0]);` error: it looks like you're manually copying between slices - --> $DIR/without_loop_counters.rs:120:5 + --> $DIR/without_loop_counters.rs:131:5 | LL | / for i in 0..src.len() { LL | | dst[i] = src[i].clone(); From 8568f44fe172b0e7b7d9ce5a66d6e69ad9ffc669 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 12 Jan 2022 12:00:36 -0800 Subject: [PATCH 62/72] suggest deref/unboxing before wrapping variant --- compiler/rustc_typeck/src/check/demand.rs | 2 +- src/test/ui/suggestions/boxed-variant-field.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 62179821d233d..c351a9f70404a 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -31,8 +31,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: TypeError<'tcx>, ) { self.annotate_expected_due_to_let_ty(err, expr, error); - self.suggest_compatible_variants(err, expr, expected, expr_ty); self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr); + self.suggest_compatible_variants(err, expr, expected, expr_ty); if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { return; } diff --git a/src/test/ui/suggestions/boxed-variant-field.stderr b/src/test/ui/suggestions/boxed-variant-field.stderr index 4df8e7b20b791..9a31dc89197e3 100644 --- a/src/test/ui/suggestions/boxed-variant-field.stderr +++ b/src/test/ui/suggestions/boxed-variant-field.stderr @@ -6,14 +6,14 @@ LL | Ty::List(elem) => foo(elem), | = note: expected enum `Ty` found struct `Box` -help: try wrapping the expression in `Ty::List` - | -LL | Ty::List(elem) => foo(Ty::List(elem)), - | +++++++++ + help: consider unboxing the value | LL | Ty::List(elem) => foo(*elem), | + +help: try wrapping the expression in `Ty::List` + | +LL | Ty::List(elem) => foo(Ty::List(elem)), + | +++++++++ + error: aborting due to previous error From 7b74ded9a08a84bd64cf972a070b3a35f661d1ed Mon Sep 17 00:00:00 2001 From: hafeoz <95505675+hafeoz@users.noreply.github.com> Date: Thu, 13 Jan 2022 03:57:44 +0000 Subject: [PATCH 63/72] Fix lints documents --- compiler/rustc_lint/src/builtin.rs | 1 + compiler/rustc_lint_defs/src/builtin.rs | 42 ++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index c0384875a47c9..b613dfafbd607 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -3147,6 +3147,7 @@ declare_lint! { /// ### Example /// /// ```rust,compile_fail + /// # #![feature(asm)] /// use std::arch::asm; /// /// fn main() { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 27a06943cbc25..c2f6118227a4a 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1805,7 +1805,7 @@ declare_lint! { /// /// ### Example /// - /// ``` + /// ```rust /// if let _ = 123 { /// println!("always runs!"); /// } @@ -2431,7 +2431,19 @@ declare_lint! { /// } /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// warning: formatting may not be suitable for sub-register argument + /// --> src/main.rs:7:19 + /// | + /// 7 | asm!("mov {0}, {0}", in(reg) 0i16); + /// | ^^^ ^^^ ---- for this argument + /// | + /// = note: `#[warn(asm_sub_register)]` on by default + /// = help: use the `x` modifier to have the register formatted as `ax` + /// = help: or use the `r` modifier to keep the default formatting of `rax` + /// ``` /// /// ### Explanation /// @@ -2470,7 +2482,17 @@ declare_lint! { /// } /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead + /// --> src/main.rs:8:14 + /// | + /// 8 | ".att_syntax", + /// | ^^^^^^^^^^^ + /// | + /// = note: `#[warn(bad_asm_style)]` on by default + /// ``` /// /// ### Explanation /// @@ -2788,7 +2810,7 @@ declare_lint! { /// /// ### Example /// - /// ```compile_fail + /// ```rust,compile_fail /// #![feature(staged_api)] /// /// #[derive(Clone)] @@ -3618,7 +3640,17 @@ declare_lint! { /// fn foo() {} /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// warning: duplicated attribute + /// --> src/lib.rs:2:1 + /// | + /// 2 | #[test] + /// | ^^^^^^^ + /// | + /// = note: `#[warn(duplicate_macro_attributes)]` on by default + /// ``` /// /// ### Explanation /// From 8a2141bae408050e3178c802c8e9557d314000d5 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 13 Jan 2022 12:48:08 +0100 Subject: [PATCH 64/72] Bump Clippy Version -> 0.1.60 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cb49dd84c39a9..e445889a58f77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.59" +version = "0.1.60" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 49171e21d10e7..2053ca64ba23d 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.59" +version = "0.1.60" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 565dd1faa3d7f..afff6491aba64 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.59" +version = "0.1.60" edition = "2021" publish = false From 6ad05bcbbe821c3643e2cecf659fe091818fb7e8 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 13 Jan 2022 12:48:17 +0100 Subject: [PATCH 65/72] Bump nightly version -> 2022-01-13 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 471ae40f1ac7a..e6a58e9207250 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-12-30" +channel = "nightly-2022-01-13" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 159d6c356e3f00d294999fd3475bf844e0f81a6e Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 13 Jan 2022 13:18:51 +0100 Subject: [PATCH 66/72] Update Cargo.lock --- Cargo.lock | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ef9f91fdb434b..aa3d704108fb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -613,7 +613,7 @@ dependencies = [ [[package]] name = "clippy" -version = "0.1.59" +version = "0.1.60" dependencies = [ "cargo_metadata 0.14.0", "clippy_lints", @@ -621,6 +621,7 @@ dependencies = [ "compiletest_rs", "derive-new", "filetime", + "futures 0.3.12", "if_chain", "itertools 0.10.1", "parking_lot", @@ -633,6 +634,7 @@ dependencies = [ "syn", "tempfile", "tester", + "tokio", ] [[package]] @@ -652,7 +654,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.59" +version = "0.1.60" dependencies = [ "cargo_metadata 0.14.0", "clippy_utils", @@ -673,8 +675,9 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.59" +version = "0.1.60" dependencies = [ + "arrayvec", "if_chain", "rustc-semver", ] From b83c77cb7cc7f616df80cc7bd3e2a5f1eb9c0a9f Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 13 Jan 2022 13:37:24 +0100 Subject: [PATCH 67/72] Fix Clippy sync fallout --- src/tools/clippy/clippy_utils/src/macros.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index a3e336d701cab..b7a242cf90a43 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -191,13 +191,13 @@ impl<'a> PanicExpn<'a> { 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 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 }; + let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None }; Self::Display(e) }, "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?), From bc6b199588cd31df1b8d8d0e3e2b91bd196d2dca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20BRANSTETT?= Date: Thu, 13 Jan 2022 14:44:30 +0100 Subject: [PATCH 68/72] Use the updated Rust logo and change it's format to SVG --- src/librustdoc/html/render/write_shared.rs | 2 +- .../html/static/images/rust-logo.png | Bin 5758 -> 0 bytes .../html/static/images/rust-logo.svg | 61 ++++++++++++++++++ src/librustdoc/html/static_files.rs | 5 +- src/librustdoc/templates/page.html | 4 +- 5 files changed, 67 insertions(+), 5 deletions(-) delete mode 100644 src/librustdoc/html/static/images/rust-logo.png create mode 100644 src/librustdoc/html/static/images/rust-logo.svg diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 2e763dbd8fe9e..d5e5af7bbf8dd 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -240,7 +240,7 @@ pub(super) fn write_shared( } if (*cx.shared).layout.logo.is_empty() { - write_toolchain("rust-logo.png", static_files::RUST_LOGO)?; + write_toolchain("rust-logo.svg", static_files::RUST_LOGO_SVG)?; } if (*cx.shared).layout.favicon.is_empty() { write_toolchain("favicon.svg", static_files::RUST_FAVICON_SVG)?; diff --git a/src/librustdoc/html/static/images/rust-logo.png b/src/librustdoc/html/static/images/rust-logo.png deleted file mode 100644 index 74b4bd695045ebc52c21af95301adc9311ca881c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5758 zcmV-^7J=!BP)rlOar#xVHcKR}urj-85WU}*d52ce!FO;4x1(Dd{rLvS^kzaz+Qg+43&c`E^ z2A4h%FgKT)L-C6=0SiO%&803L%dQ&ad>le)iUzvAtx3hUp3qiqE4OmtpW{`0v8nBU z+vNFwgO#K7`BHl6Lk5HT&J zd0%5WX?GeMO2>h@YkR3UyYLB!%U`9zGx2pv`bl*gj{l2(jn1Mg!nL1jDhv@mJF-Z) z67J}KBA8uxsR*X=Sf`PpG*`ff8oxRCuGi#KZ;di+0mQR!BHISEExW1CGQBlHFEkNU z(hzW6fL~TqN}IfE{LRuTaMO61)5uVoEy2s0>}~aTd9vJ#3c3NFB;T~tqib}0T!03k zRgt=rZq*e6NSNg`GPKSp8t`0%sVfTb6iOXlX|k6#hX|N!eoNB^SLi}PkbuM0isxjy z)5uUb6|B7};Xr|h82+jxduUULfQwjMPa@4>-q38f5EGn6hWupPwaF{BhvVvGa)^K@ zX|>ns>X7FY7!n%W4tV3VU&m`b2j6q4zfYf_ujx$xnS4s6M$Tce%Fe$?pCG$c!>Hop z#?j*z!PbLgoZbtiBT7$|)|75(J=<@U1f0m=D|O^qtPFkyy`D~>hq97Bm~?7zZ;%T} z8~GfW&%P^pLJ%^7Od!{jl`1wwhao}S-B~I$U^|6yiKp9x6@U^ z9zfH$!pSPMb+)nuLoTjXL7Lv!-A_+SN{tY*DG~mHtPe-SxM*{NK7?`^%<$xScZYA$WuF!hmCsQ!r$to142By6i zR=<@Ur#d=6p?*&(k|W8E5R4!7oD1TUpQKR0^3WRd4*U)$s}T6UY~iUid|pbuC%>g% ztS(Jv{65eSeo$>r$QQ{oi4?F`D;Px6XYictg4f9s*3A!1gwGnUOKM4zj3oDxHRQj^ z)Y|ewE+M-TDG+UftD4PDC(A&81g*Sp;M-cjZ~8nL?KCKmQDj*X1=ym1{JWE7Aak*; z{PEuWO-_RYxhatXum${RZk_%8)>(zw`RU#nOvckw=~mI%iMqS$*5^2V5b!xEwgCDJ zTbMI7p7Q)p_h$REg5zp|-_AR`mgVQ~06z}?UGfE|j{^Au`M($iEN4Y^r1A<~=1JAJ z`wxo2C|lL}v1#}W{7t0I>BE4FcoWu6k?{)(aAXKz50K7!5&Fq3(ZaKFm0!|oM+J=BV z@cK=A9~X7g-jtW|b@($CC^*T$$Dp^o)=L6%<&j$_46L;SbQL22`rOa~tt7n-U&#{u zrBn*$=(BWw3$nw8%B@${-o)(ZZ)1sp`u9C_BX{9LQYiRE=sd2qs>8n3-LH1%w~21I ztbb@XnQ3RO$HycRaDXy~M)FV%-k}Fq4cy1xI69TwNMFU^`75a#dJJF$s%t(C zO=(0;O{Cyx>kMPSd0sp5jidvQBe+tnpA)`sp6&7sP>T5-_n9h5ZNQ&MAoRC?1S zUIq3Ye!N21VA6<+UJFO+tZsLB1pbsr4x}#URq3-=OBt2YCcG|Y1SkUtWJINUb&rBG zpsSU%v+c(8TM+_Qgy=F0F4Xz#D41Qz{dpYv0KRGe6%Fv&6Z|O1X#LGZEP9`_bMKC0 zad85`o{6gtEsuJnaxd_p{OkP0ygl`}j?L-42?UVpls-yIX3%3=@E-o8MZhDLc5KI8 z>^oFjUZCQtme-31_B8W<4!ej~04VQ0qrMRgVt(O4+&6VTLd&ndbOe#GeesUXSm8$RWFTOJ5A{O=@B~*BwBR%g{)Lt!v@)3hHyZ** zxF1zXz(ewi&saJbnid~%HyCdu2KkX0F5w6_Km}OFe~knMt1Ge|(d^I-Q!dR?A1qMBEgLCH$8m~@I` zW8)`UHbBK@M1VUKy^N}Z|AM||e|L50m-VcHzQJ9N?H;VQ0wR`I0p$`?MAw@W^XbE% z0CkUaK#dWsPTH#&jo!~a1+7J8A^J)Luecm((N=Q*l;AIhrc8VpOZodcg6!$NZw7@Vt9Sv>Q1PKh1suQvr~p@K?GQ;-*;lb75_c3#v~b4b$4kE6`N;8FRN# z#pMG&P^?dpfyv{l%ee{k!B&RhKZZ|Bx#7Y_K@ZVzS4AuGk4)P>V(<+C{3?!(=VV#> z^uj~n1wk!JR1DGnF6JDOSb% zGeWo~$o;(HkpumGK(A{V1Y=-nC# zSe8n_Tgc9lEOVr)03X!-J|2+lI6Q>7w*=6QM|gK-0;(?k(<-X1i|(kAg4UFjUS_|k z?lAwRDe!=RMd_F9es>|>NJ&p!kUK0UxQ&L&22?!0(=Y)2nw9XJ2 zM3(Y;LRC{?#sj*@fXW1*j0j+vzpVxWev_EO0cevocA$EKQUfS_dpb6;PCiAKvAQqY z%4mO@6dFdrK(wt|0@#`RC#29Se>Htj^~t>*g=!f!QmtS819hLdap+ACyvAd zmfrO>=s=Kg{WUT8ngY+(i}l!5ouN^*i&#^iK6^z*)aEsifWNPSfN#gH_R%g0SicNt zFMQmEM*;!M%p^D#<0cdu57Q}ru0n=?T6K1(R7Jo5^mYvtTok%?Jlc>pf|x5)MSy7= zV%tcYOghMrXj52VD*#qvXfMO7JbIUa84&_T z*PWsilgpg&Z*n|lg*z7&DvhK49A;0Ef!=${pG?33Xk*=JMh4_^(g#2boSwqpeZ56C zg^UP2kbrxWf*G-v;IX>XjO5Q1hCSEw5}D5qZClr(M+J!O4fGE9WCE_PJ54Be3BCpT zflTPS;?Rl&ankMu_ohDFz&-3yJ9OkjQu}N!0Cwh-k z{>TgmtNVpHyYBan4ValaIP!;j({2$(+F}ZF2VPAcB@-(>E#}f=Xa}7~J6*9u3+hc5 zsK;>v^dEKAeqM+W(~0J}9quu55TxfIwmjO7VZlrs{&F+{{S*4<4CGw&FNjtg9~jNf zIlrfLSZ+>5FUHCro*U)MDG#pwxxoQso6rGX8KMNd&8DXMSzttMm!+>?SapE5?ZbnU z-c_vTt5M?kVwXMrivlzfeW=x6zFJ_dE9AGZ>~`--0@{wQMdzV0XlN*Q7SV4!IaESt zq50^yT>Z4%=JZNHL#)BFH_)%y`0p7gVDUrI3RC(yD1B)D?786cYTGe#J-aMEudb4m zw?RA5M~POrS9TRa|CVs4577(gH4DD+{&;~w!GWx+r2=%Y?xPtT=ly7DPK*xZDlcQ} zg@86xX!bzVcj(EOHG=T{%<3PYe$dPjW65No9OJYM*vG$eG7l&~-4zMY-7*KN=O61`#I`)1j!(y@ljGTzM0}?fVGLU$?Ln?9Cp(w9{d0?T7RNXl z2W!ggYCU#%Ym)Kvp$oJXpNfumPe-TY>39~oh#p(_R$4i!S}3&YV#F!osZFVjGo6eB z`i%tpXejpgN(1C4H-7w+Z>5!GAoF=`%0YVoA}U1iW+a>U$?V+eWExm$Z;|&sJ`_LI zHh#!j;e@l>PEio>Dyv13f$;E4ijXX~ z)1$sxGlVn+IcQ_M3rB`Bv`(txE_9%iVL;!|2KK@b;a7|Mf#0X5^HlrgJ6Q$xjGdYx zlzr^H@e-ap>;Rq3n{uW}I?l;3pxboH8__2<{6cPrr~+y}6ADQ!^v%vG_vV|)Q`JQ( za8DalZWWmtjG?EX`=vXg`U>A;Q&1TM2J@QSPK(-3G$A*XNk{H2xSwZ?vptlbofYsq zJ^i#DRxK;6D>uuL!b1`8*^M4#Q=P;28TznX2U~#AZQ6O=>}^Ne0lW*3#`Et`9Okk4 z?6%d-uk`gFA9$<0JLwfzj1{n0MTqeWY*A}@eu<`|QML8%*%iOcZPE7Kq-RKwmC=KC z|Lx!ke5bq0+BV@uC#w(~o?q&(rZ9wr8d>J~8En^y% z2znU%-$b*zNVnsnRnr>n~(ysag2rk-8fK73;m5*i>~l;9b}t zQ}yxS#iX^df3QGjgtk=z2bAl9Bq3+RPpi$90mEnHh=R=zKNKnEvBos^FE+f{UU+Ar zEodh)7J-))&=7~3uG}HG<~P;c$vXo4o$glI!1YW7{J zD9;i!RSHsE(xB`27I`%rpE6d4-(xi|mf66RwXOsx{JgT!i&sSD8cr7J-NLi% zT3|NiA>VWRl^ zwH5xSEz5vNcWCqFXGBN$dQ~;_^F8y#*}t=G0_mnXOTUitSBgJnJj3a~p>T9zV$3!y zu`0$gJXjL5ksX=G7&pk+A!NCw>`M|T?$3X_n;iTp`d`V`o4;5i<3pQt9IxY>*jcj8 z@c#DxcsVHqgM$ymU-NbO%^{PyNdN!jgQ56lze}k42l=i#eB47}N?|*bK2`Mp2a}$;#7RWswg3PC07*qoM6N<$g8zmuE&u=k diff --git a/src/librustdoc/html/static/images/rust-logo.svg b/src/librustdoc/html/static/images/rust-logo.svg new file mode 100644 index 0000000000000..62424d8ffd763 --- /dev/null +++ b/src/librustdoc/html/static/images/rust-logo.svg @@ -0,0 +1,61 @@ + + + diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 56c5399d074b6..cd369a93d8283 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -67,8 +67,9 @@ crate static LICENSE_APACHE: &[u8] = include_bytes!("static/LICENSE-APACHE.txt") /// The contents of `LICENSE-MIT.txt`, the text of the MIT License. crate static LICENSE_MIT: &[u8] = include_bytes!("static/LICENSE-MIT.txt"); -/// The contents of `rust-logo.png`, the default icon of the documentation. -crate static RUST_LOGO: &[u8] = include_bytes!("static/images/rust-logo.png"); +/// The contents of `rust-logo.svg`, the default icon of the documentation. +crate static RUST_LOGO_SVG: &[u8] = include_bytes!("static/images/rust-logo.svg"); + /// The default documentation favicons (SVG and PNG fallbacks) crate static RUST_FAVICON_SVG: &[u8] = include_bytes!("static/images/favicon.svg"); crate static RUST_FAVICON_PNG_16: &[u8] = include_bytes!("static/images/favicon-16x16.png"); diff --git a/src/librustdoc/templates/page.html b/src/librustdoc/templates/page.html index 673260ac6d09d..02808754b539b 100644 --- a/src/librustdoc/templates/page.html +++ b/src/librustdoc/templates/page.html @@ -79,7 +79,7 @@ {%- if !layout.logo.is_empty() %} logo {#- -#} {%- else -%} - {#- -#} + {#- -#} {%- endif -%} {#- -#} @@ -92,7 +92,7 @@ {%- if !layout.logo.is_empty() %} logo {#- -#} {%- else -%} - {#- -#} + {#- -#} {%- endif -%} {#- -#}