From c70f1e0f8f3419f67dc31b5bbbc47db1e829128c Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Fri, 1 Apr 2022 22:36:30 -0600 Subject: [PATCH 001/129] ignore `&x | &y` in unnested_or_patterns replacing it with `&(x | y)` is actually more characters --- clippy_lints/src/unnested_or_patterns.rs | 12 ++++--- tests/ui/unnested_or_patterns.fixed | 7 +++-- tests/ui/unnested_or_patterns.rs | 7 +++-- tests/ui/unnested_or_patterns.stderr | 40 ++++++++++++------------ 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 1d4fe9cfd3cf4..790ffe618d71d 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{meets_msrv, msrvs, over}; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; +use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -230,6 +230,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) // with which a pattern `C(p_0)` may be formed, // which we would want to join with other `C(p_j)`s. Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) + // Skip immutable refs, as grouping them saves few characters, + // and almost always requires adding parens (increasing noisiness). + // In the case of only two patterns, replacement adds net characters. + | Ref(_, Mutability::Not) // Dealt with elsewhere. | Or(_) | Paren(_) => false, // Transform `box x | ... | box y` into `box (x | y)`. @@ -241,10 +245,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) |k| matches!(k, Box(_)), |k| always_pat!(k, Box(p) => p), ), - // Transform `&m x | ... | &m y` into `&m (x | y)`. - Ref(target, m1) => extend_with_matching( + // Transform `&mut x | ... | &mut y` into `&mut (x | y)`. + Ref(target, Mutability::Mut) => extend_with_matching( target, start, alternatives, - |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match. + |k| matches!(k, Ref(_, Mutability::Mut)), |k| always_pat!(k, Ref(p, _) => p), ), // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`. diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index 46463a29e9b20..c223b5bc711b2 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -6,10 +6,13 @@ #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] fn main() { + // Should be ignored by this lint, as nesting requires more characters. + if let &0 | &2 = &0 {} + if let box (0 | 2) = Box::new(0) {} if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} - const C0: &u8 = &1; - if let &(0 | 2) | C0 = &0 {} + const C0: Option = Some(1); + if let Some(1 | 2) | C0 = None {} if let &mut (0 | 2) = &mut 0 {} if let x @ (0 | 2) = 0 {} if let (0, 1 | 2 | 3) = (0, 0) {} diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 8ce0738bfc27b..04cd11036e4e0 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -6,10 +6,13 @@ #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] fn main() { + // Should be ignored by this lint, as nesting requires more characters. + if let &0 | &2 = &0 {} + if let box 0 | box 2 = Box::new(0) {} if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} - const C0: &u8 = &1; - if let &0 | C0 | &2 = &0 {} + const C0: Option = Some(1); + if let Some(1) | C0 | Some(2) = None {} if let &mut 0 | &mut 2 = &mut 0 {} if let x @ 0 | x @ 2 = 0 {} if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index de424c3fdb8f2..453c66cbba8fc 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -1,5 +1,5 @@ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:9:12 + --> $DIR/unnested_or_patterns.rs:12:12 | LL | if let box 0 | box 2 = Box::new(0) {} | ^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | if let box (0 | 2) = Box::new(0) {} | ~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:10:12 + --> $DIR/unnested_or_patterns.rs:13:12 | LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,18 +22,18 @@ LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} | ~~~~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:12:12 + --> $DIR/unnested_or_patterns.rs:15:12 | -LL | if let &0 | C0 | &2 = &0 {} - | ^^^^^^^^^^^^ +LL | if let Some(1) | C0 | Some(2) = None {} + | ^^^^^^^^^^^^^^^^^^^^^^ | help: nest the patterns | -LL | if let &(0 | 2) | C0 = &0 {} - | ~~~~~~~~~~~~~ +LL | if let Some(1 | 2) | C0 = None {} + | ~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:13:12 + --> $DIR/unnested_or_patterns.rs:16:12 | LL | if let &mut 0 | &mut 2 = &mut 0 {} | ^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | if let &mut (0 | 2) = &mut 0 {} | ~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:14:12 + --> $DIR/unnested_or_patterns.rs:17:12 | LL | if let x @ 0 | x @ 2 = 0 {} | ^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | if let x @ (0 | 2) = 0 {} | ~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:15:12 + --> $DIR/unnested_or_patterns.rs:18:12 | LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | if let (0, 1 | 2 | 3) = (0, 0) {} | ~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:16:12 + --> $DIR/unnested_or_patterns.rs:19:12 | LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | if let (1 | 2 | 3, 0) = (0, 0) {} | ~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:17:12 + --> $DIR/unnested_or_patterns.rs:20:12 | LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | if let (x, ..) | (x, 1 | 2) = (0, 1) {} | ~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:18:12 + --> $DIR/unnested_or_patterns.rs:21:12 | LL | if let [0] | [1] = [0] {} | ^^^^^^^^^ @@ -99,7 +99,7 @@ LL | if let [0 | 1] = [0] {} | ~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:19:12 + --> $DIR/unnested_or_patterns.rs:22:12 | LL | if let [x, 0] | [x, 1] = [0, 1] {} | ^^^^^^^^^^^^^^^ @@ -110,7 +110,7 @@ LL | if let [x, 0 | 1] = [0, 1] {} | ~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:20:12 + --> $DIR/unnested_or_patterns.rs:23:12 | LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | if let [x, 0 | 1 | 2] = [0, 1] {} | ~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:21:12 + --> $DIR/unnested_or_patterns.rs:24:12 | LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL | if let [x, ..] | [x, 1 | 2] = [0, 1] {} | ~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:23:12 + --> $DIR/unnested_or_patterns.rs:26:12 | LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^ @@ -143,7 +143,7 @@ LL | if let TS(0 | 1, x) = TS(0, 0) {} | ~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:24:12 + --> $DIR/unnested_or_patterns.rs:27:12 | LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | if let TS(1 | 2 | 3, 0) = TS(0, 0) {} | ~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:25:12 + --> $DIR/unnested_or_patterns.rs:28:12 | LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} | ~~~~~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:30:12 + --> $DIR/unnested_or_patterns.rs:33:12 | LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From bca346b90b3423de3d333960f864d379b3cd7c0f Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 25 Mar 2022 10:06:10 +0100 Subject: [PATCH 002/129] rebase and use ty::Const in patterns again --- clippy_lints/src/matches/overlapping_arms.rs | 17 +++++++---------- clippy_lints/src/neg_multiply.rs | 2 +- clippy_utils/src/consts.rs | 4 ++-- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 7e65812669029..b5fa847451d1f 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt}; +use clippy_utils::consts::{constant, constant_full_int, FullInt}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; @@ -32,18 +32,15 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) .filter_map(|arm| { if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { - let lhs_const = match lhs { - Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0, - None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?, + let lhs_val = match lhs { + Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0.int_value(cx, ty)?, + None => FullInt::U(ty.numeric_min_val(cx.tcx)?), }; - let rhs_const = match rhs { - Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0, - None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?, + let rhs_val = match rhs { + Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0.int_value(cx, ty)?, + None => FullInt::U(ty.numeric_max_val(cx.tcx)?), }; - let lhs_val = lhs_const.int_value(cx, ty)?; - let rhs_val = rhs_const.int_value(cx, ty)?; - let rhs_bound = match range_end { RangeEnd::Included => EndBound::Included(rhs_val), RangeEnd::Excluded => EndBound::Excluded(rhs_val), diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 0d05c83ffe45e..6ba9ba0753d49 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { if_chain! { 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 consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1); if cx.typeck_results().expr_ty(exp).is_integral(); then { diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 1d6f7acab139b..be46b791aa4b6 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -179,7 +179,7 @@ impl Constant { } /// Parses a `LitKind` to a `Constant`. -pub fn lit_to_constant(lit: &LitKind, ty: Option>) -> Constant { +pub fn lit_to_mir_constant(lit: &LitKind, ty: Option>) -> Constant { match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), @@ -301,7 +301,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { if is_direct_expn_of(e.span, "cfg").is_some() { None } else { - Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))) + Some(lit_to_mir_constant(&lit.node, self.typeck_results.expr_ty_opt(e))) } }, ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec), From 422741151359b8ad4b4f1f4a545aef27f5722c82 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 2 Apr 2022 16:57:09 -0400 Subject: [PATCH 003/129] Generate deprecated lints test --- clippy_dev/src/update_lints.rs | 12 ++++++++++++ tests/ui/deprecated.rs | 4 ++++ tests/ui/deprecated.stderr | 32 ++++++++++++++++---------------- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 4e48b670457e8..b010149626cf8 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -107,6 +107,9 @@ pub fn run(update_mode: UpdateMode) { &content, ); } + + let content = gen_deprecated_lints_test(&deprecated_lints); + process_file("tests/ui/deprecated.rs", update_mode, &content); } pub fn print_lints() { @@ -276,6 +279,15 @@ fn gen_register_lint_list<'a>( output } +fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { + let mut res: String = GENERATED_FILE_COMMENT.into(); + for lint in lints { + writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap(); + } + res.push_str("\nfn main() {}\n"); + res +} + /// Gathers all lints defined in `clippy_lints/src` fn gather_all() -> (Vec, Vec) { let mut lints = Vec::with_capacity(1000); diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 39a2601fee9ac..07270bd76362a 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -1,3 +1,7 @@ +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + #![warn(clippy::should_assert_eq)] #![warn(clippy::extend_from_slice)] #![warn(clippy::range_step_by_zero)] diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 6095f134d55e0..0e142ac8f20e7 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -1,5 +1,5 @@ error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011 - --> $DIR/deprecated.rs:1:9 + --> $DIR/deprecated.rs:5:9 | LL | #![warn(clippy::should_assert_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,91 +7,91 @@ LL | #![warn(clippy::should_assert_eq)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice - --> $DIR/deprecated.rs:2:9 + --> $DIR/deprecated.rs:6:9 | LL | #![warn(clippy::extend_from_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays - --> $DIR/deprecated.rs:3:9 + --> $DIR/deprecated.rs:7:9 | LL | #![warn(clippy::range_step_by_zero)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7 - --> $DIR/deprecated.rs:4:9 + --> $DIR/deprecated.rs:8:9 | LL | #![warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7 - --> $DIR/deprecated.rs:5:9 + --> $DIR/deprecated.rs:9:9 | LL | #![warn(clippy::unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr - --> $DIR/deprecated.rs:6:9 + --> $DIR/deprecated.rs:10:9 | LL | #![warn(clippy::misaligned_transmute)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless - --> $DIR/deprecated.rs:7:9 + --> $DIR/deprecated.rs:11:9 | LL | #![warn(clippy::assign_ops)] | ^^^^^^^^^^^^^^^^^^ error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching - --> $DIR/deprecated.rs:8:9 + --> $DIR/deprecated.rs:12:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior - --> $DIR/deprecated.rs:9:9 + --> $DIR/deprecated.rs:13:9 | LL | #![warn(clippy::unsafe_vector_initialization)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint - --> $DIR/deprecated.rs:10:9 + --> $DIR/deprecated.rs:14:9 | LL | #![warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants - --> $DIR/deprecated.rs:11:9 + --> $DIR/deprecated.rs:15:9 | LL | #![warn(clippy::replace_consts)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018 - --> $DIR/deprecated.rs:12:9 + --> $DIR/deprecated.rs:16:9 | LL | #![warn(clippy::regex_macro)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint - --> $DIR/deprecated.rs:13:9 + --> $DIR/deprecated.rs:17:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint - --> $DIR/deprecated.rs:14:9 + --> $DIR/deprecated.rs:18:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items - --> $DIR/deprecated.rs:15:9 + --> $DIR/deprecated.rs:19:9 | LL | #![warn(clippy::pub_enum_variant_names)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items - --> $DIR/deprecated.rs:16:9 + --> $DIR/deprecated.rs:20:9 | LL | #![warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From d5ef542d376877380fda93ac7c457b5b8ba66833 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 3 Apr 2022 08:51:17 -0400 Subject: [PATCH 004/129] Generate renamed lint test --- clippy_dev/src/update_lints.rs | 73 +++++++++++++++++++++++++++---- clippy_lints/src/lib.rs | 42 +++--------------- clippy_lints/src/renamed_lints.rs | 37 ++++++++++++++++ tests/ui/rename.fixed | 15 +++---- tests/ui/rename.rs | 15 +++---- tests/ui/rename.stderr | 30 ++++++------- 6 files changed, 135 insertions(+), 77 deletions(-) create mode 100644 clippy_lints/src/renamed_lints.rs diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index b010149626cf8..f15b00ecad1e6 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,7 +1,7 @@ use core::fmt::Write; use itertools::Itertools; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fs; use std::path::Path; @@ -32,7 +32,7 @@ pub enum UpdateMode { /// Panics if a file path could not read from or then written to #[allow(clippy::too_many_lines)] pub fn run(update_mode: UpdateMode) { - let (lints, deprecated_lints) = gather_all(); + let (lints, deprecated_lints, renamed_lints) = gather_all(); let internal_lints = Lint::internal_lints(&lints); let usable_lints = Lint::usable_lints(&lints); @@ -110,10 +110,13 @@ pub fn run(update_mode: UpdateMode) { let content = gen_deprecated_lints_test(&deprecated_lints); process_file("tests/ui/deprecated.rs", update_mode, &content); + + let content = gen_renamed_lints_test(&renamed_lints); + process_file("tests/ui/rename.rs", update_mode, &content); } pub fn print_lints() { - let (lint_list, _) = gather_all(); + let (lint_list, _, _) = gather_all(); let usable_lints = Lint::usable_lints(&lint_list); let usable_lint_count = usable_lints.len(); let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); @@ -213,6 +216,19 @@ impl DeprecatedLint { } } +struct RenamedLint { + old_name: String, + new_name: String, +} +impl RenamedLint { + fn new(old_name: &str, new_name: &str) -> Self { + Self { + old_name: remove_line_splices(old_name), + new_name: remove_line_splices(new_name), + } + } +} + /// Generates the code for registering a group fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator) -> String { let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect(); @@ -288,10 +304,30 @@ fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { res } +fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { + let mut seen_lints = HashSet::new(); + let mut res: String = GENERATED_FILE_COMMENT.into(); + res.push_str("// run-rustfix\n\n"); + for lint in lints { + if seen_lints.insert(&lint.new_name) { + writeln!(res, "#![allow({})]", lint.new_name).unwrap(); + } + } + seen_lints.clear(); + for lint in lints { + if seen_lints.insert(&lint.old_name) { + writeln!(res, "#![warn({})]", lint.old_name).unwrap(); + } + } + res.push_str("\nfn main() {}\n"); + res +} + /// Gathers all lints defined in `clippy_lints/src` -fn gather_all() -> (Vec, Vec) { +fn gather_all() -> (Vec, Vec, Vec) { let mut lints = Vec::with_capacity(1000); let mut deprecated_lints = Vec::with_capacity(50); + let mut renamed_lints = Vec::with_capacity(50); let root_path = clippy_project_root().join("clippy_lints/src"); for (rel_path, file) in WalkDir::new(&root_path) @@ -317,13 +353,13 @@ fn gather_all() -> (Vec, Vec) { module.strip_suffix(".rs").unwrap_or(&module) }; - if module == "deprecated_lints" { - parse_deprecated_contents(&contents, &mut deprecated_lints); - } else { - parse_contents(&contents, module, &mut lints); + match module { + "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints), + "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints), + _ => parse_contents(&contents, module, &mut lints), } } - (lints, deprecated_lints) + (lints, deprecated_lints, renamed_lints) } macro_rules! match_tokens { @@ -406,6 +442,25 @@ fn parse_deprecated_contents(contents: &str, lints: &mut Vec) { } } +fn parse_renamed_contents(contents: &str, lints: &mut Vec) { + for line in contents.lines() { + let mut offset = 0usize; + let mut iter = tokenize(line).map(|t| { + let range = offset..offset + t.len; + offset = range.end; + (t.kind, &line[range]) + }); + let (old_name, new_name) = match_tokens!( + iter, + // ("old_name", + Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma + // "new_name"), + Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma + ); + lints.push(RenamedLint::new(old_name, new_name)); + } +} + /// Removes the line splices and surrounding quotes from a string literal fn remove_line_splices(s: &str) -> String { let s = s diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c8b57956b1b62..9812cfde3ec78 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -162,6 +162,8 @@ mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; +mod renamed_lints; + // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absurd_extreme_comparisons; mod approx_const; @@ -920,43 +922,9 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { /// /// Used in `./src/driver.rs`. pub fn register_renamed(ls: &mut rustc_lint::LintStore) { - // NOTE: when renaming a lint, add a corresponding test to tests/ui/rename.rs - ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions"); - ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); - ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); - ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); - ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map"); - ls.register_renamed("clippy::box_vec", "clippy::box_collection"); - ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"); - ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"); - ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); - ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"); - ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"); - ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used"); - ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used"); - ls.register_renamed("clippy::option_expect_used", "clippy::expect_used"); - ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); - ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); - ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); - ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); - ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); - ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str"); - ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok"); - ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types"); - ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods"); - ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow"); - ls.register_renamed("clippy::to_string_in_display", "clippy::recursive_format_impl"); - - // uplifted lints - ls.register_renamed("clippy::invalid_ref", "invalid_value"); - ls.register_renamed("clippy::into_iter_on_array", "array_into_iter"); - ls.register_renamed("clippy::unused_label", "unused_labels"); - ls.register_renamed("clippy::drop_bounds", "drop_bounds"); - ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"); - ls.register_renamed("clippy::panic_params", "non_fmt_panics"); - ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints"); - ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"); - ls.register_renamed("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"); + for (old_name, new_name) in renamed_lints::RENAMED_LINTS { + ls.register_renamed(old_name, new_name); + } } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs new file mode 100644 index 0000000000000..e10dc0e1bfeb7 --- /dev/null +++ b/clippy_lints/src/renamed_lints.rs @@ -0,0 +1,37 @@ +pub static RENAMED_LINTS: &[(&str, &str)] = &[ + ("clippy::stutter", "clippy::module_name_repetitions"), + ("clippy::new_without_default_derive", "clippy::new_without_default"), + ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), + ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), + ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), + ("clippy::box_vec", "clippy::box_collection"), + ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"), + ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"), + ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"), + ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"), + ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"), + ("clippy::option_unwrap_used", "clippy::unwrap_used"), + ("clippy::result_unwrap_used", "clippy::unwrap_used"), + ("clippy::option_expect_used", "clippy::expect_used"), + ("clippy::result_expect_used", "clippy::expect_used"), + ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"), + ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"), + ("clippy::identity_conversion", "clippy::useless_conversion"), + ("clippy::zero_width_space", "clippy::invisible_characters"), + ("clippy::single_char_push_str", "clippy::single_char_add_str"), + ("clippy::if_let_some_result", "clippy::match_result_ok"), + ("clippy::disallowed_type", "clippy::disallowed_types"), + ("clippy::disallowed_method", "clippy::disallowed_methods"), + ("clippy::ref_in_deref", "clippy::needless_borrow"), + ("clippy::to_string_in_display", "clippy::recursive_format_impl"), + // uplifted lints + ("clippy::invalid_ref", "invalid_value"), + ("clippy::into_iter_on_array", "array_into_iter"), + ("clippy::unused_label", "unused_labels"), + ("clippy::drop_bounds", "drop_bounds"), + ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), + ("clippy::panic_params", "non_fmt_panics"), + ("clippy::unknown_clippy_lints", "unknown_lints"), + ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), + ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), +]; diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 24a0c81229198..325f63a64dd90 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -1,12 +1,13 @@ -//! Test for Clippy lint renames. +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + // run-rustfix -#![allow(dead_code)] -// allow the new lint name here, to test if the new name works #![allow(clippy::module_name_repetitions)] #![allow(clippy::new_without_default)] -#![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] +#![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::box_collection)] #![allow(clippy::blocks_in_if_conditions)] @@ -20,8 +21,8 @@ #![allow(clippy::match_result_ok)] #![allow(clippy::disallowed_types)] #![allow(clippy::disallowed_methods)] +#![allow(clippy::needless_borrow)] #![allow(clippy::recursive_format_impl)] -// uplifted lints #![allow(invalid_value)] #![allow(array_into_iter)] #![allow(unused_labels)] @@ -31,11 +32,10 @@ #![allow(unknown_lints)] #![allow(invalid_atomic_ordering)] #![allow(enum_intrinsics_non_enums)] -// warn for the old lint name here, to test if the renaming worked #![warn(clippy::module_name_repetitions)] #![warn(clippy::new_without_default)] -#![warn(clippy::redundant_static_lifetimes)] #![warn(clippy::cognitive_complexity)] +#![warn(clippy::redundant_static_lifetimes)] #![warn(clippy::bind_instead_of_map)] #![warn(clippy::box_collection)] #![warn(clippy::blocks_in_if_conditions)] @@ -57,7 +57,6 @@ #![warn(clippy::disallowed_methods)] #![warn(clippy::needless_borrow)] #![warn(clippy::recursive_format_impl)] -// uplifted lints #![warn(invalid_value)] #![warn(array_into_iter)] #![warn(unused_labels)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index ea64234c680d3..a21b4a242880b 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -1,12 +1,13 @@ -//! Test for Clippy lint renames. +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + // run-rustfix -#![allow(dead_code)] -// allow the new lint name here, to test if the new name works #![allow(clippy::module_name_repetitions)] #![allow(clippy::new_without_default)] -#![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] +#![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::box_collection)] #![allow(clippy::blocks_in_if_conditions)] @@ -20,8 +21,8 @@ #![allow(clippy::match_result_ok)] #![allow(clippy::disallowed_types)] #![allow(clippy::disallowed_methods)] +#![allow(clippy::needless_borrow)] #![allow(clippy::recursive_format_impl)] -// uplifted lints #![allow(invalid_value)] #![allow(array_into_iter)] #![allow(unused_labels)] @@ -31,11 +32,10 @@ #![allow(unknown_lints)] #![allow(invalid_atomic_ordering)] #![allow(enum_intrinsics_non_enums)] -// warn for the old lint name here, to test if the renaming worked #![warn(clippy::stutter)] #![warn(clippy::new_without_default_derive)] -#![warn(clippy::const_static_lifetime)] #![warn(clippy::cyclomatic_complexity)] +#![warn(clippy::const_static_lifetime)] #![warn(clippy::option_and_then_some)] #![warn(clippy::box_vec)] #![warn(clippy::block_in_if_condition_expr)] @@ -57,7 +57,6 @@ #![warn(clippy::disallowed_method)] #![warn(clippy::ref_in_deref)] #![warn(clippy::to_string_in_display)] -// uplifted lints #![warn(clippy::invalid_ref)] #![warn(clippy::into_iter_on_array)] #![warn(clippy::unused_label)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 8b132a7838470..54e12d5fae5ea 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -12,17 +12,17 @@ error: lint `clippy::new_without_default_derive` has been renamed to `clippy::ne LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` -error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` +error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` --> $DIR/rename.rs:37:9 | -LL | #![warn(clippy::const_static_lifetime)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` +LL | #![warn(clippy::cyclomatic_complexity)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` -error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` +error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` --> $DIR/rename.rs:38:9 | -LL | #![warn(clippy::cyclomatic_complexity)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` +LL | #![warn(clippy::const_static_lifetime)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` --> $DIR/rename.rs:39:9 @@ -151,55 +151,55 @@ LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` From 2f780758099bd649e8b9434449ec5e303e31e1aa Mon Sep 17 00:00:00 2001 From: b-naber Date: Wed, 6 Apr 2022 10:12:42 +0200 Subject: [PATCH 005/129] get rid of visit_constant in thir visitor --- clippy_lints/src/matches/overlapping_arms.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index b5fa847451d1f..c0b3e95b18523 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, constant_full_int, FullInt}; +use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; @@ -32,15 +32,16 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) .filter_map(|arm| { if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { - let lhs_val = match lhs { - Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0.int_value(cx, ty)?, - None => FullInt::U(ty.numeric_min_val(cx.tcx)?), + let lhs_const = match lhs { + Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0, + None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?, }; - let rhs_val = match rhs { - Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0.int_value(cx, ty)?, - None => FullInt::U(ty.numeric_max_val(cx.tcx)?), + let rhs_const = match rhs { + Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0, + None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?, }; - + let lhs_val = lhs_const.int_value(cx, ty)?; + let rhs_val = rhs_const.int_value(cx, ty)?; let rhs_bound = match range_end { RangeEnd::Included => EndBound::Included(rhs_val), RangeEnd::Excluded => EndBound::Excluded(rhs_val), From 6e20a634e7a87a8c0234397e9fdcd8d8dea4e0f4 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 6 Apr 2022 09:26:59 -0400 Subject: [PATCH 006/129] Don't lint `manual_non_exhaustive` when the enum variant is used --- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/manual_non_exhaustive.rs | 177 ++++++++++++------ tests/ui/manual_non_exhaustive_enum.rs | 78 ++++++++ tests/ui/manual_non_exhaustive_enum.stderr | 41 ++++ ...ive.rs => manual_non_exhaustive_struct.rs} | 63 ------- ...rr => manual_non_exhaustive_struct.stderr} | 58 +----- 6 files changed, 246 insertions(+), 174 deletions(-) create mode 100644 tests/ui/manual_non_exhaustive_enum.rs create mode 100644 tests/ui/manual_non_exhaustive_enum.stderr rename tests/ui/{manual_non_exhaustive.rs => manual_non_exhaustive_struct.rs} (58%) rename tests/ui/{manual_non_exhaustive.stderr => manual_non_exhaustive_struct.stderr} (53%) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8dab039f24fe7..b4a70988286ae 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -575,7 +575,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv))); store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv))); store.register_late_pass(move || Box::new(matches::Matches::new(msrv))); - store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv))); + store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); + store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv))); store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv))); store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv))); diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 33d1bb2985f43..7b4b8d6bffa0c 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,13 +1,18 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::{is_lint_allowed, meets_msrv, msrvs}; use if_chain::if_chain; -use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind}; +use rustc_ast::ast::{self, FieldDef, VisibilityKind}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{self as hir, Expr, ExprKind, QPath}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::DefIdTree; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{sym, Span}; declare_clippy_lint! { @@ -58,55 +63,84 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -#[derive(Clone)] -pub struct ManualNonExhaustive { +#[allow(clippy::module_name_repetitions)] +pub struct ManualNonExhaustiveStruct { msrv: Option, } -impl ManualNonExhaustive { +impl ManualNonExhaustiveStruct { #[must_use] pub fn new(msrv: Option) -> Self { Self { msrv } } } -impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); +impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); -impl EarlyLintPass for ManualNonExhaustive { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { +#[allow(clippy::module_name_repetitions)] +pub struct ManualNonExhaustiveEnum { + msrv: Option, + constructed_enum_variants: FxHashSet<(DefId, DefId)>, + potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>, +} + +impl ManualNonExhaustiveEnum { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { + msrv, + constructed_enum_variants: FxHashSet::default(), + potential_enums: Vec::new(), + } + } +} + +impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); + +impl EarlyLintPass for ManualNonExhaustiveStruct { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { return; } - match &item.kind { - ItemKind::Enum(def, _) => { - check_manual_non_exhaustive_enum(cx, item, &def.variants); - }, - ItemKind::Struct(variant_data, _) => { - if let VariantData::Unit(..) = variant_data { - return; - } - - check_manual_non_exhaustive_struct(cx, item, variant_data); - }, - _ => {}, + if let ast::ItemKind::Struct(variant_data, _) = &item.kind { + if let ast::VariantData::Unit(..) = variant_data { + return; + } + + check_manual_non_exhaustive_struct(cx, item, variant_data); } } extract_msrv_attr!(EarlyContext); } -fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) { - fn is_non_exhaustive_marker(variant: &Variant) -> bool { - matches!(variant.data, VariantData::Unit(_)) - && variant.ident.as_str().starts_with('_') - && is_doc_hidden(&variant.attrs) +fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &ast::Item, data: &ast::VariantData) { + fn is_private(field: &FieldDef) -> bool { + matches!(field.vis.kind, VisibilityKind::Inherited) + } + + fn is_non_exhaustive_marker(field: &FieldDef) -> bool { + is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_')) + } + + fn find_header_span(cx: &EarlyContext<'_>, item: &ast::Item, data: &ast::VariantData) -> Span { + let delimiter = match data { + ast::VariantData::Struct(..) => '{', + ast::VariantData::Tuple(..) => '(', + ast::VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"), + }; + + cx.sess().source_map().span_until_char(item.span, delimiter) } - let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)); + let fields = data.fields(); + let private_fields = fields.iter().filter(|f| is_private(f)).count(); + let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count(); + if_chain! { - if let Some(marker) = markers.next(); - if markers.count() == 0 && variants.len() > 1; + if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1; + if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f)); then { span_lint_and_then( cx, @@ -116,7 +150,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants |diag| { if_chain! { if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); - let header_span = cx.sess().source_map().span_until_char(item.span, '{'); + let header_span = find_header_span(cx, item, data); if let Some(snippet) = snippet_opt(cx, header_span); then { diag.span_suggestion( @@ -127,60 +161,79 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants ); } } - diag.span_help(marker.span, "remove this variant"); + diag.span_help(marker.span, "remove this field"); }); } } } -fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) { - fn is_private(field: &FieldDef) -> bool { - matches!(field.vis.kind, VisibilityKind::Inherited) - } +impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { + return; + } - fn is_non_exhaustive_marker(field: &FieldDef) -> bool { - is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_')) + if let hir::ItemKind::Enum(def, _) = &item.kind + && def.variants.len() > 1 + { + let mut iter = def.variants.iter().filter_map(|v| { + let id = cx.tcx.hir().local_def_id(v.id); + (matches!(v.data, hir::VariantData::Unit(_)) + && v.ident.as_str().starts_with('_') + && is_doc_hidden(cx.tcx.get_attrs(id.to_def_id()))) + .then(|| (id, v.span)) + }); + if let Some((id, span)) = iter.next() + && iter.next().is_none() + { + self.potential_enums.push((item.def_id, id, item.span, span)); + } + } } - fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span { - let delimiter = match data { - VariantData::Struct(..) => '{', - VariantData::Tuple(..) => '(', - VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"), - }; - - cx.sess().source_map().span_until_char(item.span, delimiter) + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind + && let [.., name] = p.segments + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res + && name.ident.as_str().starts_with('_') + && let Some(variant_id) = cx.tcx.parent(id) + && let Some(enum_id) = cx.tcx.parent(variant_id) + { + self.constructed_enum_variants.insert((enum_id, variant_id)); + } } - let fields = data.fields(); - let private_fields = fields.iter().filter(|f| is_private(f)).count(); - let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count(); - - if_chain! { - if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1; - if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f)); - then { + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + for &(enum_id, _, enum_span, variant_span) in + self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| { + !self + .constructed_enum_variants + .contains(&(enum_id.to_def_id(), variant_id.to_def_id())) + && !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id)) + }) + { span_lint_and_then( cx, MANUAL_NON_EXHAUSTIVE, - item.span, + enum_span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - if_chain! { - if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); - let header_span = find_header_span(cx, item, data); - if let Some(snippet) = snippet_opt(cx, header_span); - then { + if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive() + && let header_span = cx.sess().source_map().span_until_char(enum_span, '{') + && let Some(snippet) = snippet_opt(cx, header_span) + { diag.span_suggestion( header_span, "add the attribute", format!("#[non_exhaustive] {}", snippet), Applicability::Unspecified, ); - } } - diag.span_help(marker.span, "remove this field"); - }); + diag.span_help(variant_span, "remove this variant"); + }, + ); } } + + extract_msrv_attr!(LateContext); } diff --git a/tests/ui/manual_non_exhaustive_enum.rs b/tests/ui/manual_non_exhaustive_enum.rs new file mode 100644 index 0000000000000..f23c6d69b4c6a --- /dev/null +++ b/tests/ui/manual_non_exhaustive_enum.rs @@ -0,0 +1,78 @@ +#![warn(clippy::manual_non_exhaustive)] +#![allow(unused)] + +enum E { + A, + B, + #[doc(hidden)] + _C, +} + +// user forgot to remove the marker +#[non_exhaustive] +enum Ep { + A, + B, + #[doc(hidden)] + _C, +} + +// marker variant does not have doc hidden attribute, should be ignored +enum NoDocHidden { + A, + B, + _C, +} + +// name of variant with doc hidden does not start with underscore, should be ignored +enum NoUnderscore { + A, + B, + #[doc(hidden)] + C, +} + +// variant with doc hidden is not unit, should be ignored +enum NotUnit { + A, + B, + #[doc(hidden)] + _C(bool), +} + +// variant with doc hidden is the only one, should be ignored +enum OnlyMarker { + #[doc(hidden)] + _A, +} + +// variant with multiple markers, should be ignored +enum MultipleMarkers { + A, + #[doc(hidden)] + _B, + #[doc(hidden)] + _C, +} + +// already non_exhaustive and no markers, should be ignored +#[non_exhaustive] +enum NonExhaustive { + A, + B, +} + +// marked is used, don't lint +enum UsedHidden { + #[doc(hidden)] + _A, + B, + C, +} +fn foo(x: &mut UsedHidden) { + if matches!(*x, UsedHidden::B) { + *x = UsedHidden::_A; + } +} + +fn main() {} diff --git a/tests/ui/manual_non_exhaustive_enum.stderr b/tests/ui/manual_non_exhaustive_enum.stderr new file mode 100644 index 0000000000000..317a45d2cbd59 --- /dev/null +++ b/tests/ui/manual_non_exhaustive_enum.stderr @@ -0,0 +1,41 @@ +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive_enum.rs:4:1 + | +LL | enum E { + | ^----- + | | + | _help: add the attribute: `#[non_exhaustive] enum E` + | | +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_^ + | + = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` +help: remove this variant + --> $DIR/manual_non_exhaustive_enum.rs:8:5 + | +LL | _C, + | ^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive_enum.rs:13:1 + | +LL | / enum Ep { +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_^ + | +help: remove this variant + --> $DIR/manual_non_exhaustive_enum.rs:17:5 + | +LL | _C, + | ^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/manual_non_exhaustive.rs b/tests/ui/manual_non_exhaustive_struct.rs similarity index 58% rename from tests/ui/manual_non_exhaustive.rs rename to tests/ui/manual_non_exhaustive_struct.rs index 7a788f4852072..498eee4447b88 100644 --- a/tests/ui/manual_non_exhaustive.rs +++ b/tests/ui/manual_non_exhaustive_struct.rs @@ -1,69 +1,6 @@ #![warn(clippy::manual_non_exhaustive)] #![allow(unused)] -mod enums { - enum E { - A, - B, - #[doc(hidden)] - _C, - } - - // user forgot to remove the marker - #[non_exhaustive] - enum Ep { - A, - B, - #[doc(hidden)] - _C, - } - - // marker variant does not have doc hidden attribute, should be ignored - enum NoDocHidden { - A, - B, - _C, - } - - // name of variant with doc hidden does not start with underscore, should be ignored - enum NoUnderscore { - A, - B, - #[doc(hidden)] - C, - } - - // variant with doc hidden is not unit, should be ignored - enum NotUnit { - A, - B, - #[doc(hidden)] - _C(bool), - } - - // variant with doc hidden is the only one, should be ignored - enum OnlyMarker { - #[doc(hidden)] - _A, - } - - // variant with multiple markers, should be ignored - enum MultipleMarkers { - A, - #[doc(hidden)] - _B, - #[doc(hidden)] - _C, - } - - // already non_exhaustive and no markers, should be ignored - #[non_exhaustive] - enum NonExhaustive { - A, - B, - } -} - mod structs { struct S { pub a: i32, diff --git a/tests/ui/manual_non_exhaustive.stderr b/tests/ui/manual_non_exhaustive_struct.stderr similarity index 53% rename from tests/ui/manual_non_exhaustive.stderr rename to tests/ui/manual_non_exhaustive_struct.stderr index 613c5e8ca1d45..e0766c17b7580 100644 --- a/tests/ui/manual_non_exhaustive.stderr +++ b/tests/ui/manual_non_exhaustive_struct.stderr @@ -1,44 +1,5 @@ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:5:5 - | -LL | enum E { - | ^----- - | | - | _____help: add the attribute: `#[non_exhaustive] enum E` - | | -LL | | A, -LL | | B, -LL | | #[doc(hidden)] -LL | | _C, -LL | | } - | |_____^ - | - = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` -help: remove this variant - --> $DIR/manual_non_exhaustive.rs:9:9 - | -LL | _C, - | ^^ - -error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:14:5 - | -LL | / enum Ep { -LL | | A, -LL | | B, -LL | | #[doc(hidden)] -LL | | _C, -LL | | } - | |_____^ - | -help: remove this variant - --> $DIR/manual_non_exhaustive.rs:18:9 - | -LL | _C, - | ^^ - -error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:68:5 + --> $DIR/manual_non_exhaustive_struct.rs:5:5 | LL | struct S { | ^------- @@ -51,14 +12,15 @@ LL | | _c: (), LL | | } | |_____^ | + = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` help: remove this field - --> $DIR/manual_non_exhaustive.rs:71:9 + --> $DIR/manual_non_exhaustive_struct.rs:8:9 | LL | _c: (), | ^^^^^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:76:5 + --> $DIR/manual_non_exhaustive_struct.rs:13:5 | LL | / struct Sp { LL | | pub a: i32, @@ -68,13 +30,13 @@ LL | | } | |_____^ | help: remove this field - --> $DIR/manual_non_exhaustive.rs:79:9 + --> $DIR/manual_non_exhaustive_struct.rs:16:9 | LL | _c: (), | ^^^^^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:117:5 + --> $DIR/manual_non_exhaustive_struct.rs:54:5 | LL | struct T(pub i32, pub i32, ()); | --------^^^^^^^^^^^^^^^^^^^^^^^ @@ -82,22 +44,22 @@ LL | struct T(pub i32, pub i32, ()); | help: add the attribute: `#[non_exhaustive] struct T` | help: remove this field - --> $DIR/manual_non_exhaustive.rs:117:32 + --> $DIR/manual_non_exhaustive_struct.rs:54:32 | LL | struct T(pub i32, pub i32, ()); | ^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:121:5 + --> $DIR/manual_non_exhaustive_struct.rs:58:5 | LL | struct Tp(pub i32, pub i32, ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove this field - --> $DIR/manual_non_exhaustive.rs:121:33 + --> $DIR/manual_non_exhaustive_struct.rs:58:33 | LL | struct Tp(pub i32, pub i32, ()); | ^^ -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors From b0f8a3155159286af233577aa0f50d933770dc47 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 6 Apr 2022 09:53:20 -0400 Subject: [PATCH 007/129] Cleanup `manual_non_exhaustive` --- clippy_lints/src/manual_non_exhaustive.rs | 83 +++++++++-------------- 1 file changed, 32 insertions(+), 51 deletions(-) diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 7b4b8d6bffa0c..2501dd5bed4b7 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -2,8 +2,7 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::{is_lint_allowed, meets_msrv, msrvs}; -use if_chain::if_chain; -use rustc_ast::ast::{self, FieldDef, VisibilityKind}; +use rustc_ast::ast::{self, VisibilityKind}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -104,55 +103,34 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { } if let ast::ItemKind::Struct(variant_data, _) = &item.kind { - if let ast::VariantData::Unit(..) = variant_data { + let (fields, delimiter) = match variant_data { + ast::VariantData::Struct(fields, _) => (&**fields, '{'), + ast::VariantData::Tuple(fields, _) => (&**fields, '('), + ast::VariantData::Unit(_) => return, + }; + if fields.len() <= 1 { return; } - - check_manual_non_exhaustive_struct(cx, item, variant_data); - } - } - - extract_msrv_attr!(EarlyContext); -} - -fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &ast::Item, data: &ast::VariantData) { - fn is_private(field: &FieldDef) -> bool { - matches!(field.vis.kind, VisibilityKind::Inherited) - } - - fn is_non_exhaustive_marker(field: &FieldDef) -> bool { - is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_')) - } - - fn find_header_span(cx: &EarlyContext<'_>, item: &ast::Item, data: &ast::VariantData) -> Span { - let delimiter = match data { - ast::VariantData::Struct(..) => '{', - ast::VariantData::Tuple(..) => '(', - ast::VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"), - }; - - cx.sess().source_map().span_until_char(item.span, delimiter) - } - - let fields = data.fields(); - let private_fields = fields.iter().filter(|f| is_private(f)).count(); - let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count(); - - if_chain! { - if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1; - if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f)); - then { - span_lint_and_then( - cx, - MANUAL_NON_EXHAUSTIVE, - item.span, - "this seems like a manual implementation of the non-exhaustive pattern", - |diag| { - if_chain! { - if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); - let header_span = find_header_span(cx, item, data); - if let Some(snippet) = snippet_opt(cx, header_span); - then { + let mut iter = fields.iter().filter_map(|f| match f.vis.kind { + VisibilityKind::Public => None, + VisibilityKind::Inherited => Some(Ok(f)), + _ => Some(Err(())), + }); + if let Some(Ok(field)) = iter.next() + && iter.next().is_none() + && field.ty.kind.is_unit() + && field.ident.map_or(true, |name| name.as_str().starts_with('_')) + { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)) + && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter) + && let Some(snippet) = snippet_opt(cx, header_span) + { diag.span_suggestion( header_span, "add the attribute", @@ -160,11 +138,14 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &ast::Item, d Applicability::Unspecified, ); } + diag.span_help(field.span, "remove this field"); } - diag.span_help(marker.span, "remove this field"); - }); + ); + } } } + + extract_msrv_attr!(EarlyContext); } impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { From ddd3af227329236db174dbfb920b27037e138279 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 6 Apr 2022 10:56:06 -0400 Subject: [PATCH 008/129] Only lint `mut_from_ref` when unsafe code is used --- clippy_lints/src/ptr.rs | 56 +++++++++++++++++++----------------- clippy_utils/src/visitors.rs | 34 +++++++++++++++++++++- tests/ui/mut_from_ref.rs | 20 +++++++++---- tests/ui/mut_from_ref.stderr | 14 ++++++++- 4 files changed, 89 insertions(+), 35 deletions(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index ba1997e70e131..4ecde8f49581e 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -3,6 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::expr_sig; +use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -10,9 +11,9 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{ - self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, + self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn, - TraitItem, TraitItemKind, TyKind, + TraitItem, TraitItemKind, TyKind, Unsafety, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; @@ -145,7 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; } - check_mut_from_ref(cx, sig.decl); + check_mut_from_ref(cx, sig, None); for arg in check_fn_args( cx, cx.tcx.fn_sig(item.def_id).skip_binder().inputs(), @@ -170,10 +171,10 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { let hir = cx.tcx.hir(); let mut parents = hir.parent_iter(body.value.hir_id); - let (item_id, decl, is_trait_item) = match parents.next() { + let (item_id, sig, is_trait_item) = match parents.next() { Some((_, Node::Item(i))) => { if let ItemKind::Fn(sig, ..) = &i.kind { - (i.def_id, sig.decl, false) + (i.def_id, sig, false) } else { return; } @@ -185,14 +186,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; } if let ImplItemKind::Fn(sig, _) = &i.kind { - (i.def_id, sig.decl, false) + (i.def_id, sig, false) } else { return; } }, Some((_, Node::TraitItem(i))) => { if let TraitItemKind::Fn(sig, _) = &i.kind { - (i.def_id, sig.decl, true) + (i.def_id, sig, true) } else { return; } @@ -200,7 +201,8 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { _ => return, }; - check_mut_from_ref(cx, decl); + check_mut_from_ref(cx, sig, Some(body)); + let decl = sig.decl; let sig = cx.tcx.fn_sig(item_id).skip_binder(); let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params) .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not) @@ -478,31 +480,31 @@ fn check_fn_args<'cx, 'tcx: 'cx>( }) } -fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) { - if let FnRetTy::Return(ty) = decl.output { - if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) { - let mut immutables = vec![]; - for (_, mutbl, argspan) in decl - .inputs - .iter() - .filter_map(get_rptr_lm) - .filter(|&(lt, _, _)| lt.name == out.name) - { - if mutbl == Mutability::Mut { - return; - } - immutables.push(argspan); - } - if immutables.is_empty() { - return; - } +fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) { + if let FnRetTy::Return(ty) = sig.decl.output + && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) + { + let args: Option> = sig + .decl + .inputs + .iter() + .filter_map(get_rptr_lm) + .filter(|&(lt, _, _)| lt.name == out.name) + .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span)) + .collect(); + if let Some(args) = args + && !args.is_empty() + && body.map_or(true, |body| { + sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value) + }) + { span_lint_and_then( cx, MUT_FROM_REF, ty.span, "mutable borrow from immutable input(s)", |diag| { - let ms = MultiSpan::from_spans(immutables); + let ms = MultiSpan::from_spans(args); diag.span_note(ms, "immutable borrow here"); }, ); diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 40451b17a9c63..3db64b2535398 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -3,7 +3,8 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor}; use rustc_hir::{ - Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety, + Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, UnsafeSource, + Unsafety, }; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -370,3 +371,34 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { v.visit_expr(e); v.is_unsafe } + +/// Checks if the given expression contains an unsafe block +pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + found_unsafe: bool, + } + impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_block(&mut self, b: &'tcx Block<'_>) { + if self.found_unsafe { + return; + } + if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { + self.found_unsafe = true; + return; + } + walk_block(self, b); + } + } + let mut v = V { + cx, + found_unsafe: false, + }; + v.visit_expr(e); + v.found_unsafe +} diff --git a/tests/ui/mut_from_ref.rs b/tests/ui/mut_from_ref.rs index a9a04c8f56b94..370dbd5882161 100644 --- a/tests/ui/mut_from_ref.rs +++ b/tests/ui/mut_from_ref.rs @@ -5,7 +5,7 @@ struct Foo; impl Foo { fn this_wont_hurt_a_bit(&self) -> &mut Foo { - unimplemented!() + unsafe { unimplemented!() } } } @@ -15,29 +15,37 @@ trait Ouch { impl Ouch for Foo { fn ouch(x: &Foo) -> &mut Foo { - unimplemented!() + unsafe { unimplemented!() } } } fn fail(x: &u32) -> &mut u16 { - unimplemented!() + unsafe { unimplemented!() } } fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { - unimplemented!() + unsafe { unimplemented!() } } fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { - unimplemented!() + unsafe { unimplemented!() } } // this is OK, because the result borrows y fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 { - unimplemented!() + unsafe { unimplemented!() } } // this is also OK, because the result could borrow y fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 { + unsafe { unimplemented!() } +} + +unsafe fn also_broken(x: &u32) -> &mut u32 { + unimplemented!() +} + +fn without_unsafe(x: &u32) -> &mut u32 { unimplemented!() } diff --git a/tests/ui/mut_from_ref.stderr b/tests/ui/mut_from_ref.stderr index 4787999920bc2..b76d6a13ffb9c 100644 --- a/tests/ui/mut_from_ref.stderr +++ b/tests/ui/mut_from_ref.stderr @@ -59,5 +59,17 @@ note: immutable borrow here LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ -error: aborting due to 5 previous errors +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:44:35 + | +LL | unsafe fn also_broken(x: &u32) -> &mut u32 { + | ^^^^^^^^ + | +note: immutable borrow here + --> $DIR/mut_from_ref.rs:44:26 + | +LL | unsafe fn also_broken(x: &u32) -> &mut u32 { + | ^^^^ + +error: aborting due to 6 previous errors From d5e887c6f005347c5c243fe8691e2b7d7d594203 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 6 Apr 2022 11:57:55 -0400 Subject: [PATCH 009/129] Better check size and alignment requirements in `unsound_collection_transmute` --- clippy_lints/src/transmute/utils.rs | 18 +++++++----------- tests/ui/transmute_collection.rs | 5 ++++- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index f359b606e4548..0e07a0b0392eb 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,10 +1,9 @@ use clippy_utils::last_path_segment; use clippy_utils::source::snippet; -use clippy_utils::ty::is_normalizable; use if_chain::if_chain; use rustc_hir::{Expr, GenericArg, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, cast::CastKind, Ty}; +use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited}; @@ -34,15 +33,12 @@ pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty // check if the component types of the transmuted collection and the result have different ABI, // size or alignment pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { - let empty_param_env = ty::ParamEnv::empty(); - // check if `from` and `to` are normalizable to avoid ICE (#4968) - if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) { - return false; - } - let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from)); - let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to)); - if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) { - from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi + if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from) + && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to) + && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from)) + && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to)) + { + from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi } else { // no idea about layout, so don't lint false diff --git a/tests/ui/transmute_collection.rs b/tests/ui/transmute_collection.rs index cd5a7127791a8..5a431bee04a45 100644 --- a/tests/ui/transmute_collection.rs +++ b/tests/ui/transmute_collection.rs @@ -1,7 +1,7 @@ #![warn(clippy::unsound_collection_transmute)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; -use std::mem::transmute; +use std::mem::{transmute, MaybeUninit}; fn main() { unsafe { @@ -43,5 +43,8 @@ fn main() { // wrong layout let _ = transmute::<_, HashMap>(HashMap::::new()); let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new()); + + let _ = transmute::<_, Vec>(Vec::>::new()); + let _ = transmute::<_, Vec<*mut u32>>(Vec::>::new()); } } From 06cfeb90c156d70c2318774081b37db809f6d840 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Sat, 2 Apr 2022 15:01:07 -0600 Subject: [PATCH 010/129] New lint `is_digit_ascii_radix` --- CHANGELOG.md | 1 + ...se_sensitive_file_extension_comparisons.rs | 4 +- clippy_lints/src/doc.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 + .../src/methods/is_digit_ascii_radix.rs | 50 +++++++++++++++++++ clippy_lints/src/methods/mod.rs | 33 ++++++++++++ clippy_lints/src/misc_early/mod.rs | 2 +- clippy_lints/src/non_expressive_names.rs | 2 +- clippy_utils/src/msrvs.rs | 1 + clippy_utils/src/numeric_literal.rs | 2 +- tests/ui/is_digit_ascii_radix.fixed | 18 +++++++ tests/ui/is_digit_ascii_radix.rs | 18 +++++++ tests/ui/is_digit_ascii_radix.stderr | 22 ++++++++ tests/ui/to_digit_is_some.fixed | 4 +- tests/ui/to_digit_is_some.rs | 4 +- tests/ui/to_digit_is_some.stderr | 8 +-- 18 files changed, 160 insertions(+), 14 deletions(-) create mode 100644 clippy_lints/src/methods/is_digit_ascii_radix.rs create mode 100644 tests/ui/is_digit_ascii_radix.fixed create mode 100644 tests/ui/is_digit_ascii_radix.rs create mode 100644 tests/ui/is_digit_ascii_radix.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index b4097ea86a518..c5c93b1132783 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3356,6 +3356,7 @@ Released 2018-09-13 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters +[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index df780747a0c75..e3e31c5863edf 100644 --- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -43,8 +43,8 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: & if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind; if (2..=6).contains(&ext_literal.as_str().len()); if ext_literal.as_str().starts_with('.'); - if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_digit(10)) - || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_digit(10)); + if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) + || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()); then { let mut ty = ctx.typeck_results().expr_ty(obj); ty = match ty.kind() { diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 28d0c75fde6ba..e72031e48778f 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -739,7 +739,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok). /// Plurals are also excluded (`IDs` is ok). fn is_camel_case(s: &str) -> bool { - if s.starts_with(|c: char| c.is_digit(10)) { + if s.starts_with(|c: char| c.is_ascii_digit()) { return false; } diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 14ca93b5f3c14..aa8e3b535b15d 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -165,6 +165,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::FLAT_MAP_IDENTITY), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::IS_DIGIT_ASCII_RADIX), LintId::of(methods::ITERATOR_STEP_BY_ZERO), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_COUNT), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 532590aaa5a3d..7436f31125b1e 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -302,6 +302,7 @@ store.register_lints(&[ methods::INEFFICIENT_TO_STRING, methods::INSPECT_FOR_EACH, methods::INTO_ITER_ON_REF, + methods::IS_DIGIT_ASCII_RADIX, methods::ITERATOR_STEP_BY_ZERO, methods::ITER_CLONED_COLLECT, methods::ITER_COUNT, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 3114afac8863e..2d98802cb1401 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -61,6 +61,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(methods::CHARS_NEXT_CMP), LintId::of(methods::ERR_EXPECT), LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::IS_DIGIT_ASCII_RADIX), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_NEXT_SLICE), LintId::of(methods::ITER_NTH_ZERO), diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs new file mode 100644 index 0000000000000..ad333df2f2d5d --- /dev/null +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -0,0 +1,50 @@ +//! Lint for `c.is_digit(10)` + +use super::IS_DIGIT_ASCII_RADIX; +use clippy_utils::{ + consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs, + source::snippet_with_applicability, +}; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_semver::RustcVersion; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + self_arg: &'tcx Expr<'_>, + radix: &'tcx Expr<'_>, + msrv: Option<&RustcVersion>, +) { + if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) { + return; + } + + if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_char() { + return; + } + + if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) { + let (num, replacement) = match radix_val { + FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"), + FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"), + _ => return, + }; + let mut applicability = Applicability::MachineApplicable; + + span_lint_and_sugg( + cx, + IS_DIGIT_ASCII_RADIX, + expr.span, + &format!("use of `char::is_digit` with literal radix of {}", num), + "try", + format!( + "{}.{}()", + snippet_with_applicability(cx, self_arg.span, "..", &mut applicability), + replacement + ), + applicability, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 70d021a1668eb..6a7841a90dfa9 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -26,6 +26,7 @@ mod implicit_clone; mod inefficient_to_string; mod inspect_for_each; mod into_iter_on_ref; +mod is_digit_ascii_radix; mod iter_cloned_collect; mod iter_count; mod iter_next_slice; @@ -2131,6 +2132,36 @@ declare_clippy_lint! { "no-op use of `deref` or `deref_mut` method to `Option`." } +declare_clippy_lint! { + /// ### What it does + /// Finds usages of [`char::is_digit`] + /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that + /// can be replaced with [`is_ascii_digit`] + /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or + /// [`is_ascii_hexdigit`] + /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit). + /// + /// ### Why is this bad? + /// `is_digit(..)` is slower and requires specifying the radix. + /// + /// ### Example + /// ```rust + /// let c: char = '6'; + /// c.is_digit(10); + /// c.is_digit(16); + /// ``` + /// Use instead: + /// ```rust + /// let c: char = '6'; + /// c.is_ascii_digit(); + /// c.is_ascii_hexdigit(); + /// ``` + #[clippy::version = "1.61.0"] + pub IS_DIGIT_ASCII_RADIX, + style, + "use of `char::is_digit(..)` with literal radix of 10 or 16" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2219,6 +2250,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_JOIN, ERR_EXPECT, NEEDLESS_OPTION_AS_DEREF, + IS_DIGIT_ASCII_RADIX, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2516,6 +2548,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), ("is_file", []) => filetype_is_file::check(cx, expr, recv), + ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("join", [join_arg]) => { diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index d955fad7d41a2..6860b60acbdb4 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -361,7 +361,7 @@ impl MiscEarlyLints { // See for a regression. // FIXME: Find a better way to detect those cases. let lit_snip = match snippet_opt(cx, lit.span) { - Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip, + Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip, _ => return, }; diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 0d0c88b02c78b..e3bc40c4b4914 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -197,7 +197,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { if interned_name.chars().any(char::is_uppercase) { return; } - if interned_name.chars().all(|c| c.is_digit(10) || c == '_') { + if interned_name.chars().all(|c| c.is_ascii_digit() || c == '_') { span_lint( self.0.cx, JUST_UNDERSCORES_AND_DIGITS, diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 0424e06720263..134fd1ce505a0 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -32,4 +32,5 @@ msrv_aliases! { 1,28,0 { FROM_BOOL } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } + 1,24,0 { IS_ASCII_DIGIT } } diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 908ff822712ff..b92d42e83232c 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -57,7 +57,7 @@ impl<'a> NumericLiteral<'a> { .trim_start() .chars() .next() - .map_or(false, |c| c.is_digit(10)) + .map_or(false, |c| c.is_ascii_digit()) { let (unsuffixed, suffix) = split_suffix(src, lit_kind); let float = matches!(lit_kind, LitKind::Float(..)); diff --git a/tests/ui/is_digit_ascii_radix.fixed b/tests/ui/is_digit_ascii_radix.fixed new file mode 100644 index 0000000000000..c0ba647d70791 --- /dev/null +++ b/tests/ui/is_digit_ascii_radix.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::is_digit_ascii_radix)] + +const TEN: u32 = 10; + +fn main() { + let c: char = '6'; + + // Should trigger the lint. + let _ = c.is_ascii_digit(); + let _ = c.is_ascii_hexdigit(); + let _ = c.is_ascii_hexdigit(); + + // Should not trigger the lint. + let _ = c.is_digit(11); + let _ = c.is_digit(TEN); +} diff --git a/tests/ui/is_digit_ascii_radix.rs b/tests/ui/is_digit_ascii_radix.rs new file mode 100644 index 0000000000000..68e3f3243d96d --- /dev/null +++ b/tests/ui/is_digit_ascii_radix.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::is_digit_ascii_radix)] + +const TEN: u32 = 10; + +fn main() { + let c: char = '6'; + + // Should trigger the lint. + let _ = c.is_digit(10); + let _ = c.is_digit(16); + let _ = c.is_digit(0x10); + + // Should not trigger the lint. + let _ = c.is_digit(11); + let _ = c.is_digit(TEN); +} diff --git a/tests/ui/is_digit_ascii_radix.stderr b/tests/ui/is_digit_ascii_radix.stderr new file mode 100644 index 0000000000000..dc5cb2913ae15 --- /dev/null +++ b/tests/ui/is_digit_ascii_radix.stderr @@ -0,0 +1,22 @@ +error: use of `char::is_digit` with literal radix of 10 + --> $DIR/is_digit_ascii_radix.rs:11:13 + | +LL | let _ = c.is_digit(10); + | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_digit()` + | + = note: `-D clippy::is-digit-ascii-radix` implied by `-D warnings` + +error: use of `char::is_digit` with literal radix of 16 + --> $DIR/is_digit_ascii_radix.rs:12:13 + | +LL | let _ = c.is_digit(16); + | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()` + +error: use of `char::is_digit` with literal radix of 16 + --> $DIR/is_digit_ascii_radix.rs:13:13 + | +LL | let _ = c.is_digit(0x10); + | ^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/to_digit_is_some.fixed b/tests/ui/to_digit_is_some.fixed index 19184df0becb5..3c5e964271465 100644 --- a/tests/ui/to_digit_is_some.fixed +++ b/tests/ui/to_digit_is_some.fixed @@ -6,6 +6,6 @@ fn main() { let c = 'x'; let d = &c; - let _ = d.is_digit(10); - let _ = char::is_digit(c, 10); + let _ = d.is_digit(8); + let _ = char::is_digit(c, 8); } diff --git a/tests/ui/to_digit_is_some.rs b/tests/ui/to_digit_is_some.rs index 45a6728ebf578..4f247c06ceeda 100644 --- a/tests/ui/to_digit_is_some.rs +++ b/tests/ui/to_digit_is_some.rs @@ -6,6 +6,6 @@ fn main() { let c = 'x'; let d = &c; - let _ = d.to_digit(10).is_some(); - let _ = char::to_digit(c, 10).is_some(); + let _ = d.to_digit(8).is_some(); + let _ = char::to_digit(c, 8).is_some(); } diff --git a/tests/ui/to_digit_is_some.stderr b/tests/ui/to_digit_is_some.stderr index 177d3ccd3e23d..10a1b393a3906 100644 --- a/tests/ui/to_digit_is_some.stderr +++ b/tests/ui/to_digit_is_some.stderr @@ -1,16 +1,16 @@ error: use of `.to_digit(..).is_some()` --> $DIR/to_digit_is_some.rs:9:13 | -LL | let _ = d.to_digit(10).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(10)` +LL | let _ = d.to_digit(8).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(8)` | = note: `-D clippy::to-digit-is-some` implied by `-D warnings` error: use of `.to_digit(..).is_some()` --> $DIR/to_digit_is_some.rs:10:13 | -LL | let _ = char::to_digit(c, 10).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 10)` +LL | let _ = char::to_digit(c, 8).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 8)` error: aborting due to 2 previous errors From 19054251d350ccc81d4fe803821a7695b4518094 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 8 Apr 2022 00:36:09 -0400 Subject: [PATCH 011/129] Update documentation for `mut_from_ref` --- clippy_lints/src/ptr.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 4ecde8f49581e..aa7222266eb5a 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -89,19 +89,26 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// This lint checks for functions that take immutable - /// references and return mutable ones. + /// This lint checks for functions that take immutable references and return + /// mutable ones. This will not trigger if no unsafe code exists as there + /// are multiple safe functions which will do this transformation + /// + /// To be on the conservative side, if there's at least one mutable + /// reference with the output lifetime, this lint will not trigger. /// /// ### Why is this bad? - /// This is trivially unsound, as one can create two - /// mutable references from the same (immutable!) source. - /// This [error](https://github.com/rust-lang/rust/issues/39465) - /// actually lead to an interim Rust release 1.15.1. + /// Creating a mutable reference which can be repeatably derived from an + /// immutable reference is unsound as it allows creating multiple live + /// mutable references to the same object. + /// + /// This [error](https://github.com/rust-lang/rust/issues/39465) actually + /// lead to an interim Rust release 1.15.1. /// /// ### Known problems - /// To be on the conservative side, if there's at least one - /// mutable reference with the output lifetime, this lint will not trigger. - /// In practice, this case is unlikely anyway. + /// This pattern is used by memory allocators to allow allocating multiple + /// objects while returning mutable references to each one. So long as + /// different mutable references are returned each time such a function may + /// be safe. /// /// ### Example /// ```ignore From 71131351de6dae8b2baa39ab2a0b0fd20fa5e4a6 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 7 Apr 2022 18:39:59 +0100 Subject: [PATCH 012/129] Merge commit '984330a6ee3c4d15626685d6dc8b7b759ff630bd' into clippyup --- CHANGELOG.md | 146 +++- Cargo.toml | 9 +- clippy_dev/Cargo.toml | 8 +- clippy_dev/src/bless.rs | 13 +- clippy_dev/src/lib.rs | 17 + clippy_dev/src/lint.rs | 63 +- clippy_dev/src/main.rs | 20 +- clippy_dev/src/new_lint.rs | 24 +- clippy_dev/src/update_lints.rs | 654 ++++++++---------- clippy_lints/Cargo.toml | 2 +- .../src/casts/cast_abs_to_unsigned.rs | 42 ++ clippy_lints/src/casts/cast_ptr_alignment.rs | 100 ++- clippy_lints/src/casts/mod.rs | 27 +- clippy_lints/src/casts/unnecessary_cast.rs | 14 +- clippy_lints/src/crate_in_macro_def.rs | 125 ++++ clippy_lints/src/doc.rs | 4 +- clippy_lints/src/drop_forget_ref.rs | 181 +++-- .../src/empty_structs_with_brackets.rs | 99 +++ clippy_lints/src/identity_op.rs | 37 +- clippy_lints/src/indexing_slicing.rs | 8 + clippy_lints/src/lib.register_all.rs | 9 +- clippy_lints/src/lib.register_complexity.rs | 2 +- clippy_lints/src/lib.register_correctness.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 10 +- clippy_lints/src/lib.register_restriction.rs | 1 + clippy_lints/src/lib.register_style.rs | 1 + clippy_lints/src/lib.register_suspicious.rs | 4 + clippy_lints/src/lib.rs | 11 +- clippy_lints/src/loops/single_element_loop.rs | 70 +- clippy_lints/src/map_unit_fn.rs | 23 +- clippy_lints/src/matches/mod.rs | 2 +- clippy_lints/src/matches/needless_match.rs | 160 +++-- clippy_lints/src/methods/bytes_nth.rs | 2 +- clippy_lints/src/methods/err_expect.rs | 60 ++ clippy_lints/src/methods/implicit_clone.rs | 12 +- .../src/methods/iter_overeager_cloned.rs | 16 +- clippy_lints/src/methods/map_identity.rs | 3 +- clippy_lints/src/methods/mod.rs | 66 +- .../src/methods/needless_option_as_deref.rs | 37 + clippy_lints/src/module_style.rs | 66 +- clippy_lints/src/needless_option_as_deref.rs | 65 -- clippy_lints/src/panic_unimplemented.rs | 4 + clippy_lints/src/ptr.rs | 4 +- clippy_lints/src/transmute/mod.rs | 9 +- .../src/transmute/transmute_int_to_char.rs | 3 +- .../src/transmute/transmute_ref_to_ref.rs | 14 +- .../src/undocumented_unsafe_blocks.rs | 309 ++++----- clippy_lints/src/undropped_manually_drops.rs | 59 -- clippy_lints/src/use_self.rs | 4 +- clippy_lints/src/utils/conf.rs | 2 +- clippy_lints/src/utils/internal_lints.rs | 15 +- .../internal_lints/metadata_collector.rs | 2 +- clippy_utils/Cargo.toml | 2 +- clippy_utils/src/lib.rs | 17 +- clippy_utils/src/msrvs.rs | 4 +- clippy_utils/src/paths.rs | 2 + doc/release.md | 21 + rust-toolchain | 2 +- src/driver.rs | 3 +- tests/{fmt.rs => check-fmt.rs} | 0 .../module_style/fail_mod/src/main.stderr | 8 +- .../module_style/fail_no_mod/src/main.stderr | 4 +- .../check_clippy_version_attribute.stderr | 5 - tests/ui-toml/struct_excessive_bools/test.rs | 2 +- tests/ui/auxiliary/proc_macro_unsafe.rs | 18 + tests/ui/bytes_nth.stderr | 6 +- ...se_sensitive_file_extension_comparisons.rs | 2 +- tests/ui/cast.rs | 2 +- tests/ui/cast_abs_to_unsigned.fixed | 8 + tests/ui/cast_abs_to_unsigned.rs | 8 + tests/ui/cast_abs_to_unsigned.stderr | 10 + tests/ui/cast_alignment.rs | 14 + tests/ui/cast_alignment.stderr | 8 +- tests/ui/crashes/ice-2774.rs | 2 +- tests/ui/crashes/ice-6179.rs | 2 +- tests/ui/crashes/ice-6792.rs | 2 +- tests/ui/crashes/ice-7868.stderr | 6 +- .../crashes/needless_lifetimes_impl_trait.rs | 2 +- tests/ui/crashes/regressions.rs | 2 +- tests/ui/crate_in_macro_def.fixed | 56 ++ tests/ui/crate_in_macro_def.rs | 56 ++ tests/ui/crate_in_macro_def.stderr | 10 + tests/ui/default_numeric_fallback_f64.fixed | 2 +- tests/ui/default_numeric_fallback_f64.rs | 2 +- tests/ui/default_numeric_fallback_i32.fixed | 2 +- tests/ui/default_numeric_fallback_i32.rs | 2 +- tests/ui/drop_forget_copy.rs | 2 +- tests/ui/drop_forget_copy.stderr | 12 +- tests/ui/drop_non_drop.rs | 40 ++ tests/ui/drop_non_drop.stderr | 27 + tests/ui/drop_ref.rs | 2 +- tests/ui/empty_structs_with_brackets.fixed | 25 + tests/ui/empty_structs_with_brackets.rs | 25 + tests/ui/empty_structs_with_brackets.stderr | 19 + tests/ui/err_expect.fixed | 14 + tests/ui/err_expect.rs | 14 + tests/ui/err_expect.stderr | 10 + tests/ui/fn_params_excessive_bools.rs | 2 +- tests/ui/forget_non_drop.rs | 27 + tests/ui/forget_non_drop.stderr | 27 + tests/ui/forget_ref.rs | 2 +- tests/ui/identity_op.rs | 9 + tests/ui/identity_op.stderr | 32 +- tests/ui/implicit_clone.rs | 4 +- tests/ui/indexing_slicing_index.rs | 24 +- tests/ui/indexing_slicing_index.stderr | 27 +- tests/ui/iter_nth_zero.fixed | 2 +- tests/ui/iter_nth_zero.rs | 2 +- tests/ui/iter_overeager_cloned.fixed | 6 + tests/ui/iter_overeager_cloned.rs | 6 + tests/ui/iter_overeager_cloned.stderr | 14 +- tests/ui/large_types_passed_by_value.rs | 2 +- tests/ui/let_and_return.rs | 2 +- tests/ui/let_underscore_must_use.rs | 2 +- tests/ui/manual_async_fn.fixed | 2 +- tests/ui/manual_async_fn.rs | 2 +- tests/ui/manual_unwrap_or.fixed | 2 +- tests/ui/manual_unwrap_or.rs | 2 +- tests/ui/map_identity.fixed | 2 + tests/ui/map_identity.rs | 2 + tests/ui/map_identity.stderr | 8 +- tests/ui/map_unit_fn.rs | 2 +- tests/ui/min_rust_version_attr.rs | 14 +- tests/ui/min_rust_version_attr.stderr | 8 +- tests/ui/missing_inline.rs | 6 +- tests/ui/module_name_repetitions.rs | 2 +- tests/ui/module_name_repetitions.stderr | 4 +- tests/ui/modulo_arithmetic_integral_const.rs | 7 +- .../modulo_arithmetic_integral_const.stderr | 34 +- .../needless_arbitrary_self_type_unfixable.rs | 4 +- tests/ui/needless_lifetimes.rs | 2 +- tests/ui/needless_match.fixed | 170 ++++- tests/ui/needless_match.rs | 202 ++++-- tests/ui/needless_match.stderr | 87 ++- tests/ui/needless_option_as_deref.fixed | 30 +- tests/ui/needless_option_as_deref.rs | 30 +- tests/ui/needless_option_as_deref.stderr | 12 +- tests/ui/no_effect.rs | 2 +- tests/ui/option_map_unit_fn_fixable.fixed | 5 +- tests/ui/option_map_unit_fn_fixable.rs | 5 +- tests/ui/option_map_unit_fn_fixable.stderr | 12 +- tests/ui/or_then_unwrap.fixed | 4 +- tests/ui/or_then_unwrap.rs | 4 +- tests/ui/panicking_macros.rs | 17 +- tests/ui/panicking_macros.stderr | 32 +- tests/ui/ptr_arg.rs | 2 +- tests/ui/recursive_format_impl.rs | 24 +- tests/ui/redundant_allocation.rs | 2 +- tests/ui/redundant_allocation_fixable.fixed | 2 +- tests/ui/redundant_allocation_fixable.rs | 2 +- tests/ui/redundant_clone.fixed | 2 +- tests/ui/redundant_clone.rs | 2 +- tests/ui/redundant_static_lifetimes.fixed | 2 +- tests/ui/redundant_static_lifetimes.rs | 2 +- tests/ui/result_map_unit_fn_fixable.fixed | 2 + tests/ui/result_map_unit_fn_fixable.rs | 2 + tests/ui/result_map_unit_fn_fixable.stderr | 10 +- tests/ui/same_item_push.rs | 2 +- tests/ui/single_element_loop.fixed | 24 +- tests/ui/single_element_loop.rs | 20 +- tests/ui/single_element_loop.stderr | 74 +- tests/ui/trait_duplication_in_bounds.rs | 4 +- tests/ui/transmute.rs | 11 +- tests/ui/transmute.stderr | 54 +- tests/ui/undocumented_unsafe_blocks.rs | 55 +- tests/ui/undocumented_unsafe_blocks.stderr | 148 ++-- tests/ui/unnecessary_cast.rs | 6 + tests/ui/unnecessary_cast_fixable.fixed | 5 + tests/ui/unnecessary_cast_fixable.rs | 5 + tests/ui/unsafe_derive_deserialize.rs | 14 +- tests/ui/unsafe_removed_from_name.rs | 4 +- tests/ui/unused_self.rs | 10 +- tests/ui/use_self.fixed | 22 +- tests/ui/use_self.rs | 22 +- tests/ui/useless_attribute.fixed | 2 +- tests/ui/useless_attribute.rs | 2 +- tests/versioncheck.rs | 34 +- 177 files changed, 3113 insertions(+), 1486 deletions(-) create mode 100644 clippy_lints/src/casts/cast_abs_to_unsigned.rs create mode 100644 clippy_lints/src/crate_in_macro_def.rs create mode 100644 clippy_lints/src/empty_structs_with_brackets.rs create mode 100644 clippy_lints/src/methods/err_expect.rs create mode 100644 clippy_lints/src/methods/needless_option_as_deref.rs delete mode 100644 clippy_lints/src/needless_option_as_deref.rs delete mode 100644 clippy_lints/src/undropped_manually_drops.rs rename tests/{fmt.rs => check-fmt.rs} (100%) create mode 100644 tests/ui/auxiliary/proc_macro_unsafe.rs create mode 100644 tests/ui/cast_abs_to_unsigned.fixed create mode 100644 tests/ui/cast_abs_to_unsigned.rs create mode 100644 tests/ui/cast_abs_to_unsigned.stderr create mode 100644 tests/ui/crate_in_macro_def.fixed create mode 100644 tests/ui/crate_in_macro_def.rs create mode 100644 tests/ui/crate_in_macro_def.stderr create mode 100644 tests/ui/drop_non_drop.rs create mode 100644 tests/ui/drop_non_drop.stderr create mode 100644 tests/ui/empty_structs_with_brackets.fixed create mode 100644 tests/ui/empty_structs_with_brackets.rs create mode 100644 tests/ui/empty_structs_with_brackets.stderr create mode 100644 tests/ui/err_expect.fixed create mode 100644 tests/ui/err_expect.rs create mode 100644 tests/ui/err_expect.stderr create mode 100644 tests/ui/forget_non_drop.rs create mode 100644 tests/ui/forget_non_drop.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 88f71931d92b5..b4097ea86a518 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,143 @@ document. ## Unreleased / In Rust Nightly -[0eff589...master](https://github.com/rust-lang/rust-clippy/compare/0eff589...master) +[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master) -## Rust 1.59 (beta) +## Rust 1.60 -Current beta, release 2022-02-24 +Current stable, released 2022-04-07 + +[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b) + +### New Lints + +* [`single_char_lifetime_names`] + [#8236](https://github.com/rust-lang/rust-clippy/pull/8236) +* [`iter_overeager_cloned`] + [#8203](https://github.com/rust-lang/rust-clippy/pull/8203) +* [`transmute_undefined_repr`] + [#8398](https://github.com/rust-lang/rust-clippy/pull/8398) +* [`default_union_representation`] + [#8289](https://github.com/rust-lang/rust-clippy/pull/8289) +* [`manual_bits`] + [#8213](https://github.com/rust-lang/rust-clippy/pull/8213) +* [`borrow_as_ptr`] + [#8210](https://github.com/rust-lang/rust-clippy/pull/8210) + +### Moves and Deprecations + +* Moved [`disallowed_methods`] and [`disallowed_types`] to `style` (now warn-by-default) + [#8261](https://github.com/rust-lang/rust-clippy/pull/8261) +* Rename `ref_in_deref` to [`needless_borrow`] + [#8217](https://github.com/rust-lang/rust-clippy/pull/8217) +* Moved [`mutex_atomic`] to `nursery` (now allow-by-default) + [#8260](https://github.com/rust-lang/rust-clippy/pull/8260) + +### Enhancements + +* [`ptr_arg`]: Now takes the argument usage into account and lints for mutable references + [#8271](https://github.com/rust-lang/rust-clippy/pull/8271) +* [`unused_io_amount`]: Now supports async read and write traits + [#8179](https://github.com/rust-lang/rust-clippy/pull/8179) +* [`while_let_on_iterator`]: Improved detection to catch more cases + [#8221](https://github.com/rust-lang/rust-clippy/pull/8221) +* [`trait_duplication_in_bounds`]: Now covers trait functions with `Self` bounds + [#8252](https://github.com/rust-lang/rust-clippy/pull/8252) +* [`unwrap_used`]: Now works for `.get(i).unwrap()` and `.get_mut(i).unwrap()` + [#8372](https://github.com/rust-lang/rust-clippy/pull/8372) +* [`map_clone`]: The suggestion takes `msrv` into account + [#8280](https://github.com/rust-lang/rust-clippy/pull/8280) +* [`manual_bits`] and [`borrow_as_ptr`]: Now track the `clippy::msrv` attribute + [#8280](https://github.com/rust-lang/rust-clippy/pull/8280) +* [`disallowed_methods`]: Now works for methods on primitive types + [#8112](https://github.com/rust-lang/rust-clippy/pull/8112) +* [`not_unsafe_ptr_arg_deref`]: Now works for type aliases + [#8273](https://github.com/rust-lang/rust-clippy/pull/8273) +* [`needless_question_mark`]: Now works for async functions + [#8311](https://github.com/rust-lang/rust-clippy/pull/8311) +* [`iter_not_returning_iterator`]: Now handles type projections + [#8228](https://github.com/rust-lang/rust-clippy/pull/8228) +* [`wrong_self_convention`]: Now detects wrong `self` references in more cases + [#8208](https://github.com/rust-lang/rust-clippy/pull/8208) +* [`single_match`]: Now works for `match` statements with tuples + [#8322](https://github.com/rust-lang/rust-clippy/pull/8322) + +### False Positive Fixes + +* [`erasing_op`]: No longer triggers if the output type changes + [#8204](https://github.com/rust-lang/rust-clippy/pull/8204) +* [`if_same_then_else`]: No longer triggers for `if let` statements + [#8297](https://github.com/rust-lang/rust-clippy/pull/8297) +* [`manual_memcpy`]: No longer lints on `VecDeque` + [#8226](https://github.com/rust-lang/rust-clippy/pull/8226) +* [`trait_duplication_in_bounds`]: Now takes path segments into account + [#8315](https://github.com/rust-lang/rust-clippy/pull/8315) +* [`deref_addrof`]: No longer lints when the dereference or borrow occurs in different a context + [#8268](https://github.com/rust-lang/rust-clippy/pull/8268) +* [`type_repetition_in_bounds`]: Now checks for full equality to prevent false positives + [#8224](https://github.com/rust-lang/rust-clippy/pull/8224) +* [`ptr_arg`]: No longer lint for mutable references in traits + [#8369](https://github.com/rust-lang/rust-clippy/pull/8369) +* [`implicit_clone`]: No longer lints for double references + [#8231](https://github.com/rust-lang/rust-clippy/pull/8231) +* [`needless_lifetimes`]: No longer lints lifetimes for explicit `self` types + [#8278](https://github.com/rust-lang/rust-clippy/pull/8278) +* [`op_ref`]: No longer lints in `BinOp` impl if that can cause recursion + [#8298](https://github.com/rust-lang/rust-clippy/pull/8298) +* [`enum_variant_names`]: No longer triggers for empty variant names + [#8329](https://github.com/rust-lang/rust-clippy/pull/8329) +* [`redundant_closure`]: No longer lints for `Arc` or `Rc` + [#8193](https://github.com/rust-lang/rust-clippy/pull/8193) +* [`iter_not_returning_iterator`]: No longer lints on trait implementations but therefore on trait definitions + [#8228](https://github.com/rust-lang/rust-clippy/pull/8228) +* [`single_match`]: No longer lints on exhaustive enum patterns without a wildcard + [#8322](https://github.com/rust-lang/rust-clippy/pull/8322) +* [`manual_swap`]: No longer lints on cases that involve automatic dereferences + [#8220](https://github.com/rust-lang/rust-clippy/pull/8220) +* [`useless_format`]: Now works for implicit named arguments + [#8295](https://github.com/rust-lang/rust-clippy/pull/8295) + +### Suggestion Fixes/Improvements + +* [`needless_borrow`]: Prevent mutable borrows being moved and suggest removing the borrow on method calls + [#8217](https://github.com/rust-lang/rust-clippy/pull/8217) +* [`chars_next_cmp`]: Correctly excapes the suggestion + [#8376](https://github.com/rust-lang/rust-clippy/pull/8376) +* [`explicit_write`]: Add suggestions for `write!`s with format arguments + [#8365](https://github.com/rust-lang/rust-clippy/pull/8365) +* [`manual_memcpy`]: Suggests `copy_from_slice` when applicable + [#8226](https://github.com/rust-lang/rust-clippy/pull/8226) +* [`or_fun_call`]: Improved suggestion display for long arguments + [#8292](https://github.com/rust-lang/rust-clippy/pull/8292) +* [`unnecessary_cast`]: Now correctly includes the sign + [#8350](https://github.com/rust-lang/rust-clippy/pull/8350) +* [`cmp_owned`]: No longer flips the comparison order + [#8299](https://github.com/rust-lang/rust-clippy/pull/8299) +* [`explicit_counter_loop`]: Now correctly suggests `iter()` on references + [#8382](https://github.com/rust-lang/rust-clippy/pull/8382) + +### ICE Fixes + +* [`manual_split_once`] + [#8250](https://github.com/rust-lang/rust-clippy/pull/8250) + +### Documentation Improvements + +* [`map_flatten`]: Add documentation for the `Option` type + [#8354](https://github.com/rust-lang/rust-clippy/pull/8354) +* Document that Clippy's driver might use a different code generation than rustc + [#8037](https://github.com/rust-lang/rust-clippy/pull/8037) +* Clippy's lint list will now automatically focus the search box + [#8343](https://github.com/rust-lang/rust-clippy/pull/8343) + +### Others + +* Clippy now warns if we find multiple Clippy config files exist + [#8326](https://github.com/rust-lang/rust-clippy/pull/8326) + +## Rust 1.59 + +Released 2022-02-24 [e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589) @@ -174,7 +306,7 @@ Current beta, release 2022-02-24 ## Rust 1.58 -Current stable, released 2022-01-13 +Released 2022-01-13 [00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011) @@ -3069,6 +3201,7 @@ Released 2018-09-13 [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons +[`cast_abs_to_unsigned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned [`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor [`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless @@ -3097,6 +3230,7 @@ Released 2018-09-13 [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator +[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def [`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute [`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro @@ -3123,6 +3257,7 @@ Released 2018-09-13 [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens [`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy +[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec @@ -3130,12 +3265,14 @@ Released 2018-09-13 [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop +[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets [`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant [`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use [`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names [`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op [`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op +[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums @@ -3174,6 +3311,7 @@ Released 2018-09-13 [`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map [`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy +[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect diff --git a/Cargo.toml b/Cargo.toml index 123af23881b62..dd6518d5241b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.61" +version = "0.1.62" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -21,13 +21,12 @@ name = "clippy-driver" path = "src/driver.rs" [dependencies] -clippy_lints = { version = "0.1", path = "clippy_lints" } +clippy_lints = { path = "clippy_lints" } semver = "1.0" -rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } +rustc_tools_util = { path = "rustc_tools_util" } tempfile = { version = "3.2", optional = true } [dev-dependencies] -cargo_metadata = "0.14" compiletest_rs = { version = "0.7.1", features = ["tmp"] } tester = "0.9" regex = "1.5" @@ -45,7 +44,7 @@ derive-new = "0.5" if_chain = "1.0" itertools = "0.10.1" quote = "1.0" -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0.125", features = ["derive"] } syn = { version = "1.0", features = ["full"] } futures = "0.3" parking_lot = "0.11.2" diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index d133e8cddabc7..c2ebba0683ca9 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -4,15 +4,17 @@ version = "0.0.1" edition = "2021" [dependencies] -bytecount = "0.6" clap = "2.33" indoc = "1.0" itertools = "0.10.1" opener = "0.5" -regex = "1.5" shell-escape = "0.1" +tempfile = "3.3" walkdir = "2.3" -cargo_metadata = "0.14" [features] deny-warnings = [] + +[package.metadata.rust-analyzer] +# This package uses #[feature(rustc_private)] +rustc_private = true diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs index b0fb39e816996..8e5c739afe05a 100644 --- a/clippy_dev/src/bless.rs +++ b/clippy_dev/src/bless.rs @@ -1,22 +1,15 @@ //! `bless` updates the reference files in the repo with changed output files //! from the last test run. +use crate::cargo_clippy_path; use std::ffi::OsStr; use std::fs; use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; -#[cfg(not(windows))] -static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; -#[cfg(windows)] -static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; - -static CLIPPY_BUILD_TIME: SyncLazy> = SyncLazy::new(|| { - let mut path = std::env::current_exe().unwrap(); - path.set_file_name(CARGO_CLIPPY_EXE); - fs::metadata(path).ok()?.modified().ok() -}); +static CLIPPY_BUILD_TIME: SyncLazy> = + SyncLazy::new(|| cargo_clippy_path().metadata().ok()?.modified().ok()); /// # Panics /// diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 59fde44754714..9c6d754b400fc 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,8 +1,12 @@ +#![feature(let_else)] #![feature(once_cell)] +#![feature(rustc_private)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] +extern crate rustc_lexer; + use std::path::PathBuf; pub mod bless; @@ -13,6 +17,19 @@ pub mod serve; pub mod setup; pub mod update_lints; +#[cfg(not(windows))] +static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; +#[cfg(windows)] +static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; + +/// Returns the path to the `cargo-clippy` binary +#[must_use] +pub fn cargo_clippy_path() -> PathBuf { + let mut path = std::env::current_exe().expect("failed to get current executable name"); + path.set_file_name(CARGO_CLIPPY_EXE); + path +} + /// Returns the path to the Clippy project directory /// /// # Panics diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index b8287980a4bac..1bc1a39542db5 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -1,19 +1,52 @@ -use std::process::{self, Command}; +use crate::cargo_clippy_path; +use std::process::{self, Command, ExitStatus}; +use std::{fs, io}; -pub fn run(filename: &str) { - let code = Command::new("cargo") - .args(["run", "--bin", "clippy-driver", "--"]) - .args(["-L", "./target/debug"]) - .args(["-Z", "no-codegen"]) - .args(["--edition", "2021"]) - .arg(filename) - .status() - .expect("failed to run cargo") - .code(); - - if code.is_none() { - eprintln!("Killed by signal"); +fn exit_if_err(status: io::Result) { + match status.expect("failed to run command").code() { + Some(0) => {}, + Some(n) => process::exit(n), + None => { + eprintln!("Killed by signal"); + process::exit(1); + }, } +} + +pub fn run(path: &str) { + let is_file = match fs::metadata(path) { + Ok(metadata) => metadata.is_file(), + Err(e) => { + eprintln!("Failed to read {path}: {e:?}"); + process::exit(1); + }, + }; + + if is_file { + exit_if_err( + Command::new("cargo") + .args(["run", "--bin", "clippy-driver", "--"]) + .args(["-L", "./target/debug"]) + .args(["-Z", "no-codegen"]) + .args(["--edition", "2021"]) + .arg(path) + .status(), + ); + } else { + exit_if_err(Command::new("cargo").arg("build").status()); - process::exit(code.unwrap_or(1)); + // Run in a tempdir as changes to clippy do not retrigger linting + let target = tempfile::Builder::new() + .prefix("clippy") + .tempdir() + .expect("failed to create tempdir"); + + let status = Command::new(cargo_clippy_path()) + .current_dir(path) + .env("CARGO_TARGET_DIR", target.as_ref()) + .status(); + + target.close().expect("failed to remove tempdir"); + exit_if_err(status); + } } diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 30a241c8ba151..b1fe35a0243f0 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -4,6 +4,7 @@ use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints}; +use indoc::indoc; fn main() { let matches = get_clap_config(); @@ -56,8 +57,8 @@ fn main() { serve::run(port, lint); }, ("lint", Some(matches)) => { - let filename = matches.value_of("filename").unwrap(); - lint::run(filename); + let path = matches.value_of("path").unwrap(); + lint::run(path); }, _ => {}, } @@ -225,11 +226,20 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { ) .subcommand( SubCommand::with_name("lint") - .about("Manually run clippy on a file") + .about("Manually run clippy on a file or package") + .after_help(indoc! {" + EXAMPLES + Lint a single file: + cargo dev lint tests/ui/attrs.rs + + Lint a package directory: + cargo dev lint tests/ui-cargo/wildcard_dependencies/fail + cargo dev lint ~/my-project + "}) .arg( - Arg::with_name("filename") + Arg::with_name("path") .required(true) - .help("The path to a file to lint"), + .help("The path to a file or package directory to lint"), ), ) .get_matches() diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 59658b42c79b1..7a3fd1317619e 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -133,15 +133,23 @@ fn to_camel_case(name: &str) -> String { } fn get_stabilisation_version() -> String { - let mut command = cargo_metadata::MetadataCommand::new(); - command.no_deps(); - if let Ok(metadata) = command.exec() { - if let Some(pkg) = metadata.packages.iter().find(|pkg| pkg.name == "clippy") { - return format!("{}.{}.0", pkg.version.minor, pkg.version.patch); - } + fn parse_manifest(contents: &str) -> Option { + let version = contents + .lines() + .filter_map(|l| l.split_once('=')) + .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?; + let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else { + return None; + }; + let (minor, patch) = version.split_once('.')?; + Some(format!( + "{}.{}.0", + minor.parse::().ok()?, + patch.parse::().ok()? + )) } - - String::from("") + let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`"); + parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`") } fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index d368ef1f46a2a..59db51fbfac51 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,9 +1,9 @@ +use core::fmt::Write; use itertools::Itertools; -use regex::Regex; +use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use std::collections::HashMap; use std::ffi::OsStr; use std::fs; -use std::lazy::SyncLazy; use std::path::Path; use walkdir::WalkDir; @@ -13,35 +13,7 @@ const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev u // Use that command to update this file and do not edit by hand.\n\ // Manual edits will be overwritten.\n\n"; -static DEC_CLIPPY_LINT_RE: SyncLazy = SyncLazy::new(|| { - Regex::new( - r#"(?x) - declare_clippy_lint!\s*[\{(] - (?:\s+///.*)* - (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])? - \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* - (?P[a-z_]+)\s*,\s* - "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] -"#, - ) - .unwrap() -}); - -static DEC_DEPRECATED_LINT_RE: SyncLazy = SyncLazy::new(|| { - Regex::new( - r#"(?x) - declare_deprecated_lint!\s*[{(]\s* - (?:\s+///.*)* - (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])? - \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* - "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] -"#, - ) - .unwrap() -}); -static NL_ESCAPE_RE: SyncLazy = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap()); - -static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; +const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; #[derive(Clone, Copy, PartialEq)] pub enum UpdateMode { @@ -60,60 +32,52 @@ pub enum UpdateMode { /// Panics if a file path could not read from or then written to #[allow(clippy::too_many_lines)] pub fn run(update_mode: UpdateMode) { - let lint_list: Vec = gather_all().collect(); + let (lints, deprecated_lints) = gather_all(); - let internal_lints = Lint::internal_lints(&lint_list); - let deprecated_lints = Lint::deprecated_lints(&lint_list); - let usable_lints = Lint::usable_lints(&lint_list); + let internal_lints = Lint::internal_lints(&lints); + let usable_lints = Lint::usable_lints(&lints); let mut sorted_usable_lints = usable_lints.clone(); sorted_usable_lints.sort_by_key(|lint| lint.name.clone()); - let usable_lint_count = round_to_fifty(usable_lints.len()); - - let mut file_change = false; - - file_change |= replace_region_in_file( + replace_region_in_file( + update_mode, Path::new("README.md"), - &format!( - r#"\[There are over \d+ lints included in this crate!\]\({}\)"#, - DOCS_LINK - ), - "", - true, - update_mode == UpdateMode::Change, - || { - vec![format!( - "[There are over {} lints included in this crate!]({})", - usable_lint_count, DOCS_LINK - )] + "[There are over ", + " lints included in this crate!]", + |res| { + write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap(); }, - ) - .changed; + ); - file_change |= replace_region_in_file( + replace_region_in_file( + update_mode, Path::new("CHANGELOG.md"), - "", + "\n", "", - false, - update_mode == UpdateMode::Change, - || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())), - ) - .changed; + |res| { + for lint in usable_lints + .iter() + .map(|l| &l.name) + .chain(deprecated_lints.iter().map(|l| &l.name)) + .sorted() + { + writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap(); + } + }, + ); // This has to be in lib.rs, otherwise rustfmt doesn't work - file_change |= replace_region_in_file( + replace_region_in_file( + update_mode, Path::new("clippy_lints/src/lib.rs"), - "begin lints modules", - "end lints modules", - false, - update_mode == UpdateMode::Change, - || gen_modules_list(usable_lints.iter()), - ) - .changed; - - if file_change && update_mode == UpdateMode::Check { - exit_with_failure(); - } + "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n", + "// end lints modules, do not remove this comment, it’s used in `update_lints`", + |res| { + for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() { + writeln!(res, "mod {};", lint_mod).unwrap(); + } + }, + ); process_file( "clippy_lints/src/lib.register_lints.rs", @@ -123,7 +87,7 @@ pub fn run(update_mode: UpdateMode) { process_file( "clippy_lints/src/lib.deprecated.rs", update_mode, - &gen_deprecated(deprecated_lints.iter()), + &gen_deprecated(&deprecated_lints), ); let all_group_lints = usable_lints.iter().filter(|l| { @@ -146,15 +110,12 @@ pub fn run(update_mode: UpdateMode) { } pub fn print_lints() { - let lint_list: Vec = gather_all().collect(); + let (lint_list, _) = gather_all(); let usable_lints = Lint::usable_lints(&lint_list); let usable_lint_count = usable_lints.len(); let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); for (lint_group, mut lints) in grouped_by_lint_group { - if lint_group == "Deprecated" { - continue; - } println!("\n## {}", lint_group); lints.sort_by_key(|l| l.name.clone()); @@ -198,19 +159,17 @@ struct Lint { name: String, group: String, desc: String, - deprecation: Option, module: String, } impl Lint { #[must_use] - fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self { + fn new(name: &str, group: &str, desc: &str, module: &str) -> Self { Self { name: name.to_lowercase(), - group: group.to_string(), - desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(), - deprecation: deprecation.map(ToString::to_string), - module: module.to_string(), + group: group.into(), + desc: remove_line_splices(desc), + module: module.into(), } } @@ -219,7 +178,7 @@ impl Lint { fn usable_lints(lints: &[Self]) -> Vec { lints .iter() - .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal")) + .filter(|l| !l.group.starts_with("internal")) .cloned() .collect() } @@ -230,12 +189,6 @@ impl Lint { lints.iter().filter(|l| l.group == "internal").cloned().collect() } - /// Returns all deprecated lints - #[must_use] - fn deprecated_lints(lints: &[Self]) -> Vec { - lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect() - } - /// Returns the lints in a `HashMap`, grouped by the different lint groups #[must_use] fn by_lint_group(lints: impl Iterator) -> HashMap> { @@ -243,6 +196,20 @@ impl Lint { } } +#[derive(Clone, PartialEq, Debug)] +struct DeprecatedLint { + name: String, + reason: String, +} +impl DeprecatedLint { + fn new(name: &str, reason: &str) -> Self { + Self { + name: name.to_lowercase(), + reason: remove_line_splices(reason), + } + } +} + /// Generates the code for registering a group fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator) -> String { let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect(); @@ -262,32 +229,12 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator(lints: impl Iterator) -> Vec { - lints - .map(|l| &l.module) - .unique() - .map(|module| format!("mod {};", module)) - .sorted() - .collect::>() -} - -/// Generates the list of lint links at the bottom of the CHANGELOG -#[must_use] -fn gen_changelog_lint_list<'a>(lints: impl Iterator) -> Vec { - lints - .sorted_by_key(|l| &l.name) - .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name)) - .collect() -} - /// Generates the `register_removed` code #[must_use] -fn gen_deprecated<'a>(lints: impl Iterator) -> String { +fn gen_deprecated(lints: &[DeprecatedLint]) -> String { let mut output = GENERATED_FILE_COMMENT.to_string(); output.push_str("{\n"); - for Lint { name, deprecation, .. } in lints { + for lint in lints { output.push_str(&format!( concat!( " store.register_removed(\n", @@ -295,8 +242,7 @@ fn gen_deprecated<'a>(lints: impl Iterator) -> String { " \"{}\",\n", " );\n" ), - name, - deprecation.as_ref().expect("`lints` are deprecated") + lint.name, lint.reason, )); } output.push_str("}\n"); @@ -330,61 +276,136 @@ fn gen_register_lint_list<'a>( output } -/// Gathers all files in `src/clippy_lints` and gathers all lints inside -fn gather_all() -> impl Iterator { - lint_files().flat_map(|f| gather_from_file(&f)) -} +/// Gathers all lints defined in `clippy_lints/src` +fn gather_all() -> (Vec, Vec) { + let mut lints = Vec::with_capacity(1000); + let mut deprecated_lints = Vec::with_capacity(50); + let root_path = clippy_project_root().join("clippy_lints/src"); -fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator { - let content = fs::read_to_string(dir_entry.path()).unwrap(); - let path = dir_entry.path(); - let filename = path.file_stem().unwrap(); - let path_buf = path.with_file_name(filename); - let mut rel_path = path_buf - .strip_prefix(clippy_project_root().join("clippy_lints/src")) - .expect("only files in `clippy_lints/src` should be looked at"); - // If the lints are stored in mod.rs, we get the module name from - // the containing directory: - if filename == "mod" { - rel_path = rel_path.parent().unwrap(); - } + for (rel_path, file) in WalkDir::new(&root_path) + .into_iter() + .map(Result::unwrap) + .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) + .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) + { + let path = file.path(); + let contents = + fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); + let module = rel_path + .components() + .map(|c| c.as_os_str().to_str().unwrap()) + .collect::>() + .join("::"); + + // If the lints are stored in mod.rs, we get the module name from + // the containing directory: + let module = if let Some(module) = module.strip_suffix("::mod.rs") { + module + } else { + module.strip_suffix(".rs").unwrap_or(&module) + }; - let module = rel_path - .components() - .map(|c| c.as_os_str().to_str().unwrap()) - .collect::>() - .join("::"); + if module == "deprecated_lints" { + parse_deprecated_contents(&contents, &mut deprecated_lints); + } else { + parse_contents(&contents, module, &mut lints); + } + } + (lints, deprecated_lints) +} - parse_contents(&content, &module) +macro_rules! match_tokens { + ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => { + { + $($(let $capture =)? if let Some((TokenKind::$token $({$($fields)*})?, _x)) = $iter.next() { + _x + } else { + continue; + };)* + #[allow(clippy::unused_unit)] + { ($($($capture,)?)*) } + } + } } -fn parse_contents(content: &str, module: &str) -> impl Iterator { - let lints = DEC_CLIPPY_LINT_RE - .captures_iter(content) - .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module)); - let deprecated = DEC_DEPRECATED_LINT_RE - .captures_iter(content) - .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module)); - // Removing the `.collect::>().into_iter()` causes some lifetime issues due to the map - lints.chain(deprecated).collect::>().into_iter() +/// Parse a source file looking for `declare_clippy_lint` macro invocations. +fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { + let mut offset = 0usize; + let mut iter = tokenize(contents).map(|t| { + let range = offset..offset + t.len; + offset = range.end; + (t.kind, &contents[range]) + }); + + while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_clippy_lint") { + let mut iter = iter + .by_ref() + .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); + // matches `!{` + match_tokens!(iter, Bang OpenBrace); + match iter.next() { + // #[clippy::version = "version"] pub + Some((TokenKind::Pound, _)) => { + match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident); + }, + // pub + Some((TokenKind::Ident, _)) => (), + _ => continue, + } + let (name, group, desc) = match_tokens!( + iter, + // LINT_NAME + Ident(name) Comma + // group, + Ident(group) Comma + // "description" } + Literal{..}(desc) CloseBrace + ); + lints.push(Lint::new(name, group, desc, module)); + } } -/// Collects all .rs files in the `clippy_lints/src` directory -fn lint_files() -> impl Iterator { - // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories. - // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`. - let path = clippy_project_root().join("clippy_lints/src"); - WalkDir::new(path) - .into_iter() - .filter_map(Result::ok) - .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) +/// Parse a source file looking for `declare_deprecated_lint` macro invocations. +fn parse_deprecated_contents(contents: &str, lints: &mut Vec) { + let mut offset = 0usize; + let mut iter = tokenize(contents).map(|t| { + let range = offset..offset + t.len; + offset = range.end; + (t.kind, &contents[range]) + }); + while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_deprecated_lint") { + let mut iter = iter + .by_ref() + .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); + let (name, reason) = match_tokens!( + iter, + // !{ + Bang OpenBrace + // #[clippy::version = "version"] + Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket + // pub LINT_NAME, + Ident Ident(name) Comma + // "description" + Literal{kind: LiteralKind::Str{..},..}(reason) + // } + CloseBrace + ); + lints.push(DeprecatedLint::new(name, reason)); + } } -/// Whether a file has had its text changed or not -#[derive(PartialEq, Debug)] -struct FileChange { - changed: bool, - new_lines: String, +/// Removes the line splices and surrounding quotes from a string literal +fn remove_line_splices(s: &str) -> String { + let s = s + .strip_prefix('r') + .unwrap_or(s) + .trim_matches('#') + .strip_prefix('"') + .and_then(|s| s.strip_suffix('"')) + .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s)); + let mut res = String::with_capacity(s.len()); + unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, _| res.push_str(&s[range])); + res } /// Replaces a region in a file delimited by two lines matching regexes. @@ -396,144 +417,49 @@ struct FileChange { /// # Panics /// /// Panics if the path could not read or then written -fn replace_region_in_file( +fn replace_region_in_file( + update_mode: UpdateMode, path: &Path, start: &str, end: &str, - replace_start: bool, - write_back: bool, - replacements: F, -) -> FileChange -where - F: FnOnce() -> Vec, -{ - let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e)); - let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements); - - if write_back { - if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) { - panic!("Cannot write to {}: {}", path.display(), e); - } - } - file_change -} - -/// Replaces a region in a text delimited by two lines matching regexes. -/// -/// * `text` is the input text on which you want to perform the replacement -/// * `start` is a `&str` that describes the delimiter line before the region you want to replace. -/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too. -/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen. -/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too. -/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end` -/// delimiter line is never replaced. -/// * `replacements` is a closure that has to return a `Vec` which contains the new text. -/// -/// If you want to perform the replacement on files instead of already parsed text, -/// use `replace_region_in_file`. -/// -/// # Example -/// -/// ```ignore -/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end"; -/// let result = -/// replace_region_in_text(the_text, "replace_start", "replace_end", false, || { -/// vec!["a different".to_string(), "text".to_string()] -/// }) -/// .new_lines; -/// assert_eq!("replace_start\na different\ntext\nreplace_end", result); -/// ``` -/// -/// # Panics -/// -/// Panics if start or end is not valid regex -fn replace_region_in_text(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange -where - F: FnOnce() -> Vec, -{ - let replace_it = replacements(); - let mut in_old_region = false; - let mut found = false; - let mut new_lines = vec![]; - let start = Regex::new(start).unwrap(); - let end = Regex::new(end).unwrap(); - - for line in text.lines() { - if in_old_region { - if end.is_match(line) { - in_old_region = false; - new_lines.extend(replace_it.clone()); - new_lines.push(line.to_string()); - } - } else if start.is_match(line) { - if !replace_start { - new_lines.push(line.to_string()); + write_replacement: impl FnMut(&mut String), +) { + let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); + let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) { + Ok(x) => x, + Err(delim) => panic!("Couldn't find `{}` in file `{}`", delim, path.display()), + }; + + match update_mode { + UpdateMode::Check if contents != new_contents => exit_with_failure(), + UpdateMode::Check => (), + UpdateMode::Change => { + if let Err(e) = fs::write(path, new_contents.as_bytes()) { + panic!("Cannot write to `{}`: {}", path.display(), e); } - in_old_region = true; - found = true; - } else { - new_lines.push(line.to_string()); - } - } - - if !found { - // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the - // given text or file. Most likely this is an error on the programmer's side and the Regex - // is incorrect. - eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start); - std::process::exit(1); - } - - let mut new_lines = new_lines.join("\n"); - if text.ends_with('\n') { - new_lines.push('\n'); + }, } - let changed = new_lines != text; - FileChange { changed, new_lines } -} - -#[test] -fn test_parse_contents() { - let result: Vec = parse_contents( - r#" -declare_clippy_lint! { - #[clippy::version = "Hello Clippy!"] - pub PTR_ARG, - style, - "really long \ - text" } -declare_clippy_lint!{ - #[clippy::version = "Test version"] - pub DOC_MARKDOWN, - pedantic, - "single line" -} - -/// some doc comment -declare_deprecated_lint! { - #[clippy::version = "I'm a version"] - pub SHOULD_ASSERT_EQ, - "`assert!()` will be more flexible with RFC 2011" -} - "#, - "module_name", - ) - .collect(); - - let expected = vec![ - Lint::new("ptr_arg", "style", "really long text", None, "module_name"), - Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"), - Lint::new( - "should_assert_eq", - "Deprecated", - "`assert!()` will be more flexible with RFC 2011", - Some("`assert!()` will be more flexible with RFC 2011"), - "module_name", - ), - ]; - assert_eq!(expected, result); +/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters +/// were found, or the missing delimiter if not. +fn replace_region_in_text<'a>( + text: &str, + start: &'a str, + end: &'a str, + mut write_replacement: impl FnMut(&mut String), +) -> Result { + let (text_start, rest) = text.split_once(start).ok_or(start)?; + let (_, text_end) = rest.split_once(end).ok_or(end)?; + + let mut res = String::with_capacity(text.len() + 4096); + res.push_str(text_start); + res.push_str(start); + write_replacement(&mut res); + res.push_str(end); + res.push_str(text_end); + + Ok(res) } #[cfg(test)] @@ -541,55 +467,65 @@ mod tests { use super::*; #[test] - fn test_replace_region() { - let text = "\nabc\n123\n789\ndef\nghi"; - let expected = FileChange { - changed: true, - new_lines: "\nabc\nhello world\ndef\nghi".to_string(), - }; - let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || { - vec!["hello world".to_string()] - }); - assert_eq!(expected, result); - } + fn test_parse_contents() { + static CONTENTS: &str = r#" + declare_clippy_lint! { + #[clippy::version = "Hello Clippy!"] + pub PTR_ARG, + style, + "really long \ + text" + } - #[test] - fn test_replace_region_with_start() { - let text = "\nabc\n123\n789\ndef\nghi"; - let expected = FileChange { - changed: true, - new_lines: "\nhello world\ndef\nghi".to_string(), - }; - let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || { - vec!["hello world".to_string()] - }); + declare_clippy_lint!{ + #[clippy::version = "Test version"] + pub DOC_MARKDOWN, + pedantic, + "single line" + } + "#; + let mut result = Vec::new(); + parse_contents(CONTENTS, "module_name", &mut result); + + let expected = vec![ + Lint::new("ptr_arg", "style", "\"really long text\"", "module_name"), + Lint::new("doc_markdown", "pedantic", "\"single line\"", "module_name"), + ]; assert_eq!(expected, result); } #[test] - fn test_replace_region_no_changes() { - let text = "123\n456\n789"; - let expected = FileChange { - changed: false, - new_lines: "123\n456\n789".to_string(), - }; - let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new); + fn test_parse_deprecated_contents() { + static DEPRECATED_CONTENTS: &str = r#" + /// some doc comment + declare_deprecated_lint! { + #[clippy::version = "I'm a version"] + pub SHOULD_ASSERT_EQ, + "`assert!()` will be more flexible with RFC 2011" + } + "#; + + let mut result = Vec::new(); + parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result); + + let expected = vec![DeprecatedLint::new( + "should_assert_eq", + "\"`assert!()` will be more flexible with RFC 2011\"", + )]; assert_eq!(expected, result); } #[test] fn test_usable_lints() { let lints = vec![ - Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"), - Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"), - Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"), - Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"), + Lint::new("should_assert_eq2", "Not Deprecated", "\"abc\"", "module_name"), + Lint::new("should_assert_eq2", "internal", "\"abc\"", "module_name"), + Lint::new("should_assert_eq2", "internal_style", "\"abc\"", "module_name"), ]; let expected = vec![Lint::new( "should_assert_eq2", "Not Deprecated", - "abc", - None, + "\"abc\"", "module_name", )]; assert_eq!(expected, Lint::usable_lints(&lints)); @@ -598,55 +534,30 @@ mod tests { #[test] fn test_by_lint_group() { let lints = vec![ - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"), - Lint::new("incorrect_match", "group1", "abc", None, "module_name"), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"), + Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name"), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"), ]; let mut expected: HashMap> = HashMap::new(); expected.insert( "group1".to_string(), vec![ - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("incorrect_match", "group1", "abc", None, "module_name"), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"), ], ); expected.insert( "group2".to_string(), - vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")], + vec![Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name")], ); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); } - #[test] - fn test_gen_changelog_lint_list() { - let lints = vec![ - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"), - ]; - let expected = vec![ - format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK), - format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK), - ]; - assert_eq!(expected, gen_changelog_lint_list(lints.iter())); - } - #[test] fn test_gen_deprecated() { let lints = vec![ - Lint::new( - "should_assert_eq", - "group1", - "abc", - Some("has been superseded by should_assert_eq2"), - "module_name", - ), - Lint::new( - "another_deprecated", - "group2", - "abc", - Some("will be removed"), - "module_name", - ), + DeprecatedLint::new("should_assert_eq", "\"has been superseded by should_assert_eq2\""), + DeprecatedLint::new("another_deprecated", "\"will be removed\""), ]; let expected = GENERATED_FILE_COMMENT.to_string() @@ -665,32 +576,15 @@ mod tests { .join("\n") + "\n"; - assert_eq!(expected, gen_deprecated(lints.iter())); - } - - #[test] - #[should_panic] - fn test_gen_deprecated_fail() { - let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")]; - let _deprecated_lints = gen_deprecated(lints.iter()); - } - - #[test] - fn test_gen_modules_list() { - let lints = vec![ - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"), - ]; - let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()]; - assert_eq!(expected, gen_modules_list(lints.iter())); + assert_eq!(expected, gen_deprecated(&lints)); } #[test] fn test_gen_lint_group_list() { let lints = vec![ - Lint::new("abc", "group1", "abc", None, "module_name"), - Lint::new("should_assert_eq", "group1", "abc", None, "module_name"), - Lint::new("internal", "internal_style", "abc", None, "module_name"), + Lint::new("abc", "group1", "\"abc\"", "module_name"), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"), + Lint::new("internal", "internal_style", "\"abc\"", "module_name"), ]; let expected = GENERATED_FILE_COMMENT.to_string() + &[ diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 66e61660d313a..aebf9a87cabd2 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.61" +version = "0.1.62" 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/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs new file mode 100644 index 0000000000000..e9b0f1f672de0 --- /dev/null +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -0,0 +1,42 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::{meets_msrv, msrvs}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_semver::RustcVersion; + +use super::CAST_ABS_TO_UNSIGNED; + +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_expr: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, + msrv: &Option, +) { + if_chain! { + if meets_msrv(msrv.as_ref(), &msrvs::UNSIGNED_ABS); + if cast_from.is_integral(); + if cast_to.is_integral(); + if cast_from.is_signed(); + if !cast_to.is_signed(); + if let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind; + if let method_name = method_path.ident.name.as_str(); + if method_name == "abs"; + then { + span_lint_and_sugg( + cx, + CAST_ABS_TO_UNSIGNED, + expr.span, + &format!("casting the result of `{}::{}()` to {}", cast_from, method_name, cast_to), + "replace with", + format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index a4ef1344ab951..d476a1a7646c0 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_hir_ty_cfg_dependant; use clippy_utils::ty::is_c_void; -use if_chain::if_chain; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, match_any_def_paths, paths}; use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; @@ -20,45 +19,78 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } else if let ExprKind::MethodCall(method_path, [self_arg, ..], _) = &expr.kind { - if_chain! { - if method_path.ident.name == sym!(cast); - if let Some(generic_args) = method_path.args; - if let [GenericArg::Type(cast_to)] = generic_args.args; + if method_path.ident.name == sym!(cast) + && let Some(generic_args) = method_path.args + && let [GenericArg::Type(cast_to)] = generic_args.args // There probably is no obvious reason to do this, just to be consistent with `as` cases. - if !is_hir_ty_cfg_dependant(cx, cast_to); - then { - let (cast_from, cast_to) = - (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); - lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } + && !is_hir_ty_cfg_dependant(cx, cast_to) + { + let (cast_from, cast_to) = + (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } } } fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { - if_chain! { - if let ty::RawPtr(from_ptr_ty) = &cast_from.kind(); - if let ty::RawPtr(to_ptr_ty) = &cast_to.kind(); - if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty); - if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty); - if from_layout.align.abi < to_layout.align.abi; + if let ty::RawPtr(from_ptr_ty) = &cast_from.kind() + && let ty::RawPtr(to_ptr_ty) = &cast_to.kind() + && let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty) + && let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty) + && from_layout.align.abi < to_layout.align.abi // with c_void, we inherently need to trust the user - if !is_c_void(cx, from_ptr_ty.ty); + && !is_c_void(cx, from_ptr_ty.ty) // when casting from a ZST, we don't know enough to properly lint - if !from_layout.is_zst(); - then { - span_lint( - cx, - CAST_PTR_ALIGNMENT, - expr.span, - &format!( - "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", - cast_from, - cast_to, - from_layout.align.abi.bytes(), - to_layout.align.abi.bytes(), - ), - ); - } + && !from_layout.is_zst() + && !is_used_as_unaligned(cx, expr) + { + span_lint( + cx, + CAST_PTR_ALIGNMENT, + expr.span, + &format!( + "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", + cast_from, + cast_to, + from_layout.align.abi.bytes(), + to_layout.align.abi.bytes(), + ), + ); + } +} + +fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + let Some(parent) = get_parent_expr(cx, e) else { + return false; + }; + match parent.kind { + ExprKind::MethodCall(name, [self_arg, ..], _) if self_arg.hir_id == e.hir_id => { + if matches!(name.ident.as_str(), "read_unaligned" | "write_unaligned") + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) + && let Some(def_id) = cx.tcx.impl_of_method(def_id) + && cx.tcx.type_of(def_id).is_unsafe_ptr() + { + true + } else { + false + } + }, + ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => { + static PATHS: &[&[&str]] = &[ + paths::PTR_READ_UNALIGNED.as_slice(), + paths::PTR_WRITE_UNALIGNED.as_slice(), + paths::PTR_UNALIGNED_VOLATILE_LOAD.as_slice(), + paths::PTR_UNALIGNED_VOLATILE_STORE.as_slice(), + ]; + if let ExprKind::Path(path) = &func.kind + && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() + && match_any_def_paths(cx, def_id, PATHS).is_some() + { + true + } else { + false + } + }, + _ => false, } } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index be59145afa003..55c1f085657bb 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -1,3 +1,4 @@ +mod cast_abs_to_unsigned; mod cast_enum_constructor; mod cast_lossless; mod cast_possible_truncation; @@ -473,6 +474,28 @@ declare_clippy_lint! { "casts from an enum tuple constructor to an integer" } +declare_clippy_lint! { + /// ### What it does + /// Checks for uses of the `abs()` method that cast the result to unsigned. + /// + /// ### Why is this bad? + /// The `unsigned_abs()` method avoids panic when called on the MIN value. + /// + /// ### Example + /// ```rust + /// let x: i32 = -42; + /// let y: u32 = x.abs() as u32; + /// ``` + /// Use instead: + /// let x: i32 = -42; + /// let y: u32 = x.unsigned_abs(); + /// ``` + #[clippy::version = "1.61.0"] + pub CAST_ABS_TO_UNSIGNED, + suspicious, + "casting the result of `abs()` to an unsigned integer can panic" +} + pub struct Casts { msrv: Option, } @@ -500,7 +523,8 @@ impl_lint_pass!(Casts => [ CHAR_LIT_AS_U8, PTR_AS_PTR, CAST_ENUM_TRUNCATION, - CAST_ENUM_CONSTRUCTOR + CAST_ENUM_CONSTRUCTOR, + CAST_ABS_TO_UNSIGNED ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -536,6 +560,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); + cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); } cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 470c8c7ea26a6..af56ec11ef8ac 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -4,7 +4,8 @@ use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Lit, UnOp}; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, Lit, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; @@ -18,6 +19,17 @@ pub(super) fn check( cast_from: Ty<'_>, cast_to: Ty<'_>, ) -> bool { + // skip non-primitive type cast + if_chain! { + if let ExprKind::Cast(_, cast_to) = expr.kind; + if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind; + if let Res::PrimTy(_) = path.res; + then {} + else { + return false + } + } + if let Some(lit) = get_numeric_literal(cast_expr) { let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs new file mode 100644 index 0000000000000..fc141b4a6e3af --- /dev/null +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -0,0 +1,125 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::{AttrKind, Attribute, Item, ItemKind}; +use rustc_ast::token::{Token, TokenKind}; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{symbol::sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for use of `crate` as opposed to `$crate` in a macro definition. + /// + /// ### Why is this bad? + /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's + /// crate. Rarely is the former intended. See: + /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene + /// + /// ### Example + /// ```rust + /// #[macro_export] + /// macro_rules! print_message { + /// () => { + /// println!("{}", crate::MESSAGE); + /// }; + /// } + /// pub const MESSAGE: &str = "Hello!"; + /// ``` + /// Use instead: + /// ```rust + /// #[macro_export] + /// macro_rules! print_message { + /// () => { + /// println!("{}", $crate::MESSAGE); + /// }; + /// } + /// pub const MESSAGE: &str = "Hello!"; + /// ``` + /// + /// Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the + /// macro definition, e.g.: + /// ```rust,ignore + /// #[allow(clippy::crate_in_macro_def)] + /// macro_rules! ok { ... crate::foo ... } + /// ``` + #[clippy::version = "1.61.0"] + pub CRATE_IN_MACRO_DEF, + suspicious, + "using `crate` in a macro definition" +} +declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); + +impl EarlyLintPass for CrateInMacroDef { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if_chain! { + if item.attrs.iter().any(is_macro_export); + if let ItemKind::MacroDef(macro_def) = &item.kind; + let tts = macro_def.body.inner_tokens(); + if let Some(span) = contains_unhygienic_crate_reference(&tts); + then { + span_lint_and_sugg( + cx, + CRATE_IN_MACRO_DEF, + span, + "`crate` references the macro call's crate", + "to reference the macro definition's crate, use", + String::from("$crate"), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn is_macro_export(attr: &Attribute) -> bool { + if_chain! { + if let AttrKind::Normal(attr_item, _) = &attr.kind; + if let [segment] = attr_item.path.segments.as_slice(); + then { + segment.ident.name == sym::macro_export + } else { + false + } + } +} + +fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { + let mut prev_is_dollar = false; + let mut cursor = tts.trees(); + while let Some(curr) = cursor.next() { + if_chain! { + if !prev_is_dollar; + if let Some(span) = is_crate_keyword(&curr); + if let Some(next) = cursor.look_ahead(0); + if is_token(next, &TokenKind::ModSep); + then { + return Some(span); + } + } + if let TokenTree::Delimited(_, _, tts) = &curr { + let span = contains_unhygienic_crate_reference(tts); + if span.is_some() { + return span; + } + } + prev_is_dollar = is_token(&curr, &TokenKind::Dollar); + } + None +} + +fn is_crate_keyword(tt: &TokenTree) -> Option { + if_chain! { + if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }) = tt; + if symbol.as_str() == "crate"; + then { Some(*span) } else { None } + } +} + +fn is_token(tt: &TokenTree, kind: &TokenKind) -> bool { + if let TokenTree::Token(Token { kind: other, .. }) = tt { + kind == other + } else { + false + } +} diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 92cf82bcd6a34..28d0c75fde6ba 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -621,8 +621,8 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle(false) - .expect("failed to load fallback fluent bundle"); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle"); let emitter = EmitterWriter::new( Box::new(io::sink()), None, diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 5c4b35fd4b9d2..88c54828da834 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,9 +1,8 @@ -use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::ty::is_copy; -use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; +use clippy_utils::is_must_use_func_call; +use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -103,6 +102,75 @@ declare_clippy_lint! { "calls to `std::mem::forget` with a value that implements Copy" } +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `std::mem::drop` with a value that does not implement `Drop`. + /// + /// ### Why is this bad? + /// Calling `std::mem::drop` is no different than dropping such a type. A different value may + /// have been intended. + /// + /// ### Example + /// ```rust + /// struct Foo; + /// let x = Foo; + /// std::mem::drop(x); + /// ``` + #[clippy::version = "1.61.0"] + pub DROP_NON_DROP, + suspicious, + "call to `std::mem::drop` with a value which does not implement `Drop`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `std::mem::forget` with a value that does not implement `Drop`. + /// + /// ### Why is this bad? + /// Calling `std::mem::forget` is no different than dropping such a type. A different value may + /// have been intended. + /// + /// ### Example + /// ```rust + /// struct Foo; + /// let x = Foo; + /// std::mem::forget(x); + /// ``` + #[clippy::version = "1.61.0"] + pub FORGET_NON_DROP, + suspicious, + "call to `std::mem::forget` with a value which does not implement `Drop`" +} + +declare_clippy_lint! { + /// ### What it does + /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. + /// + /// ### Why is this bad? + /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`. + /// + /// ### Known problems + /// Does not catch cases if the user binds `std::mem::drop` + /// to a different name and calls it that way. + /// + /// ### Example + /// ```rust + /// struct S; + /// drop(std::mem::ManuallyDrop::new(S)); + /// ``` + /// Use instead: + /// ```rust + /// struct S; + /// unsafe { + /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); + /// } + /// ``` + #[clippy::version = "1.49.0"] + pub UNDROPPED_MANUALLY_DROPS, + correctness, + "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value" +} + const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \ Dropping a reference does nothing"; const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \ @@ -111,60 +179,65 @@ const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that imp Dropping a copy leaves the original intact"; const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \ Forgetting a copy leaves the original intact"; +const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \ + Dropping such a type only extends it's contained lifetimes"; +const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \ + Forgetting such a type is the same as dropping it"; -declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY]); +declare_lint_pass!(DropForgetRef => [ + DROP_REF, + FORGET_REF, + DROP_COPY, + FORGET_COPY, + DROP_NON_DROP, + FORGET_NON_DROP, + UNDROPPED_MANUALLY_DROPS +]); impl<'tcx> LateLintPass<'tcx> for DropForgetRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(path, args) = expr.kind; - if let ExprKind::Path(ref qpath) = path.kind; - if args.len() == 1; - if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - then { - let lint; - let msg; - let arg = &args[0]; - let arg_ty = cx.typeck_results().expr_ty(arg); - - if let ty::Ref(..) = arg_ty.kind() { - match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::mem_drop) => { - lint = DROP_REF; - msg = DROP_REF_SUMMARY.to_string(); - }, - Some(sym::mem_forget) => { - lint = FORGET_REF; - msg = FORGET_REF_SUMMARY.to_string(); - }, - _ => return, - } - span_lint_and_note(cx, - lint, - expr.span, - &msg, - Some(arg.span), - &format!("argument has type `{}`", arg_ty)); - } else if is_copy(cx, arg_ty) { - match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::mem_drop) => { - lint = DROP_COPY; - msg = DROP_COPY_SUMMARY.to_string(); - }, - Some(sym::mem_forget) => { - lint = FORGET_COPY; - msg = FORGET_COPY_SUMMARY.to_string(); - }, - _ => return, - } - span_lint_and_note(cx, - lint, - expr.span, - &msg, - Some(arg.span), - &format!("argument has type {}", arg_ty)); + if let ExprKind::Call(path, [arg]) = expr.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id) + { + let arg_ty = cx.typeck_results().expr_ty(arg); + let (lint, msg) = match fn_name { + sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY), + sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY), + sym::mem_drop if is_copy(cx, arg_ty) => (DROP_COPY, DROP_COPY_SUMMARY), + sym::mem_forget if is_copy(cx, arg_ty) => (FORGET_COPY, FORGET_COPY_SUMMARY), + sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => { + span_lint_and_help( + cx, + UNDROPPED_MANUALLY_DROPS, + expr.span, + "the inner value of this ManuallyDrop will not be dropped", + None, + "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop", + ); + return; } - } + sym::mem_drop + if !(arg_ty.needs_drop(cx.tcx, cx.param_env) + || is_must_use_func_call(cx, arg) + || is_must_use_ty(cx, arg_ty)) => + { + (DROP_NON_DROP, DROP_NON_DROP_SUMMARY) + }, + sym::mem_forget if !arg_ty.needs_drop(cx.tcx, cx.param_env) => { + (FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY) + }, + _ => return, + }; + span_lint_and_note( + cx, + lint, + expr.span, + msg, + Some(arg.span), + &format!("argument has type `{}`", arg_ty), + ); } } } diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs new file mode 100644 index 0000000000000..fdeac8d82557f --- /dev/null +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -0,0 +1,99 @@ +use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt}; +use rustc_ast::ast::{Item, ItemKind, VariantData}; +use rustc_errors::Applicability; +use rustc_lexer::TokenKind; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Finds structs without fields (a so-called "empty struct") that are declared with brackets. + /// + /// ### Why is this bad? + /// Empty brackets after a struct declaration can be omitted. + /// + /// ### Example + /// ```rust + /// struct Cookie {} + /// ``` + /// Use instead: + /// ```rust + /// struct Cookie; + /// ``` + #[clippy::version = "1.62.0"] + pub EMPTY_STRUCTS_WITH_BRACKETS, + restriction, + "finds struct declarations with empty brackets" +} +declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]); + +impl EarlyLintPass for EmptyStructsWithBrackets { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + let span_after_ident = item.span.with_lo(item.ident.span.hi()); + + if let ItemKind::Struct(var_data, _) = &item.kind + && has_brackets(var_data) + && has_no_fields(cx, var_data, span_after_ident) { + span_lint_and_then( + cx, + EMPTY_STRUCTS_WITH_BRACKETS, + span_after_ident, + "found empty brackets on struct declaration", + |diagnostic| { + diagnostic.span_suggestion_hidden( + span_after_ident, + "remove the brackets", + ";".to_string(), + Applicability::MachineApplicable); + }, + ); + } + } +} + +fn has_no_ident_token(braces_span_str: &str) -> bool { + !rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident) +} + +fn has_brackets(var_data: &VariantData) -> bool { + !matches!(var_data, VariantData::Unit(_)) +} + +fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool { + if !var_data.fields().is_empty() { + return false; + } + + // there might still be field declarations hidden from the AST + // (conditionaly compiled code using #[cfg(..)]) + + let Some(braces_span_str) = snippet_opt(cx, braces_span) else { + return false; + }; + + has_no_ident_token(braces_span_str.as_ref()) +} + +#[cfg(test)] +mod unit_test { + use super::*; + + #[test] + fn test_has_no_ident_token() { + let input = "{ field: u8 }"; + assert!(!has_no_ident_token(input)); + + let input = "(u8, String);"; + assert!(!has_no_ident_token(input)); + + let input = " { + // test = 5 + } + "; + assert!(has_no_ident_token(input)); + + let input = " ();"; + assert!(has_no_ident_token(input)); + } +} diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index f824f20ca40a0..4d6bef89bea7f 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -5,7 +5,7 @@ use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; use clippy_utils::diagnostics::span_lint; use clippy_utils::{clip, unsext}; @@ -54,6 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { check(cx, left, -1, e.span, right.span); check(cx, right, -1, e.span, left.span); }, + BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span), _ => (), } } @@ -70,6 +71,18 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_ && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))) } +fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { + let lhs_const = constant_full_int(cx, cx.typeck_results(), left); + let rhs_const = constant_full_int(cx, cx.typeck_results(), right); + if match (lhs_const, rhs_const) { + (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(), + (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, + _ => return, + } { + span_ineffective_operation(cx, span, arg); + } +} + 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).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { @@ -83,15 +96,19 @@ fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { 1 => v == 1, _ => unreachable!(), } { - span_lint( - cx, - IDENTITY_OP, - span, - &format!( - "the operation is ineffective. Consider reducing it to `{}`", - snippet(cx, arg, "..") - ), - ); + span_ineffective_operation(cx, span, arg); } } } + +fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) { + span_lint( + cx, + IDENTITY_OP, + span, + &format!( + "the operation is ineffective. Consider reducing it to `{}`", + snippet(cx, arg, "..") + ), + ); +} diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 9ead4bb27a588..4ba7477add82a 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -96,6 +96,10 @@ declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING] impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + return; + } + if let ExprKind::Index(array, index) = &expr.kind { let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::Range::hir(index) { @@ -151,6 +155,10 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { } else { // Catchall non-range index, i.e., [n] or [n << m] if let ty::Array(..) = ty.kind() { + // Index is a const block. + if let ExprKind::ConstBlock(..) = index.kind { + return; + } // Index is a constant uint. if let Some(..) = constant(cx, cx.typeck_results(), index) { // Let rustc's `const_err` lint handle constant `usize` indexing on arrays. diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 132a466267626..14ca93b5f3c14 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -23,6 +23,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(casts::CAST_REF_TO_MUT), @@ -37,6 +38,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), LintId::of(derivable_impls::DERIVABLE_IMPLS), @@ -49,9 +51,12 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(double_comparison::DOUBLE_COMPARISONS), LintId::of(double_parens::DOUBLE_PARENS), LintId::of(drop_forget_ref::DROP_COPY), + LintId::of(drop_forget_ref::DROP_NON_DROP), LintId::of(drop_forget_ref::DROP_REF), LintId::of(drop_forget_ref::FORGET_COPY), + LintId::of(drop_forget_ref::FORGET_NON_DROP), LintId::of(drop_forget_ref::FORGET_REF), + LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), LintId::of(duration_subsec::DURATION_SUBSEC), LintId::of(entry::MAP_ENTRY), LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), @@ -152,6 +157,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::CHARS_NEXT_CMP), LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::CLONE_ON_COPY), + LintId::of(methods::ERR_EXPECT), LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::FILTER_MAP_IDENTITY), @@ -175,6 +181,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), + LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::OK_EXPECT), @@ -224,7 +231,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), - LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), @@ -296,7 +302,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(types::REDUNDANT_ALLOCATION), LintId::of(types::TYPE_COMPLEXITY), LintId::of(types::VEC_BOX), - LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_hash::UNIT_HASH), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index a2ce69065f94d..10369a855ae6e 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -44,6 +44,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::MANUAL_SPLIT_ONCE), LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), + LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), @@ -60,7 +61,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index df63f84463dba..6bf2c4bbaedc0 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -22,6 +22,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(drop_forget_ref::DROP_REF), LintId::of(drop_forget_ref::FORGET_COPY), LintId::of(drop_forget_ref::FORGET_REF), + LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), LintId::of(eq_op::EQ_OP), LintId::of(erasing_op::ERASING_OP), @@ -62,7 +63,6 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmuting_null::TRANSMUTING_NULL), - LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_hash::UNIT_HASH), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 21f1ef562b5a3..532590aaa5a3d 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -70,6 +70,7 @@ store.register_lints(&[ cargo::REDUNDANT_FEATURE_NAMES, cargo::WILDCARD_DEPENDENCIES, case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + casts::CAST_ABS_TO_UNSIGNED, casts::CAST_ENUM_CONSTRUCTOR, casts::CAST_ENUM_TRUNCATION, casts::CAST_LOSSLESS, @@ -97,6 +98,7 @@ store.register_lints(&[ copies::IF_SAME_THEN_ELSE, copies::SAME_FUNCTIONS_IN_IF_CONDITION, copy_iterator::COPY_ITERATOR, + crate_in_macro_def::CRATE_IN_MACRO_DEF, create_dir::CREATE_DIR, dbg_macro::DBG_MACRO, default::DEFAULT_TRAIT_ACCESS, @@ -122,12 +124,16 @@ store.register_lints(&[ double_comparison::DOUBLE_COMPARISONS, double_parens::DOUBLE_PARENS, drop_forget_ref::DROP_COPY, + drop_forget_ref::DROP_NON_DROP, drop_forget_ref::DROP_REF, drop_forget_ref::FORGET_COPY, + drop_forget_ref::FORGET_NON_DROP, drop_forget_ref::FORGET_REF, + drop_forget_ref::UNDROPPED_MANUALLY_DROPS, duration_subsec::DURATION_SUBSEC, else_if_without_else::ELSE_IF_WITHOUT_ELSE, empty_enum::EMPTY_ENUM, + empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS, entry::MAP_ENTRY, enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, enum_variants::ENUM_VARIANT_NAMES, @@ -280,6 +286,7 @@ store.register_lints(&[ methods::CLONE_DOUBLE_REF, methods::CLONE_ON_COPY, methods::CLONE_ON_REF_PTR, + methods::ERR_EXPECT, methods::EXPECT_FUN_CALL, methods::EXPECT_USED, methods::EXTEND_WITH_DRAIN, @@ -313,6 +320,7 @@ store.register_lints(&[ methods::MAP_FLATTEN, methods::MAP_IDENTITY, methods::MAP_UNWRAP_OR, + methods::NEEDLESS_OPTION_AS_DEREF, methods::NEEDLESS_SPLITN, methods::NEW_RET_NO_SELF, methods::OK_EXPECT, @@ -384,7 +392,6 @@ store.register_lints(&[ needless_continue::NEEDLESS_CONTINUE, needless_for_each::NEEDLESS_FOR_EACH, needless_late_init::NEEDLESS_LATE_INIT, - needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF, needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, needless_question_mark::NEEDLESS_QUESTION_MARK, needless_update::NEEDLESS_UPDATE, @@ -505,7 +512,6 @@ store.register_lints(&[ types::TYPE_COMPLEXITY, types::VEC_BOX, undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS, - undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, unicode::INVISIBLE_CHARACTERS, unicode::NON_ASCII_LITERAL, unicode::UNICODE_NOT_NFC, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 6ab139b2fb67b..4802dd877e99d 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -16,6 +16,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION), LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS), LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), LintId::of(exit::EXIT), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index dcf399cf9562f..3114afac8863e 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -59,6 +59,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(methods::BYTES_NTH), LintId::of(methods::CHARS_LAST_CMP), LintId::of(methods::CHARS_NEXT_CMP), + LintId::of(methods::ERR_EXPECT), LintId::of(methods::INTO_ITER_ON_REF), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_NEXT_SLICE), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index fa3a88e1368ce..82f45b5fd58b9 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -7,8 +7,12 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), + LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), + LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), + LintId::of(drop_forget_ref::DROP_NON_DROP), + LintId::of(drop_forget_ref::FORGET_NON_DROP), LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f2a0799914448..c9b836f95808a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,5 +1,6 @@ // error-pattern:cargo-clippy +#![feature(array_windows)] #![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] #![feature(control_flow_enum)] @@ -190,6 +191,7 @@ mod collapsible_match; mod comparison_chain; mod copies; mod copy_iterator; +mod crate_in_macro_def; mod create_dir; mod dbg_macro; mod default; @@ -208,6 +210,7 @@ mod drop_forget_ref; mod duration_subsec; mod else_if_without_else; mod empty_enum; +mod empty_structs_with_brackets; mod entry; mod enum_clike; mod enum_variants; @@ -305,7 +308,6 @@ mod needless_borrowed_ref; mod needless_continue; mod needless_for_each; mod needless_late_init; -mod needless_option_as_deref; mod needless_pass_by_value; mod needless_question_mark; mod needless_update; @@ -375,7 +377,6 @@ mod transmuting_null; mod try_err; mod types; mod undocumented_unsafe_blocks; -mod undropped_manually_drops; mod unicode; mod uninit_vec; mod unit_hash; @@ -533,7 +534,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(ptr::Ptr)); store.register_late_pass(|| Box::new(ptr_eq::PtrEq)); store.register_late_pass(|| Box::new(needless_bool::NeedlessBool)); - store.register_late_pass(|| Box::new(needless_option_as_deref::OptionNeedlessDeref)); store.register_late_pass(|| Box::new(needless_bool::BoolComparison)); store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach)); store.register_late_pass(|| Box::new(misc::MiscLints)); @@ -812,7 +812,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); - store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops)); store.register_late_pass(|| Box::new(strings::StrToString)); store.register_late_pass(|| Box::new(strings::StringToString)); store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues)); @@ -847,7 +846,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: enable_raw_pointer_heuristic_for_send, )) }); - store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default())); + store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch)); store.register_late_pass(move || Box::new(format_args::FormatArgs)); store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray)); @@ -867,6 +866,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ignore_publish: cargo_ignore_publish, }) }); + store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); + store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index 36ecd83f7d643..a0bd7ad0ac647 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -2,9 +2,12 @@ use super::SINGLE_ELEMENT_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet_with_applicability}; use if_chain::if_chain; +use rustc_ast::util::parser::PREC_PREFIX; +use rustc_ast::Mutability; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, Pat}; +use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat}; use rustc_lint::LateContext; +use rustc_span::edition::Edition; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -13,31 +16,84 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { - let arg_expr = match arg.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg, - ExprKind::MethodCall(method, [arg], _) if method.ident.name == rustc_span::sym::iter => arg, + let (arg_expression, prefix) = match arg.kind { + ExprKind::AddrOf( + BorrowKind::Ref, + Mutability::Not, + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ) => (arg, "&"), + ExprKind::AddrOf( + BorrowKind::Ref, + Mutability::Mut, + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ) => (arg, "&mut "), + ExprKind::MethodCall( + method, + [ + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ], + _, + ) if method.ident.name == rustc_span::sym::iter => (arg, "&"), + ExprKind::MethodCall( + method, + [ + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ], + _, + ) if method.ident.name.as_str() == "iter_mut" => (arg, "&mut "), + ExprKind::MethodCall( + method, + [ + Expr { + kind: ExprKind::Array([arg]), + .. + }, + ], + _, + ) if method.ident.name == rustc_span::sym::into_iter => (arg, ""), + // Only check for arrays edition 2021 or later, as this case will trigger a compiler error otherwise. + ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""), _ => return, }; if_chain! { - if let ExprKind::Array([arg_expression]) = arg_expr.kind; if let ExprKind::Block(block, _) = body.kind; if !block.stmts.is_empty(); then { let mut applicability = Applicability::MachineApplicable; let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); - let arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); + let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned(); block_str.remove(0); block_str.pop(); let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)); + // Reference iterator from `&(mut) []` or `[].iter(_mut)()`. + if !prefix.is_empty() && ( + // Precedence of internal expression is less than or equal to precedence of `&expr`. + arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression) + ) { + arg_snip = format!("({arg_snip})").into(); + } + span_lint_and_sugg( cx, SINGLE_ELEMENT_LOOP, expr.span, "for loop over a single element", "try", - format!("{{\n{}let {} = &{};{}}}", indent, pat_snip, arg_snip, block_str), + format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"), applicability, ) } diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 0f6ac47843241..f552d5c1afab9 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{iter_input_pats, method_chain_args}; use if_chain::if_chain; @@ -217,36 +217,33 @@ fn lint_map_unit_fn(cx: &LateContext<'_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr let fn_arg = &map_args[1]; if is_unit_function(cx, fn_arg) { + let mut applicability = Applicability::MachineApplicable; let msg = suggestion_msg("function", map_type); let suggestion = format!( "if let {0}({binding}) = {1} {{ {2}({binding}) }}", variant, - snippet(cx, var_arg.span, "_"), - snippet(cx, fn_arg.span, "_"), + snippet_with_applicability(cx, var_arg.span, "_", &mut applicability), + snippet_with_applicability(cx, fn_arg.span, "_", &mut applicability), binding = let_binding_name(cx, var_arg) ); span_lint_and_then(cx, lint, expr.span, &msg, |diag| { - diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::MachineApplicable); + diag.span_suggestion(stmt.span, "try this", suggestion, applicability); }); } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) { let msg = suggestion_msg("closure", map_type); span_lint_and_then(cx, lint, expr.span, &msg, |diag| { if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) { + let mut applicability = Applicability::MachineApplicable; let suggestion = format!( "if let {0}({1}) = {2} {{ {3} }}", variant, - snippet(cx, binding.pat.span, "_"), - snippet(cx, var_arg.span, "_"), - snippet(cx, reduced_expr_span, "_") - ); - diag.span_suggestion( - stmt.span, - "try this", - suggestion, - Applicability::MachineApplicable, // snippet + snippet_with_applicability(cx, binding.pat.span, "_", &mut applicability), + snippet_with_applicability(cx, var_arg.span, "_", &mut applicability), + snippet_with_context(cx, reduced_expr_span, var_arg.span.ctxt(), "_", &mut applicability).0, ); + diag.span_suggestion(stmt.span, "try this", suggestion, applicability); } else { let suggestion = format!( "if let {0}({1}) = {2} {{ ... }}", diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index ff85623acf49b..e93b494653fc0 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -667,7 +667,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { overlapping_arms::check(cx, ex, arms); match_wild_enum::check(cx, ex, arms); match_as_ref::check(cx, ex, arms, expr); - needless_match::check_match(cx, ex, arms); + needless_match::check_match(cx, ex, arms, expr); if self.infallible_destructuring_match_linted { self.infallible_destructuring_match_linted = false; diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 76131d307d777..2105a03e03a30 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -1,37 +1,25 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, get_parent_expr, higher, is_else_clause, is_lang_ctor, peel_blocks_with_stmt}; +use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::{ + eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, over, + peel_blocks_with_stmt, +}; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, Pat, PatKind, Path, PathSegment, QPath, UnOp}; +use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::sym; +use rustc_typeck::hir_ty_to_ty; -pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { - // This is for avoiding collision with `match_single_binding`. - if arms.len() < 2 { - return; - } - - for arm in arms { - if let PatKind::Wild = arm.pat.kind { - let ret_expr = strip_return(arm.body); - if !eq_expr_value(cx, ex, ret_expr) { - return; - } - } else if !pat_same_as_expr(arm.pat, arm.body) { - return; - } - } - - if let Some(match_expr) = get_parent_expr(cx, ex) { +pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { + if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, NEEDLESS_MATCH, - match_expr.span, + expr.span, "this match expression is unnecessary", "replace it with", snippet_with_applicability(cx, ex.span, "..", &mut applicability).to_string(), @@ -60,11 +48,8 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) /// } /// ``` pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) { - if_chain! { - if let Some(ref if_let) = higher::IfLet::hir(cx, ex); - if !is_else_clause(cx.tcx, ex); - if check_if_let(cx, if_let); - then { + if let Some(ref if_let) = higher::IfLet::hir(cx, ex) { + if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -79,6 +64,19 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) { } } +fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool { + for arm in arms { + let arm_expr = peel_blocks_with_stmt(arm.body); + if let PatKind::Wild = arm.pat.kind { + return eq_expr_value(cx, match_expr, strip_return(arm_expr)); + } else if !pat_same_as_expr(arm.pat, arm_expr) { + return false; + } + } + + true +} + fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if let Some(if_else) = if_let.if_else { if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) { @@ -92,18 +90,21 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if matches!(if_else.kind, ExprKind::Block(..)) { let else_expr = peel_blocks_with_stmt(if_else); + if matches!(else_expr.kind, ExprKind::Block(..)) { + return false; + } let ret = strip_return(else_expr); let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { if let ExprKind::Path(ref qpath) = ret.kind { return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); } - } else { - return eq_expr_value(cx, if_let.let_expr, ret); + return true; } - return true; + return eq_expr_value(cx, if_let.let_expr, ret); } } + false } @@ -116,48 +117,70 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { } } +/// Manually check for coercion casting by checking if the type of the match operand or let expr +/// differs with the assigned local variable or the funtion return type. +fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { + if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) { + match p_node { + // Compare match_expr ty with local in `let local = match match_expr {..}` + Node::Local(local) => { + let results = cx.typeck_results(); + return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); + }, + // compare match_expr ty with RetTy in `fn foo() -> RetTy` + Node::Item(..) => { + if let Some(fn_decl) = p_node.fn_decl() { + if let FnRetTy::Return(ret_ty) = fn_decl.output { + return same_type_and_consts(hir_ty_to_ty(cx.tcx, ret_ty), cx.typeck_results().expr_ty(expr)); + } + } + }, + // check the parent expr for this whole block `{ match match_expr {..} }` + Node::Block(block) => { + if let Some(block_parent_expr) = get_parent_expr_for_hir(cx, block.hir_id) { + return expr_ty_matches_p_ty(cx, expr, block_parent_expr); + } + }, + // recursively call on `if xxx {..}` etc. + Node::Expr(p_expr) => { + return expr_ty_matches_p_ty(cx, expr, p_expr); + }, + _ => {}, + } + } + false +} + fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { let expr = strip_return(expr); match (&pat.kind, &expr.kind) { // Example: `Some(val) => Some(val)` - ( - PatKind::TupleStruct(QPath::Resolved(_, path), [first_pat, ..], _), - ExprKind::Call(call_expr, [first_param, ..]), - ) => { + (PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => { if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind { - if has_identical_segments(path.segments, call_path.segments) - && has_same_non_ref_symbol(first_pat, first_param) - { - return true; - } + return over(path.segments, call_path.segments, |pat_seg, call_seg| { + pat_seg.ident.name == call_seg.ident.name + }) && same_non_ref_symbols(tuple_params, call_params); } }, - // Example: `val => val`, or `ref val => *val` - (PatKind::Binding(annot, _, pat_ident, _), _) => { - let new_expr = if let ( - BindingAnnotation::Ref | BindingAnnotation::RefMut, - ExprKind::Unary(UnOp::Deref, operand_expr), - ) = (annot, &expr.kind) - { - operand_expr - } else { - expr - }; - - if let ExprKind::Path(QPath::Resolved( + // Example: `val => val` + ( + PatKind::Binding(annot, _, pat_ident, _), + ExprKind::Path(QPath::Resolved( _, Path { segments: [first_seg, ..], .. }, - )) = new_expr.kind - { - return pat_ident.name == first_seg.ident.name; - } + )), + ) => { + return !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut) + && pat_ident.name == first_seg.ident.name; }, // Example: `Custom::TypeA => Custom::TypeB`, or `None => None` (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => { - return has_identical_segments(p_path.segments, e_path.segments); + return over(p_path.segments, e_path.segments, |p_seg, e_seg| { + p_seg.ident.name == e_seg.ident.name + }); }, // Example: `5 => 5` (PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => { @@ -171,27 +194,16 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { false } -fn has_identical_segments(left_segs: &[PathSegment<'_>], right_segs: &[PathSegment<'_>]) -> bool { - if left_segs.len() != right_segs.len() { +fn same_non_ref_symbols(pats: &[Pat<'_>], exprs: &[Expr<'_>]) -> bool { + if pats.len() != exprs.len() { return false; } - for i in 0..left_segs.len() { - if left_segs[i].ident.name != right_segs[i].ident.name { - return false; - } - } - true -} -fn has_same_non_ref_symbol(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { - if_chain! { - if let PatKind::Binding(annot, _, pat_ident, _) = pat.kind; - if !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); - if let ExprKind::Path(QPath::Resolved(_, Path {segments: [first_seg, ..], .. })) = expr.kind; - then { - return pat_ident.name == first_seg.ident.name; + for i in 0..pats.len() { + if !pat_same_as_expr(&pats[i], &exprs[i]) { + return false; } } - false + true } diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 76eaedea8a0d2..44857d61fef8f 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E cx, BYTES_NTH, expr.span, - &format!("called `.byte().nth()` on a `{}`", caller_type), + &format!("called `.bytes().nth()` on a `{}`", caller_type), "try", format!( "{}.as_bytes().get({})", diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs new file mode 100644 index 0000000000000..be9d4ad94fb8e --- /dev/null +++ b/clippy_lints/src/methods/err_expect.rs @@ -0,0 +1,60 @@ +use super::ERR_EXPECT; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item}; +use rustc_errors::Applicability; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_middle::ty::Ty; +use rustc_semver::RustcVersion; +use rustc_span::{sym, Span}; + +pub(super) fn check( + cx: &LateContext<'_>, + _expr: &rustc_hir::Expr<'_>, + recv: &rustc_hir::Expr<'_>, + msrv: Option<&RustcVersion>, + expect_span: Span, + err_span: Span, +) { + if_chain! { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + // Test the version to make sure the lint can be showed (expect_err has been + // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) + if meets_msrv(msrv, &msrvs::EXPECT_ERR); + + // Grabs the `Result` type + let result_type = cx.typeck_results().expr_ty(recv); + // Tests if the T type in a `Result` is not None + if let Some(data_type) = get_data_type(cx, result_type); + // Tests if the T type in a `Result` implements debug + if has_debug_impl(data_type, cx); + + then { + span_lint_and_sugg( + cx, + ERR_EXPECT, + err_span.to(expect_span), + "called `.err().expect()` on a `Result` value", + "try", + "expect_err".to_string(), + Applicability::MachineApplicable + ); + } + }; +} + +/// Given a `Result` type, return its data (`T`). +fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { + match ty.kind() { + ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::Result) => substs.types().next(), + _ => None, + } +} + +/// Given a type, very if the Debug trait has been impl'd +fn has_debug_impl<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { + cx.tcx + .get_diagnostic_item(sym::Debug) + .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) +} diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index c98cdfbca434e..9651a52be4e72 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -48,13 +48,11 @@ pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr), "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned), "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path), - "to_vec" => { - cx.tcx.impl_of_method(method_def_id) - .filter(|&impl_did| { - cx.tcx.type_of(impl_did).is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none() - }) - .is_some() - }, + "to_vec" => cx + .tcx + .impl_of_method(method_def_id) + .filter(|&impl_did| cx.tcx.type_of(impl_did).is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none()) + .is_some(), _ => false, } } diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index b93f1399eaeed..54c9ca435a447 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::{get_iterator_item_ty, is_copy}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy}; use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; +use rustc_span::sym; use std::ops::Not; use super::ITER_OVEREAGER_CLONED; @@ -20,9 +21,16 @@ pub(super) fn check<'tcx>( map_arg: &[hir::Expr<'_>], ) { // Check if it's iterator and get type associated with `Item`. - let inner_ty = match get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv)) { - Some(ty) => ty, - _ => return, + let inner_ty = if_chain! { + if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + let recv_ty = cx.typeck_results().expr_ty(recv); + if implements_trait(cx, recv_ty, iterator_trait_id, &[]); + if let Some(inner_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv)); + then { + inner_ty + } else { + return; + } }; match inner_ty.kind() { diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index f112b500d3d29..862a9578e6ff2 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -13,6 +13,7 @@ pub(super) fn check( expr: &hir::Expr<'_>, caller: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, + name: &str, _map_span: Span, ) { let caller_ty = cx.typeck_results().expr_ty(caller); @@ -29,7 +30,7 @@ pub(super) fn check( MAP_IDENTITY, sugg_span, "unnecessary map of the identity function", - "remove the call to `map`", + &format!("remove the call to `{}`", name), String::new(), Applicability::MachineApplicable, ) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9d4e1fa399401..70d021a1668eb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -9,6 +9,7 @@ mod chars_next_cmp_with_unwrap; mod clone_on_copy; mod clone_on_ref_ptr; mod cloned_instead_of_copied; +mod err_expect; mod expect_fun_call; mod expect_used; mod extend_with_drain; @@ -40,6 +41,7 @@ mod map_collect_result_unit; mod map_flatten; mod map_identity; mod map_unwrap_or; +mod needless_option_as_deref; mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; @@ -362,6 +364,29 @@ declare_clippy_lint! { "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `.err().expect()` calls on the `Result` type. + /// + /// ### Why is this bad? + /// `.expect_err()` can be called directly to avoid the extra type conversion from `err()`. + /// + /// ### Example + /// ```should_panic + /// let x: Result = Ok(10); + /// x.err().expect("Testing err().expect()"); + /// ``` + /// Use instead: + /// ```should_panic + /// let x: Result = Ok(10); + /// x.expect_err("Testing expect_err"); + /// ``` + #[clippy::version = "1.61.0"] + pub ERR_EXPECT, + style, + r#"using `.err().expect("")` when `.expect_err("")` can be used"# +} + declare_clippy_lint! { /// ### What it does /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and @@ -2055,7 +2080,7 @@ declare_clippy_lint! { /// Checks for use of `.collect::>().join("")` on iterators. /// /// ### Why is this bad? - /// `.collect::()` is more concise and usually more performant + /// `.collect::()` is more concise and might be more performant /// /// ### Example /// ```rust @@ -2070,15 +2095,42 @@ declare_clippy_lint! { /// println!("{}", output); /// ``` /// ### Known problems - /// While `.collect::()` is more performant in most cases, there are cases where + /// While `.collect::()` is sometimes more performant, there are cases where /// using `.collect::()` over `.collect::>().join("")` /// will prevent loop unrolling and will result in a negative performance impact. + /// + /// Additionlly, differences have been observed between aarch64 and x86_64 assembly output, + /// with aarch64 tending to producing faster assembly in more cases when using `.collect::()` #[clippy::version = "1.61.0"] pub UNNECESSARY_JOIN, pedantic, "using `.collect::>().join(\"\")` on an iterator" } +declare_clippy_lint! { + /// ### What it does + /// Checks for no-op uses of `Option::{as_deref, as_deref_mut}`, + /// for example, `Option<&T>::as_deref()` returns the same type. + /// + /// ### Why is this bad? + /// Redundant code and improving readability. + /// + /// ### Example + /// ```rust + /// let a = Some(&1); + /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32> + /// ``` + /// Could be written as: + /// ```rust + /// let a = Some(&1); + /// let b = a; + /// ``` + #[clippy::version = "1.57.0"] + pub NEEDLESS_OPTION_AS_DEREF, + complexity, + "no-op use of `deref` or `deref_mut` method to `Option`." +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2165,6 +2217,8 @@ impl_lint_pass!(Methods => [ NEEDLESS_SPLITN, UNNECESSARY_TO_OWNED, UNNECESSARY_JOIN, + ERR_EXPECT, + NEEDLESS_OPTION_AS_DEREF, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2397,6 +2451,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } }, + ("as_deref" | "as_deref_mut", []) => { + needless_option_as_deref::check(cx, expr, recv, name); + }, ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), @@ -2428,6 +2485,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("expect", [_]) => match method_call(recv) { Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), + Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, msrv, span, err_span), _ => expect_used::check(cx, expr, recv), }, ("extend", [arg]) => { @@ -2472,7 +2530,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } } }, - ("map", [m_arg]) => { + (name @ ("map" | "map_err"), [m_arg]) => { 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), @@ -2484,7 +2542,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio _ => {}, } } - map_identity::check(cx, expr, recv, m_arg, span); + map_identity::check(cx, expr, recv, m_arg, name, span); }, ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), (name @ "next", args @ []) => { diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs new file mode 100644 index 0000000000000..7030baf19ff5c --- /dev/null +++ b/clippy_lints/src/methods/needless_option_as_deref.rs @@ -0,0 +1,37 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::path_res; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::usage::local_used_after_expr; +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::NEEDLESS_OPTION_AS_DEREF; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name: &str) { + let typeck = cx.typeck_results(); + let outer_ty = typeck.expr_ty(expr); + + if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) { + if name == "as_deref_mut" && recv.is_syntactic_place_expr() { + let Res::Local(binding_id) = path_res(cx, recv) else { return }; + + if local_used_after_expr(cx, binding_id, recv) { + return; + } + } + + span_lint_and_sugg( + cx, + NEEDLESS_OPTION_AS_DEREF, + expr.span, + "derefed type is same as origin", + "try this", + snippet_opt(cx, recv.span).unwrap(), + Applicability::MachineApplicable, + ); + } +} diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index b8dfe99688066..0a393657267b0 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -1,17 +1,14 @@ -use std::{ - ffi::OsString, - path::{Component, Path}, -}; - use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext}; +use std::ffi::OsStr; +use std::path::{Component, Path}; declare_clippy_lint! { /// ### What it does - /// Checks that module layout uses only self named module files, bans mod.rs files. + /// Checks that module layout uses only self named module files, bans `mod.rs` files. /// /// ### Why is this bad? /// Having multiple module layout styles in a project can be confusing. @@ -40,7 +37,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks that module layout uses only mod.rs files. + /// Checks that module layout uses only `mod.rs` files. /// /// ### Why is this bad? /// Having multiple module layout styles in a project can be confusing. @@ -82,11 +79,7 @@ impl EarlyLintPass for ModStyle { let files = cx.sess().source_map().files(); - let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess().opts.working_dir { - p.to_string_lossy() - } else { - return; - }; + let RealFileName::LocalPath(trim_to_src) = &cx.sess().opts.working_dir else { return }; // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives // `[path, to]` but not foo @@ -97,26 +90,27 @@ impl EarlyLintPass for ModStyle { // `{ foo => path/to/foo.rs, .. } let mut file_map = FxHashMap::default(); for file in files.iter() { - match &file.name { - FileName::Real(RealFileName::LocalPath(lp)) - if lp.to_string_lossy().starts_with(trim_to_src.as_ref()) => - { - let p = lp.to_string_lossy(); - let path = Path::new(p.trim_start_matches(trim_to_src.as_ref())); - if let Some(stem) = path.file_stem() { - file_map.insert(stem.to_os_string(), (file, path.to_owned())); - } - process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders); - check_self_named_mod_exists(cx, path, file); - }, - _ => {}, + if let FileName::Real(RealFileName::LocalPath(lp)) = &file.name { + let path = if lp.is_relative() { + lp + } else if let Ok(relative) = lp.strip_prefix(trim_to_src) { + relative + } else { + continue; + }; + + if let Some(stem) = path.file_stem() { + file_map.insert(stem, (file, path)); + } + process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders); + check_self_named_mod_exists(cx, path, file); } } for folder in &folder_segments { if !mod_folders.contains(folder) { if let Some((file, path)) = file_map.get(folder) { - let mut correct = path.clone(); + let mut correct = path.to_path_buf(); correct.pop(); correct.push(folder); correct.push("mod.rs"); @@ -138,25 +132,17 @@ impl EarlyLintPass for ModStyle { /// For each `path` we add each folder component to `folder_segments` and if the file name /// is `mod.rs` we add it's parent folder to `mod_folders`. -fn process_paths_for_mod_files( - path: &Path, - folder_segments: &mut FxHashSet, - mod_folders: &mut FxHashSet, +fn process_paths_for_mod_files<'a>( + path: &'a Path, + folder_segments: &mut FxHashSet<&'a OsStr>, + mod_folders: &mut FxHashSet<&'a OsStr>, ) { let mut comp = path.components().rev().peekable(); let _ = comp.next(); if path.ends_with("mod.rs") { - mod_folders.insert(comp.peek().map(|c| c.as_os_str().to_owned()).unwrap_or_default()); + mod_folders.insert(comp.peek().map(|c| c.as_os_str()).unwrap_or_default()); } - let folders = comp - .filter_map(|c| { - if let Component::Normal(s) = c { - Some(s.to_os_string()) - } else { - None - } - }) - .collect::>(); + let folders = comp.filter_map(|c| if let Component::Normal(s) = c { Some(s) } else { None }); folder_segments.extend(folders); } diff --git a/clippy_lints/src/needless_option_as_deref.rs b/clippy_lints/src/needless_option_as_deref.rs deleted file mode 100644 index 9d3d7d1f24cbc..0000000000000 --- a/clippy_lints/src/needless_option_as_deref.rs +++ /dev/null @@ -1,65 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; -use clippy_utils::ty::is_type_diagnostic_item; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for no-op uses of Option::{as_deref,as_deref_mut}, - /// for example, `Option<&T>::as_deref()` returns the same type. - /// - /// ### Why is this bad? - /// Redundant code and improving readability. - /// - /// ### Example - /// ```rust - /// let a = Some(&1); - /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32> - /// ``` - /// Could be written as: - /// ```rust - /// let a = Some(&1); - /// let b = a; - /// ``` - #[clippy::version = "1.57.0"] - pub NEEDLESS_OPTION_AS_DEREF, - complexity, - "no-op use of `deref` or `deref_mut` method to `Option`." -} - -declare_lint_pass!(OptionNeedlessDeref=> [ - NEEDLESS_OPTION_AS_DEREF, -]); - -impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { - return; - } - let typeck = cx.typeck_results(); - let outer_ty = typeck.expr_ty(expr); - - if_chain! { - if is_type_diagnostic_item(cx,outer_ty,sym::Option); - if let ExprKind::MethodCall(path, [sub_expr], _) = expr.kind; - let symbol = path.ident.as_str(); - if symbol == "as_deref" || symbol == "as_deref_mut"; - if outer_ty == typeck.expr_ty(sub_expr); - then{ - span_lint_and_sugg( - cx, - NEEDLESS_OPTION_AS_DEREF, - expr.span, - "derefed type is same as origin", - "try this", - snippet_opt(cx,sub_expr.span).unwrap(), - Applicability::MachineApplicable - ); - } - } - } -} diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 6ef6b9a20aa4b..2f3007658ea62 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -78,6 +78,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; if is_panic(cx, macro_call.def_id) { + if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + return; + } + span_lint( cx, PANIC, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 5f453dc165558..48a2666a2e0ce 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -601,9 +601,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: }, // If the types match check for methods which exist on both types. e.g. `Vec::len` and // `slice::len` - ty::Adt(def, _) - if def.did() == args.ty_did => - { + ty::Adt(def, _) if def.did() == args.ty_did => { set_skip_flag(); }, _ => (), diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 02569bd3a476e..342f23f030cd0 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -410,9 +410,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if cx.tcx.is_diagnostic_item(sym::transmute, def_id); then { - // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts. - // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. - // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers. + // Avoid suggesting non-const operations in const contexts: + // - from/to bits (https://github.com/rust-lang/rust/issues/73736) + // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911) + // - char conversions (https://github.com/rust-lang/rust/issues/89259) let const_context = in_constant(cx, e.hir_id); let from_ty = cx.typeck_results().expr_ty_adjusted(arg); @@ -427,7 +428,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath) - | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg) + | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) diff --git a/clippy_lints/src/transmute/transmute_int_to_char.rs b/clippy_lints/src/transmute/transmute_int_to_char.rs index 3eb07b68992a8..9e1823c373bfd 100644 --- a/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -15,9 +15,10 @@ pub(super) fn check<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, + const_context: bool, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { - (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) => { + (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) if !const_context => { span_lint_and_then( cx, TRANSMUTE_INT_TO_CHAR, diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 7570bc2a7a8f0..786e7bfc56f6e 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -32,18 +32,20 @@ pub(super) fn check<'tcx>( "" }; + let snippet = snippet(cx, arg.span, ".."); + span_lint_and_sugg( cx, TRANSMUTE_BYTES_TO_STR, e.span, &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), "consider using", - format!( - "std::str::from_utf8{}({}).unwrap()", - postfix, - snippet(cx, arg.span, ".."), - ), - Applicability::Unspecified, + if const_context { + format!("std::str::from_utf8_unchecked{postfix}({snippet})") + } else { + format!("std::str::from_utf8{postfix}({snippet}).unwrap()") + }, + Applicability::MaybeIncorrect, ); triggered = true; } else { diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index e42c6c63ede0b..c8912a18f1854 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,16 +1,13 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_lint_allowed; -use clippy_utils::source::{indent_of, reindent_multiline, snippet}; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, HirId, Local, UnsafeSource}; -use rustc_lexer::TokenKind; -use rustc_lint::{LateContext, LateLintPass}; +use clippy_utils::source::walk_span_to_context; +use rustc_data_structures::sync::Lrc; +use rustc_hir::{Block, BlockCheckMode, UnsafeSource}; +use rustc_lexer::{tokenize, TokenKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::TyCtxt; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{BytePos, Span}; -use std::borrow::Cow; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Pos, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -18,6 +15,24 @@ declare_clippy_lint! { /// explaining why the unsafe operations performed inside /// the block are safe. /// + /// Note the comment must appear on the line(s) preceding the unsafe block + /// with nothing appearing in between. The following is ok: + /// ```ignore + /// foo( + /// // SAFETY: + /// // This is a valid safety comment + /// unsafe { *x } + /// ) + /// ``` + /// But neither of these are: + /// ```ignore + /// // SAFETY: + /// // This is not a valid safety comment + /// foo( + /// /* SAFETY: Neither is this */ unsafe { *x }, + /// ); + /// ``` + /// /// ### Why is this bad? /// Undocumented unsafe blocks can make it difficult to /// read and maintain code, as well as uncover unsoundness @@ -44,179 +59,139 @@ declare_clippy_lint! { "creating an unsafe block without explaining why it is safe" } -impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]); - -#[derive(Default)] -pub struct UndocumentedUnsafeBlocks { - pub local_level: u32, - pub local_span: Option, - // The local was already checked for an overall safety comment - // There is no need to continue checking the blocks in the local - pub local_checked: bool, - // Since we can only check the blocks from expanded macros - // We have to omit the suggestion due to the actual definition - // Not being available to us - pub macro_expansion: bool, -} +declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]); impl LateLintPass<'_> for UndocumentedUnsafeBlocks { fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) { - if_chain! { - if !self.local_checked; - if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id); - if !in_external_macro(cx.tcx.sess, block.span); - if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules; - if let Some(enclosing_scope_hir_id) = cx.tcx.hir().get_enclosing_scope(block.hir_id); - if self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, block.span) == Some(false); - then { - let mut span = block.span; - - if let Some(local_span) = self.local_span { - span = local_span; - - let result = self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, span); + if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + && !in_external_macro(cx.tcx.sess, block.span) + && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) + && !is_unsafe_from_proc_macro(cx, block) + && !block_has_safety_comment(cx, block) + { + let source_map = cx.tcx.sess.source_map(); + let span = if source_map.is_multiline(block.span) { + source_map.span_until_char(block.span, '\n') + } else { + block.span + }; - if result.unwrap_or(true) { - self.local_checked = true; - return; - } - } - - self.lint(cx, span); - } - } - } - - fn check_local(&mut self, cx: &LateContext<'_>, local: &'_ Local<'_>) { - if_chain! { - if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, local.hir_id); - if !in_external_macro(cx.tcx.sess, local.span); - if let Some(init) = local.init; - then { - self.visit_expr(init); - - if self.local_level > 0 { - self.local_span = Some(local.span); - } - } + span_lint_and_help( + cx, + UNDOCUMENTED_UNSAFE_BLOCKS, + span, + "unsafe block missing a safety comment", + None, + "consider adding a safety comment on the preceding line", + ); } } +} - fn check_block_post(&mut self, _: &LateContext<'_>, _: &'_ Block<'_>) { - self.local_level = self.local_level.saturating_sub(1); - - if self.local_level == 0 { - self.local_checked = false; - self.local_span = None; - } - } +fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { + let source_map = cx.sess().source_map(); + let file_pos = source_map.lookup_byte_offset(block.span.lo()); + file_pos + .sf + .src + .as_deref() + .and_then(|src| src.get(file_pos.pos.to_usize()..)) + .map_or(true, |src| !src.starts_with("unsafe")) } -impl<'v> Visitor<'v> for UndocumentedUnsafeBlocks { - fn visit_expr(&mut self, ex: &'v Expr<'v>) { - match ex.kind { - ExprKind::Block(_, _) => self.local_level = self.local_level.saturating_add(1), - _ => walk_expr(self, ex), +/// Checks if the lines immediately preceding the block contain a safety comment. +fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { + // This intentionally ignores text before the start of a function so something like: + // ``` + // // SAFETY: reason + // fn foo() { unsafe { .. } } + // ``` + // won't work. This is to avoid dealing with where such a comment should be place relative to + // attributes and doc comments. + + let source_map = cx.sess().source_map(); + let ctxt = block.span.ctxt(); + if ctxt != SyntaxContext::root() { + // From a macro expansion. Get the text from the start of the macro declaration to start of the unsafe block. + // macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; } + // ^--------------------------------------------^ + if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo()) + && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo()) + && Lrc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + macro_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines[macro_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + } else { + // Problem getting source text. Pretend a comment was found. + true } + } else if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo()) + && let Some(body) = cx.enclosing_body + && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root()) + && let Ok(body_line) = source_map.lookup_line(body_span.lo()) + && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + // Get the text from the start of function body to the unsafe block. + // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } + // ^-------------^ + body_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + } else { + // Problem getting source text. Pretend a comment was found. + true } } -impl UndocumentedUnsafeBlocks { - fn block_has_safety_comment(&mut self, tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span: Span) -> Option { - let map = tcx.hir(); - let source_map = tcx.sess.source_map(); - - let enclosing_scope_span = map.opt_span(enclosing_hir_id)?; - - let between_span = if block_span.from_expansion() { - self.macro_expansion = true; - enclosing_scope_span.with_hi(block_span.hi()).source_callsite() - } else { - self.macro_expansion = false; - enclosing_scope_span.to(block_span).source_callsite() - }; - - let file_name = source_map.span_to_filename(between_span); - let source_file = source_map.get_source_file(&file_name)?; - - let lex_start = (between_span.lo().0 - source_file.start_pos.0 + 1) as usize; - let lex_end = (between_span.hi().0 - source_file.start_pos.0) as usize; - let src_str = source_file.src.as_ref()?[lex_start..lex_end].to_string(); - - let source_start_pos = source_file.start_pos.0 as usize + lex_start; - - let mut pos = 0; - let mut comment = false; - - for token in rustc_lexer::tokenize(&src_str) { - match token.kind { - TokenKind::LineComment { doc_style: None } - | TokenKind::BlockComment { - doc_style: None, - terminated: true, - } => { - let comment_str = src_str[pos + 2..pos + token.len].to_ascii_uppercase(); - - if comment_str.contains("SAFETY:") { - comment = true; - } - }, - // We need to add all whitespace to `pos` before checking the comment's line number - TokenKind::Whitespace => {}, - _ => { - if comment { - // Get the line number of the "comment" (really wherever the trailing whitespace ended) - let comment_line_num = source_file - .lookup_file_pos(BytePos((source_start_pos + pos).try_into().unwrap())) - .0; - // Find the block/local's line number - let block_line_num = tcx.sess.source_map().lookup_char_pos(block_span.lo()).line; - - // Check the comment is immediately followed by the block/local - if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num { - return Some(true); - } - - comment = false; - } - }, +/// Checks if the given text has a safety comment for the immediately proceeding line. +fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool { + let mut lines = line_starts + .array_windows::<2>() + .rev() + .map_while(|[start, end]| { + src.get(start.to_usize() - offset..end.to_usize() - offset) + .map(|text| (start.to_usize(), text.trim_start())) + }) + .filter(|(_, text)| !text.is_empty()); + + let Some((line_start, line)) = lines.next() else { + return false; + }; + // Check for a sequence of line comments. + if line.starts_with("//") { + let mut line = line; + loop { + if line.to_ascii_uppercase().contains("SAFETY:") { + return true; + } + match lines.next() { + Some((_, x)) if x.starts_with("//") => line = x, + _ => return false, } - - pos += token.len; } - - Some(false) } - - fn lint(&self, cx: &LateContext<'_>, mut span: Span) { - let source_map = cx.tcx.sess.source_map(); - - if source_map.is_multiline(span) { - span = source_map.span_until_char(span, '\n'); + // No line comments; look for the start of a block comment. + // This will only find them if they are at the start of a line. + let (mut line_start, mut line) = (line_start, line); + loop { + if line.starts_with("/*") { + let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start(); + let mut tokens = tokenize(src); + return src[..tokens.next().unwrap().len] + .to_ascii_uppercase() + .contains("SAFETY:") + && tokens.all(|t| t.kind == TokenKind::Whitespace); } - - if self.macro_expansion { - span_lint_and_help( - cx, - UNDOCUMENTED_UNSAFE_BLOCKS, - span, - "unsafe block in macro expansion missing a safety comment", - None, - "consider adding a safety comment in the macro definition", - ); - } else { - let block_indent = indent_of(cx, span); - let suggestion = format!("// SAFETY: ...\n{}", snippet(cx, span, "..")); - - span_lint_and_sugg( - cx, - UNDOCUMENTED_UNSAFE_BLOCKS, - span, - "unsafe block missing a safety comment", - "consider adding a safety comment", - reindent_multiline(Cow::Borrowed(&suggestion), true, block_indent).to_string(), - Applicability::HasPlaceholders, - ); + match lines.next() { + Some(x) => (line_start, line) = x, + None => return false, } } } diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs deleted file mode 100644 index db652766705c4..0000000000000 --- a/clippy_lints/src/undropped_manually_drops.rs +++ /dev/null @@ -1,59 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_res; -use clippy_utils::ty::is_type_lang_item; -use rustc_hir::{lang_items, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. - /// - /// ### Why is this bad? - /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`. - /// - /// ### Known problems - /// Does not catch cases if the user binds `std::mem::drop` - /// to a different name and calls it that way. - /// - /// ### Example - /// ```rust - /// struct S; - /// drop(std::mem::ManuallyDrop::new(S)); - /// ``` - /// Use instead: - /// ```rust - /// struct S; - /// unsafe { - /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); - /// } - /// ``` - #[clippy::version = "1.49.0"] - pub UNDROPPED_MANUALLY_DROPS, - correctness, - "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value" -} - -declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); - -impl<'tcx> LateLintPass<'tcx> for UndroppedManuallyDrops { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(fun, [arg_0, ..]) = expr.kind; - if path_res(cx, fun).opt_def_id() == cx.tcx.get_diagnostic_item(sym::mem_drop); - let ty = cx.typeck_results().expr_ty(arg_0); - if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop); - then { - span_lint_and_help( - cx, - UNDROPPED_MANUALLY_DROPS, - expr.span, - "the inner value of this ManuallyDrop will not be dropped", - None, - "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop", - ); - } - } - } -} diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 09d671e11184d..f8e1021af0ea1 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -34,7 +34,7 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// struct Foo {} + /// struct Foo; /// impl Foo { /// fn new() -> Foo { /// Foo {} @@ -43,7 +43,7 @@ declare_clippy_lint! { /// ``` /// could be /// ```rust - /// struct Foo {} + /// struct Foo; /// impl Foo { /// fn new() -> Self { /// Self {} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 680b2eb1da723..271c3a3dd181c 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -156,7 +156,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED. /// /// The minimum rust version that the project supports (msrv: Option = None), diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index b3b241392fed5..25d74b8c49939 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -25,7 +25,7 @@ use rustc_hir::{ use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::mir::interpret::ConstValue; -use rustc_middle::ty::{self, subst::GenericArgKind}; +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy}; use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; @@ -889,7 +889,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { + Res::Def(DefKind::Const | DefKind::Static(..), def_id) => { if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) { if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind { let body = cx.tcx.hir().body(body_id); @@ -934,7 +934,16 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { // implementations of native types. Check lang items. let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); let lang_items = cx.tcx.lang_items(); - for item_def_id in lang_items.items().iter().flatten() { + // This list isn't complete, but good enough for our current list of paths. + let incoherent_impls = [ + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), + SimplifiedTypeGen::SliceSimplifiedType, + SimplifiedTypeGen::StrSimplifiedType, + ] + .iter() + .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); + for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { 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()..] { diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index b3fad6ce7b651..ca03b8010dd82 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -756,7 +756,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> { let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)); if match_type(self.cx, expr_ty, &paths::LINT); then { - if let hir::def::Res::Def(DefKind::Static, _) = path.res { + if let hir::def::Res::Def(DefKind::Static(..), _) = path.res { let lint_name = last_path_segment(qpath).ident.name; self.lints.push(sym_to_string(lint_name).to_ascii_lowercase()); } else if let Some(local) = get_parent_local(self.cx, expr) { diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index d3ed8da4499f8..0b1fd95c3453d 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.61" +version = "0.1.62" edition = "2021" publish = false diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 62e144398012d..a275bac4ce63d 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -77,19 +77,22 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, - ExprKind, FnDecl, ForeignItem, 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, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl, + ForeignItem, 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, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; use rustc_middle::ty as rustc_ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::{IntTy, UintTy, FloatTy}; -use rustc_middle::ty::fast_reject::SimplifiedTypeGen::*; +use rustc_middle::ty::fast_reject::SimplifiedTypeGen::{ + ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType, + PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType, +}; use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture}; +use rustc_middle::ty::{FloatTy, IntTy, UintTy}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -522,7 +525,7 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { let tcx = cx.tcx; let starts = find_primitive(tcx, base) .chain(find_crate(tcx, base)) - .flat_map(|id| item_child_by_name(tcx, id, first)); + .filter_map(|id| item_child_by_name(tcx, id, first)); for first in starts { let last = path diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index fce93153d96ec..0424e06720263 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -14,7 +14,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,53,0 { OR_PATTERNS, MANUAL_BITS } 1,52,0 { STR_SPLIT_ONCE } - 1,51,0 { BORROW_AS_PTR } + 1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS } 1,50,0 { BOOL_THEN } 1,47,0 { TAU } 1,46,0 { CONST_IF_MATCH } @@ -30,6 +30,6 @@ msrv_aliases! { 1,34,0 { TRY_FROM } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,28,0 { FROM_BOOL } - 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST } + 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 6f56f8d51365a..79e6e92dc0aaf 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -105,6 +105,8 @@ pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"]; pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"]; pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"]; pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"]; +pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"]; +pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"]; pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"]; pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"]; pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"]; diff --git a/doc/release.md b/doc/release.md index afe3033c288cf..c4f8f98938428 100644 --- a/doc/release.md +++ b/doc/release.md @@ -121,4 +121,25 @@ happened a stable backport, make sure to re-merge those changes just as with the For this see the document on [how to update the changelog]. +If you don't have time to do a complete changelog update right away, just update +the following parts: + +- Remove the `(beta)` from the new stable version: + + ```markdown + ## Rust 1.XX (beta) -> ## Rust 1.XX + ``` + +- Update the release date line of the new stable version: + + ```markdown + Current beta, release 20YY-MM-DD -> Current stable, released 20YY-MM-DD + ``` + +- Update the release date line of the previous stable version: + + ```markdown + Current stable, released 20YY-MM-DD -> Released 20YY-MM-DD + ``` + [how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md diff --git a/rust-toolchain b/rust-toolchain index 5befb856a0234..bb29c71e9f455 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-03-24" +channel = "nightly-2022-04-07" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/driver.rs b/src/driver.rs index bc1b0d7457559..00dc916b217ca 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -165,8 +165,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); - let fallback_bundle = rustc_errors::fallback_fluent_bundle(false) - .expect("failed to load fallback fluent bundle"); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle"); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, diff --git a/tests/fmt.rs b/tests/check-fmt.rs similarity index 100% rename from tests/fmt.rs rename to tests/check-fmt.rs diff --git a/tests/ui-cargo/module_style/fail_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_mod/src/main.stderr index af4c298b31085..e2010e9981315 100644 --- a/tests/ui-cargo/module_style/fail_mod/src/main.stderr +++ b/tests/ui-cargo/module_style/fail_mod/src/main.stderr @@ -1,19 +1,19 @@ -error: `mod.rs` files are required, found `/bad/inner.rs` +error: `mod.rs` files are required, found `bad/inner.rs` --> $DIR/bad/inner.rs:1:1 | LL | pub mod stuff; | ^ | = note: `-D clippy::self-named-module-files` implied by `-D warnings` - = help: move `/bad/inner.rs` to `/bad/inner/mod.rs` + = help: move `bad/inner.rs` to `bad/inner/mod.rs` -error: `mod.rs` files are required, found `/bad/inner/stuff.rs` +error: `mod.rs` files are required, found `bad/inner/stuff.rs` --> $DIR/bad/inner/stuff.rs:1:1 | LL | pub mod most; | ^ | - = help: move `/bad/inner/stuff.rs` to `/bad/inner/stuff/mod.rs` + = help: move `bad/inner/stuff.rs` to `bad/inner/stuff/mod.rs` error: aborting due to 2 previous errors diff --git a/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr index 11e15db7fb96b..f91940209383f 100644 --- a/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr +++ b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr @@ -1,11 +1,11 @@ -error: `mod.rs` files are not allowed, found `/bad/mod.rs` +error: `mod.rs` files are not allowed, found `bad/mod.rs` --> $DIR/bad/mod.rs:1:1 | LL | pub struct Thing; | ^ | = note: `-D clippy::mod-module-files` implied by `-D warnings` - = help: move `/bad/mod.rs` to `/bad.rs` + = help: move `bad/mod.rs` to `bad.rs` error: aborting due to previous error diff --git a/tests/ui-internal/check_clippy_version_attribute.stderr b/tests/ui-internal/check_clippy_version_attribute.stderr index 9302e02ccb9c4..67e1a07b7f5fa 100644 --- a/tests/ui-internal/check_clippy_version_attribute.stderr +++ b/tests/ui-internal/check_clippy_version_attribute.stderr @@ -46,11 +46,6 @@ LL | | report_in_external_macro: true LL | | } | |_^ | -note: the lint level is defined here - --> $DIR/check_clippy_version_attribute.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` = help: please use a `clippy::version` attribute, see `doc/adding_lints.md` = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-toml/struct_excessive_bools/test.rs b/tests/ui-toml/struct_excessive_bools/test.rs index 242984680e163..32dd80246fab4 100644 --- a/tests/ui-toml/struct_excessive_bools/test.rs +++ b/tests/ui-toml/struct_excessive_bools/test.rs @@ -4,6 +4,6 @@ struct S { a: bool, } -struct Foo {} +struct Foo; fn main() {} diff --git a/tests/ui/auxiliary/proc_macro_unsafe.rs b/tests/ui/auxiliary/proc_macro_unsafe.rs new file mode 100644 index 0000000000000..3c40f77469b82 --- /dev/null +++ b/tests/ui/auxiliary/proc_macro_unsafe.rs @@ -0,0 +1,18 @@ +// compile-flags: --emit=link +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree}; + +#[proc_macro] +pub fn unsafe_block(input: TokenStream) -> TokenStream { + let span = input.into_iter().next().unwrap().span(); + TokenStream::from_iter([TokenTree::Ident(Ident::new("unsafe", span)), { + let mut group = Group::new(Delimiter::Brace, TokenStream::new()); + group.set_span(span); + TokenTree::Group(group) + }]) +} diff --git a/tests/ui/bytes_nth.stderr b/tests/ui/bytes_nth.stderr index 8a7afa934502d..9851d4791d8db 100644 --- a/tests/ui/bytes_nth.stderr +++ b/tests/ui/bytes_nth.stderr @@ -1,4 +1,4 @@ -error: called `.byte().nth()` on a `String` +error: called `.bytes().nth()` on a `String` --> $DIR/bytes_nth.rs:8:13 | LL | let _ = s.bytes().nth(3); @@ -6,13 +6,13 @@ LL | let _ = s.bytes().nth(3); | = note: `-D clippy::bytes-nth` implied by `-D warnings` -error: called `.byte().nth()` on a `String` +error: called `.bytes().nth()` on a `String` --> $DIR/bytes_nth.rs:9:14 | LL | let _ = &s.bytes().nth(3); | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)` -error: called `.byte().nth()` on a `str` +error: called `.bytes().nth()` on a `str` --> $DIR/bytes_nth.rs:10:13 | LL | let _ = s[..].bytes().nth(3); diff --git a/tests/ui/case_sensitive_file_extension_comparisons.rs b/tests/ui/case_sensitive_file_extension_comparisons.rs index 68719c2bc6d05..0d65071af15ed 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -2,7 +2,7 @@ use std::string::String; -struct TestStruct {} +struct TestStruct; impl TestStruct { fn ends_with(self, arg: &str) {} diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index 2e31ad3172ee8..cf85a5ca931dd 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -7,7 +7,7 @@ clippy::cast_sign_loss, clippy::cast_possible_wrap )] -#[allow(clippy::no_effect, clippy::unnecessary_operation)] +#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)] fn main() { // Test clippy::cast_precision_loss let x0 = 1i32; diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed new file mode 100644 index 0000000000000..4ec2465be06db --- /dev/null +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -0,0 +1,8 @@ +// run-rustfix +#![warn(clippy::cast_abs_to_unsigned)] + +fn main() { + let x: i32 = -42; + let y: u32 = x.unsigned_abs(); + println!("The absolute value of {} is {}", x, y); +} diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs new file mode 100644 index 0000000000000..59b9c8c367883 --- /dev/null +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -0,0 +1,8 @@ +// run-rustfix +#![warn(clippy::cast_abs_to_unsigned)] + +fn main() { + let x: i32 = -42; + let y: u32 = x.abs() as u32; + println!("The absolute value of {} is {}", x, y); +} diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr new file mode 100644 index 0000000000000..eb12857357a44 --- /dev/null +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -0,0 +1,10 @@ +error: casting the result of `i32::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:6:18 + | +LL | let y: u32 = x.abs() as u32; + | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` + | + = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_alignment.rs index 659591fffbecd..e4e7290a30e9e 100644 --- a/tests/ui/cast_alignment.rs +++ b/tests/ui/cast_alignment.rs @@ -1,6 +1,7 @@ //! Test casts for alignment issues #![feature(rustc_private)] +#![feature(core_intrinsics)] extern crate libc; #[warn(clippy::cast_ptr_alignment)] @@ -34,4 +35,17 @@ fn main() { (&1u32 as *const u32 as *const libc::c_void) as *const u32; // For ZST, we should trust the user. See #4256 (&1u32 as *const u32 as *const ()) as *const u32; + + // Issue #2881 + let mut data = [0u8, 0u8]; + unsafe { + let ptr = &data as *const [u8; 2] as *const u8; + let _ = (ptr as *const u16).read_unaligned(); + let _ = core::ptr::read_unaligned(ptr as *const u16); + let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16); + let ptr = &mut data as *mut [u8; 2] as *mut u8; + let _ = (ptr as *mut u16).write_unaligned(0); + let _ = core::ptr::write_unaligned(ptr as *mut u16, 0); + let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0); + } } diff --git a/tests/ui/cast_alignment.stderr b/tests/ui/cast_alignment.stderr index aedd368445554..5df2b5b1094be 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:18:5 + --> $DIR/cast_alignment.rs:19: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:19:5 + --> $DIR/cast_alignment.rs:20: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:22:5 + --> $DIR/cast_alignment.rs:23: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:23:5 + --> $DIR/cast_alignment.rs:24:5 | LL | (&mut 1u8 as *mut u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/crashes/ice-2774.rs b/tests/ui/crashes/ice-2774.rs index d44b0fae82001..88cfa1f923c0b 100644 --- a/tests/ui/crashes/ice-2774.rs +++ b/tests/ui/crashes/ice-2774.rs @@ -8,7 +8,7 @@ pub struct Bar { } #[derive(Eq, PartialEq, Debug, Hash)] -pub struct Foo {} +pub struct Foo; #[allow(clippy::implicit_hasher)] // This should not cause a "cannot relate bound region" ICE. diff --git a/tests/ui/crashes/ice-6179.rs b/tests/ui/crashes/ice-6179.rs index 8d9a1af8ff118..4fe92d356c44d 100644 --- a/tests/ui/crashes/ice-6179.rs +++ b/tests/ui/crashes/ice-6179.rs @@ -4,7 +4,7 @@ #![warn(clippy::use_self)] #![allow(dead_code)] -struct Foo {} +struct Foo; impl Foo { fn new() -> Self { diff --git a/tests/ui/crashes/ice-6792.rs b/tests/ui/crashes/ice-6792.rs index 0e2ab1a39b82f..9cbafc716b500 100644 --- a/tests/ui/crashes/ice-6792.rs +++ b/tests/ui/crashes/ice-6792.rs @@ -7,7 +7,7 @@ trait Trait { fn broken() -> Self::Ty; } -struct Foo {} +struct Foo; impl Trait for Foo { type Ty = Foo; diff --git a/tests/ui/crashes/ice-7868.stderr b/tests/ui/crashes/ice-7868.stderr index 111350a6280dc..1a33e647588f1 100644 --- a/tests/ui/crashes/ice-7868.stderr +++ b/tests/ui/crashes/ice-7868.stderr @@ -5,11 +5,7 @@ LL | unsafe { 0 }; | ^^^^^^^^^^^^ | = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL ~ unsafe { 0 }; - | + = help: consider adding a safety comment on the preceding line error: aborting due to previous error diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.rs b/tests/ui/crashes/needless_lifetimes_impl_trait.rs index 676564b2445d5..376ff97ba6036 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.rs +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.rs @@ -3,7 +3,7 @@ trait Foo {} -struct Bar {} +struct Bar; struct Baz<'a> { bar: &'a Bar, diff --git a/tests/ui/crashes/regressions.rs b/tests/ui/crashes/regressions.rs index a41bcb33b4460..6f9d98bbfe7f3 100644 --- a/tests/ui/crashes/regressions.rs +++ b/tests/ui/crashes/regressions.rs @@ -6,6 +6,6 @@ pub fn foo(bar: *const u8) { // Regression test for https://github.com/rust-lang/rust-clippy/issues/4917 /// { + println!("{}", $crate::hygienic::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +mod unhygienic { + #[macro_export] + macro_rules! print_message_unhygienic { + () => { + println!("{}", $crate::unhygienic::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +mod unhygienic_intentionally { + // For cases where the use of `crate` is intentional, applying `allow` to the macro definition + // should suppress the lint. + #[allow(clippy::crate_in_macro_def)] + #[macro_export] + macro_rules! print_message_unhygienic_intentionally { + () => { + println!("{}", crate::CALLER_PROVIDED_MESSAGE); + }; + } +} + +#[macro_use] +mod not_exported { + macro_rules! print_message_not_exported { + () => { + println!("{}", crate::not_exported::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +fn main() { + print_message_hygienic!(); + print_message_unhygienic!(); + print_message_unhygienic_intentionally!(); + print_message_not_exported!(); +} + +pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!"; diff --git a/tests/ui/crate_in_macro_def.rs b/tests/ui/crate_in_macro_def.rs new file mode 100644 index 0000000000000..ac456108e4ab1 --- /dev/null +++ b/tests/ui/crate_in_macro_def.rs @@ -0,0 +1,56 @@ +// run-rustfix +#![warn(clippy::crate_in_macro_def)] + +mod hygienic { + #[macro_export] + macro_rules! print_message_hygienic { + () => { + println!("{}", $crate::hygienic::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +mod unhygienic { + #[macro_export] + macro_rules! print_message_unhygienic { + () => { + println!("{}", crate::unhygienic::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +mod unhygienic_intentionally { + // For cases where the use of `crate` is intentional, applying `allow` to the macro definition + // should suppress the lint. + #[allow(clippy::crate_in_macro_def)] + #[macro_export] + macro_rules! print_message_unhygienic_intentionally { + () => { + println!("{}", crate::CALLER_PROVIDED_MESSAGE); + }; + } +} + +#[macro_use] +mod not_exported { + macro_rules! print_message_not_exported { + () => { + println!("{}", crate::not_exported::MESSAGE); + }; + } + + pub const MESSAGE: &str = "Hello!"; +} + +fn main() { + print_message_hygienic!(); + print_message_unhygienic!(); + print_message_unhygienic_intentionally!(); + print_message_not_exported!(); +} + +pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!"; diff --git a/tests/ui/crate_in_macro_def.stderr b/tests/ui/crate_in_macro_def.stderr new file mode 100644 index 0000000000000..9ac5937dcc063 --- /dev/null +++ b/tests/ui/crate_in_macro_def.stderr @@ -0,0 +1,10 @@ +error: `crate` references the macro call's crate + --> $DIR/crate_in_macro_def.rs:19:28 + | +LL | println!("{}", crate::unhygienic::MESSAGE); + | ^^^^^ help: to reference the macro definition's crate, use: `$crate` + | + = note: `-D clippy::crate-in-macro-def` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed index 1b0e7544e79c6..e0b4a2f694239 100644 --- a/tests/ui/default_numeric_fallback_f64.fixed +++ b/tests/ui/default_numeric_fallback_f64.fixed @@ -134,7 +134,7 @@ mod enum_ctor { } mod method_calls { - struct StructForMethodCallTest {} + struct StructForMethodCallTest; impl StructForMethodCallTest { fn concrete_arg(&self, f: f64) {} diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs index e9687777bbd0b..50bbb6eec6c70 100644 --- a/tests/ui/default_numeric_fallback_f64.rs +++ b/tests/ui/default_numeric_fallback_f64.rs @@ -134,7 +134,7 @@ mod enum_ctor { } mod method_calls { - struct StructForMethodCallTest {} + struct StructForMethodCallTest; impl StructForMethodCallTest { fn concrete_arg(&self, f: f64) {} diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index 55c082fcb19fb..bded9e2c0e801 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -133,7 +133,7 @@ mod enum_ctor { } mod method_calls { - struct StructForMethodCallTest {} + struct StructForMethodCallTest; impl StructForMethodCallTest { fn concrete_arg(&self, x: i32) {} diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index e0a4828ce9ff6..3fceefa551c78 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -133,7 +133,7 @@ mod enum_ctor { } mod method_calls { - struct StructForMethodCallTest {} + struct StructForMethodCallTest; impl StructForMethodCallTest { fn concrete_arg(&self, x: i32) {} diff --git a/tests/ui/drop_forget_copy.rs b/tests/ui/drop_forget_copy.rs index 9ddd6d64701a6..7c7a9ecff67f5 100644 --- a/tests/ui/drop_forget_copy.rs +++ b/tests/ui/drop_forget_copy.rs @@ -5,7 +5,7 @@ use std::mem::{drop, forget}; use std::vec::Vec; #[derive(Copy, Clone)] -struct SomeStruct {} +struct SomeStruct; struct AnotherStruct { x: u8, diff --git a/tests/ui/drop_forget_copy.stderr b/tests/ui/drop_forget_copy.stderr index 01de0be7caea9..88228afae89c0 100644 --- a/tests/ui/drop_forget_copy.stderr +++ b/tests/ui/drop_forget_copy.stderr @@ -5,7 +5,7 @@ LL | drop(s1); | ^^^^^^^^ | = note: `-D clippy::drop-copy` implied by `-D warnings` -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:33:10 | LL | drop(s1); @@ -17,7 +17,7 @@ error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a LL | drop(s2); | ^^^^^^^^ | -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:34:10 | LL | drop(s2); @@ -29,7 +29,7 @@ error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a LL | drop(s4); | ^^^^^^^^ | -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:36:10 | LL | drop(s4); @@ -42,7 +42,7 @@ LL | forget(s1); | ^^^^^^^^^^ | = note: `-D clippy::forget-copy` implied by `-D warnings` -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:39:12 | LL | forget(s1); @@ -54,7 +54,7 @@ error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetti LL | forget(s2); | ^^^^^^^^^^ | -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:40:12 | LL | forget(s2); @@ -66,7 +66,7 @@ error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetti LL | forget(s4); | ^^^^^^^^^^ | -note: argument has type SomeStruct +note: argument has type `SomeStruct` --> $DIR/drop_forget_copy.rs:42:12 | LL | forget(s4); diff --git a/tests/ui/drop_non_drop.rs b/tests/ui/drop_non_drop.rs new file mode 100644 index 0000000000000..5a0ebde82c5d4 --- /dev/null +++ b/tests/ui/drop_non_drop.rs @@ -0,0 +1,40 @@ +#![warn(clippy::drop_non_drop)] + +use core::mem::drop; + +fn make_result(t: T) -> Result { + Ok(t) +} + +#[must_use] +fn must_use(t: T) -> T { + t +} + +fn drop_generic(t: T) { + // Don't lint + drop(t) +} + +fn main() { + struct Foo; + // Lint + drop(Foo); + // Don't lint + drop(make_result(Foo)); + // Don't lint + drop(must_use(Foo)); + + struct Bar; + impl Drop for Bar { + fn drop(&mut self) {} + } + // Don't lint + drop(Bar); + + struct Baz(T); + // Lint + drop(Baz(Foo)); + // Don't lint + drop(Baz(Bar)); +} diff --git a/tests/ui/drop_non_drop.stderr b/tests/ui/drop_non_drop.stderr new file mode 100644 index 0000000000000..f73068901c503 --- /dev/null +++ b/tests/ui/drop_non_drop.stderr @@ -0,0 +1,27 @@ +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes + --> $DIR/drop_non_drop.rs:22:5 + | +LL | drop(Foo); + | ^^^^^^^^^ + | + = note: `-D clippy::drop-non-drop` implied by `-D warnings` +note: argument has type `main::Foo` + --> $DIR/drop_non_drop.rs:22:10 + | +LL | drop(Foo); + | ^^^ + +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes + --> $DIR/drop_non_drop.rs:37:5 + | +LL | drop(Baz(Foo)); + | ^^^^^^^^^^^^^^ + | +note: argument has type `main::Baz` + --> $DIR/drop_non_drop.rs:37:10 + | +LL | drop(Baz(Foo)); + | ^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index e1a15c609fd23..7de0b0bbdf9ae 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -1,7 +1,7 @@ #![warn(clippy::drop_ref)] #![allow(clippy::toplevel_ref_arg)] #![allow(clippy::map_err_ignore)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::drop_non_drop)] use std::mem::drop; diff --git a/tests/ui/empty_structs_with_brackets.fixed b/tests/ui/empty_structs_with_brackets.fixed new file mode 100644 index 0000000000000..80f07603b8d4f --- /dev/null +++ b/tests/ui/empty_structs_with_brackets.fixed @@ -0,0 +1,25 @@ +// run-rustfix +#![warn(clippy::empty_structs_with_brackets)] +#![allow(dead_code)] + +pub struct MyEmptyStruct; // should trigger lint +struct MyEmptyTupleStruct; // should trigger lint + +// should not trigger lint +struct MyCfgStruct { + #[cfg(feature = "thisisneverenabled")] + field: u8, +} + +// should not trigger lint +struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8); + +// should not trigger lint +struct MyStruct { + field: u8, +} +struct MyTupleStruct(usize, String); // should not trigger lint +struct MySingleTupleStruct(usize); // should not trigger lint +struct MyUnitLikeStruct; // should not trigger lint + +fn main() {} diff --git a/tests/ui/empty_structs_with_brackets.rs b/tests/ui/empty_structs_with_brackets.rs new file mode 100644 index 0000000000000..1d1ed4c769025 --- /dev/null +++ b/tests/ui/empty_structs_with_brackets.rs @@ -0,0 +1,25 @@ +// run-rustfix +#![warn(clippy::empty_structs_with_brackets)] +#![allow(dead_code)] + +pub struct MyEmptyStruct {} // should trigger lint +struct MyEmptyTupleStruct(); // should trigger lint + +// should not trigger lint +struct MyCfgStruct { + #[cfg(feature = "thisisneverenabled")] + field: u8, +} + +// should not trigger lint +struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8); + +// should not trigger lint +struct MyStruct { + field: u8, +} +struct MyTupleStruct(usize, String); // should not trigger lint +struct MySingleTupleStruct(usize); // should not trigger lint +struct MyUnitLikeStruct; // should not trigger lint + +fn main() {} diff --git a/tests/ui/empty_structs_with_brackets.stderr b/tests/ui/empty_structs_with_brackets.stderr new file mode 100644 index 0000000000000..0308cb5571af2 --- /dev/null +++ b/tests/ui/empty_structs_with_brackets.stderr @@ -0,0 +1,19 @@ +error: found empty brackets on struct declaration + --> $DIR/empty_structs_with_brackets.rs:5:25 + | +LL | pub struct MyEmptyStruct {} // should trigger lint + | ^^^ + | + = note: `-D clippy::empty-structs-with-brackets` implied by `-D warnings` + = help: remove the brackets + +error: found empty brackets on struct declaration + --> $DIR/empty_structs_with_brackets.rs:6:26 + | +LL | struct MyEmptyTupleStruct(); // should trigger lint + | ^^^ + | + = help: remove the brackets + +error: aborting due to 2 previous errors + diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed new file mode 100644 index 0000000000000..7e18d70bae400 --- /dev/null +++ b/tests/ui/err_expect.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +struct MyTypeNonDebug; + +#[derive(Debug)] +struct MyTypeDebug; + +fn main() { + let test_debug: Result = Ok(MyTypeDebug); + test_debug.expect_err("Testing debug type"); + + let test_non_debug: Result = Ok(MyTypeNonDebug); + test_non_debug.err().expect("Testing non debug type"); +} diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs new file mode 100644 index 0000000000000..bf8c3c9fb8c98 --- /dev/null +++ b/tests/ui/err_expect.rs @@ -0,0 +1,14 @@ +// run-rustfix + +struct MyTypeNonDebug; + +#[derive(Debug)] +struct MyTypeDebug; + +fn main() { + let test_debug: Result = Ok(MyTypeDebug); + test_debug.err().expect("Testing debug type"); + + let test_non_debug: Result = Ok(MyTypeNonDebug); + test_non_debug.err().expect("Testing non debug type"); +} diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr new file mode 100644 index 0000000000000..ffd97e00a5c09 --- /dev/null +++ b/tests/ui/err_expect.stderr @@ -0,0 +1,10 @@ +error: called `.err().expect()` on a `Result` value + --> $DIR/err_expect.rs:10:16 + | +LL | test_debug.err().expect("Testing debug type"); + | ^^^^^^^^^^^^ help: try: `expect_err` + | + = note: `-D clippy::err-expect` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs index 1442ee08e7546..f805bcc9ba8af 100644 --- a/tests/ui/fn_params_excessive_bools.rs +++ b/tests/ui/fn_params_excessive_bools.rs @@ -20,7 +20,7 @@ fn h(_: bool, _: bool, _: bool) {} fn e(_: S, _: S, _: Box, _: Vec) {} fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} -struct S {} +struct S; trait Trait { fn f(_: bool, _: bool, _: bool, _: bool); fn g(_: bool, _: bool, _: bool, _: Vec); diff --git a/tests/ui/forget_non_drop.rs b/tests/ui/forget_non_drop.rs new file mode 100644 index 0000000000000..7580cf95ebfa8 --- /dev/null +++ b/tests/ui/forget_non_drop.rs @@ -0,0 +1,27 @@ +#![warn(clippy::forget_non_drop)] + +use core::mem::forget; + +fn forget_generic(t: T) { + // Don't lint + forget(t) +} + +fn main() { + struct Foo; + // Lint + forget(Foo); + + struct Bar; + impl Drop for Bar { + fn drop(&mut self) {} + } + // Don't lint + forget(Bar); + + struct Baz(T); + // Lint + forget(Baz(Foo)); + // Don't lint + forget(Baz(Bar)); +} diff --git a/tests/ui/forget_non_drop.stderr b/tests/ui/forget_non_drop.stderr new file mode 100644 index 0000000000000..03fb00960a447 --- /dev/null +++ b/tests/ui/forget_non_drop.stderr @@ -0,0 +1,27 @@ +error: call to `std::mem::forget` with a value that does not implement `Drop`. Forgetting such a type is the same as dropping it + --> $DIR/forget_non_drop.rs:13:5 + | +LL | forget(Foo); + | ^^^^^^^^^^^ + | + = note: `-D clippy::forget-non-drop` implied by `-D warnings` +note: argument has type `main::Foo` + --> $DIR/forget_non_drop.rs:13:12 + | +LL | forget(Foo); + | ^^^ + +error: call to `std::mem::forget` with a value that does not implement `Drop`. Forgetting such a type is the same as dropping it + --> $DIR/forget_non_drop.rs:24:5 + | +LL | forget(Baz(Foo)); + | ^^^^^^^^^^^^^^^^ + | +note: argument has type `main::Baz` + --> $DIR/forget_non_drop.rs:24:12 + | +LL | forget(Baz(Foo)); + | ^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/forget_ref.rs b/tests/ui/forget_ref.rs index c49e6756a6c5b..6c8c4c9c0edec 100644 --- a/tests/ui/forget_ref.rs +++ b/tests/ui/forget_ref.rs @@ -1,6 +1,6 @@ #![warn(clippy::forget_ref)] #![allow(clippy::toplevel_ref_arg)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::forget_non_drop)] use std::mem::forget; diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index 12bbda71f4348..edc3fe1aec13a 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -66,4 +66,13 @@ fn main() { let b = a << 0; // no error: non-integer 1 * Meter; // no error: non-integer + + 2 % 3; + -2 % 3; + 2 % -3 + x; + -2 % -3 + x; + x + 1 % 3; + (x + 1) % 3; // no error + 4 % 3; // no error + 4 % -3; // no error } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 0103cf5457e81..706f01a3dd6c4 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -78,5 +78,35 @@ error: the operation is ineffective. Consider reducing it to `x` LL | x >> &0; | ^^^^^^^ -error: aborting due to 13 previous errors +error: the operation is ineffective. Consider reducing it to `2` + --> $DIR/identity_op.rs:70:5 + | +LL | 2 % 3; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `-2` + --> $DIR/identity_op.rs:71:5 + | +LL | -2 % 3; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `2` + --> $DIR/identity_op.rs:72:5 + | +LL | 2 % -3 + x; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `-2` + --> $DIR/identity_op.rs:73:5 + | +LL | -2 % -3 + x; + | ^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:74:9 + | +LL | x + 1 % 3; + | ^^^^^ + +error: aborting due to 18 previous errors diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index 639fecb8927bd..2549c9f32f904 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -30,7 +30,7 @@ where } #[derive(Copy, Clone)] -struct Kitten {} +struct Kitten; impl Kitten { // badly named method fn to_vec(self) -> Kitten { @@ -44,7 +44,7 @@ impl Borrow for Kitten { } } -struct BorrowedKitten {} +struct BorrowedKitten; impl ToOwned for BorrowedKitten { type Owned = Kitten; fn to_owned(&self) -> Kitten { diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index ca8ca53c80c3f..45a430edcb589 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -1,18 +1,34 @@ +#![feature(inline_const)] #![warn(clippy::indexing_slicing)] // We also check the out_of_bounds_indexing lint here, because it lints similar things and // we want to avoid false positives. #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(const_err, clippy::no_effect, clippy::unnecessary_operation)] + +const ARR: [i32; 2] = [1, 2]; +const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr. +const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + +const fn idx() -> usize { + 1 +} +const fn idx4() -> usize { + 4 +} fn main() { let x = [1, 2, 3, 4]; let index: usize = 1; x[index]; - x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. - x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + x[4]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + x[1 << 3]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. x[0]; // Ok, should not produce stderr. x[3]; // Ok, should not produce stderr. + x[const { idx() }]; // Ok, should not produce stderr. + x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + const { &ARR[idx()] }; // Ok, should not produce stderr. + const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. let y = &x; y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 @@ -25,7 +41,7 @@ fn main() { const N: usize = 15; // Out of bounds const M: usize = 3; // In bounds - x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + x[N]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. x[M]; // Ok, should not produce stderr. v[N]; v[M]; diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 76ecec3348400..83a36f407d5d8 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -1,5 +1,17 @@ +error[E0080]: evaluation of `main::{constant#3}::<&i32>` failed + --> $DIR/indexing_slicing_index.rs:31:14 + | +LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +error[E0080]: erroneous constant used + --> $DIR/indexing_slicing_index.rs:31:5 + | +LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + error: indexing may panic - --> $DIR/indexing_slicing_index.rs:10:5 + --> $DIR/indexing_slicing_index.rs:22:5 | LL | x[index]; | ^^^^^^^^ @@ -8,7 +20,7 @@ LL | x[index]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:22:5 + --> $DIR/indexing_slicing_index.rs:38:5 | LL | v[0]; | ^^^^ @@ -16,7 +28,7 @@ LL | v[0]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:23:5 + --> $DIR/indexing_slicing_index.rs:39:5 | LL | v[10]; | ^^^^^ @@ -24,7 +36,7 @@ LL | v[10]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:24:5 + --> $DIR/indexing_slicing_index.rs:40:5 | LL | v[1 << 3]; | ^^^^^^^^^ @@ -32,7 +44,7 @@ LL | v[1 << 3]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:30:5 + --> $DIR/indexing_slicing_index.rs:46:5 | LL | v[N]; | ^^^^ @@ -40,12 +52,13 @@ LL | v[N]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:31:5 + --> $DIR/indexing_slicing_index.rs:47:5 | LL | v[M]; | ^^^^ | = help: consider using `.get(n)` or `.get_mut(n)` instead -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/iter_nth_zero.fixed b/tests/ui/iter_nth_zero.fixed index b54147c94d192..f23671c26e4cc 100644 --- a/tests/ui/iter_nth_zero.fixed +++ b/tests/ui/iter_nth_zero.fixed @@ -3,7 +3,7 @@ #![warn(clippy::iter_nth_zero)] use std::collections::HashSet; -struct Foo {} +struct Foo; impl Foo { fn nth(&self, index: usize) -> usize { diff --git a/tests/ui/iter_nth_zero.rs b/tests/ui/iter_nth_zero.rs index b92c7d18adb4f..7c968d4984571 100644 --- a/tests/ui/iter_nth_zero.rs +++ b/tests/ui/iter_nth_zero.rs @@ -3,7 +3,7 @@ #![warn(clippy::iter_nth_zero)] use std::collections::HashSet; -struct Foo {} +struct Foo; impl Foo { fn nth(&self, index: usize) -> usize { diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index a9041671101b5..56761ebbcb80b 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![allow(dead_code)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; @@ -43,3 +44,8 @@ fn main() { // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); } + +// #8527 +fn cloned_flatten(x: Option<&Option>) -> Option { + x.cloned().flatten() +} diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index dd04e33a4b3ae..98321d889b582 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![allow(dead_code)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; @@ -45,3 +46,8 @@ fn main() { // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); } + +// #8527 +fn cloned_flatten(x: Option<&Option>) -> Option { + x.cloned().flatten() +} diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index e36b0e36fbdf9..0582700fd16a8 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -1,5 +1,5 @@ error: called `cloned().last()` on an `Iterator`. It may be more efficient to call `last().cloned()` instead - --> $DIR/iter_overeager_cloned.rs:7:29 + --> $DIR/iter_overeager_cloned.rs:8:29 | LL | let _: Option = vec.iter().cloned().last(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().last().cloned()` @@ -7,13 +7,13 @@ LL | let _: Option = vec.iter().cloned().last(); = note: `-D clippy::iter-overeager-cloned` implied by `-D warnings` error: called `cloned().next()` on an `Iterator`. It may be more efficient to call `next().cloned()` instead - --> $DIR/iter_overeager_cloned.rs:9:29 + --> $DIR/iter_overeager_cloned.rs:10:29 | LL | let _: Option = vec.iter().chain(vec.iter()).cloned().next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().chain(vec.iter()).next().cloned()` error: called `cloned().count()` on an `Iterator`. It may be more efficient to call `count()` instead - --> $DIR/iter_overeager_cloned.rs:11:20 + --> $DIR/iter_overeager_cloned.rs:12:20 | LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").count()` @@ -21,25 +21,25 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); = note: `-D clippy::redundant-clone` implied by `-D warnings` error: called `cloned().take(...)` on an `Iterator`. It may be more efficient to call `take(...).cloned()` instead - --> $DIR/iter_overeager_cloned.rs:13:21 + --> $DIR/iter_overeager_cloned.rs:14:21 | LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().take(2).cloned()` error: called `cloned().skip(...)` on an `Iterator`. It may be more efficient to call `skip(...).cloned()` instead - --> $DIR/iter_overeager_cloned.rs:15:21 + --> $DIR/iter_overeager_cloned.rs:16:21 | LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().skip(2).cloned()` error: called `cloned().nth(...)` on an `Iterator`. It may be more efficient to call `nth(...).cloned()` instead - --> $DIR/iter_overeager_cloned.rs:17:13 + --> $DIR/iter_overeager_cloned.rs:18:13 | LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").nth(2).cloned()` error: called `cloned().flatten()` on an `Iterator`. It may be more efficient to call `flatten().cloned()` instead - --> $DIR/iter_overeager_cloned.rs:19:13 + --> $DIR/iter_overeager_cloned.rs:20:13 | LL | let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] | _____________^ diff --git a/tests/ui/large_types_passed_by_value.rs b/tests/ui/large_types_passed_by_value.rs index e4a2e9df4d7ba..7601b5c66fa35 100644 --- a/tests/ui/large_types_passed_by_value.rs +++ b/tests/ui/large_types_passed_by_value.rs @@ -37,7 +37,7 @@ pub trait PubLargeTypeDevourer { fn devoure_array_in_public(&self, array: [u8; 6666]); } -struct S {} +struct S; impl LargeTypeDevourer for S { fn devoure_array(&self, array: [u8; 6666]) { todo!(); diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index e3561863c1e1f..bb162adc9adb2 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -88,7 +88,7 @@ mod no_lint_if_stmt_borrows { ret } - struct Bar {} + struct Bar; impl Bar { fn new() -> Self { diff --git a/tests/ui/let_underscore_must_use.rs b/tests/ui/let_underscore_must_use.rs index a842e872a37b1..1edb77c748bfb 100644 --- a/tests/ui/let_underscore_must_use.rs +++ b/tests/ui/let_underscore_must_use.rs @@ -26,7 +26,7 @@ fn h() -> u32 { 0 } -struct S {} +struct S; impl S { #[must_use] diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 136cc96be70ca..b7e46a4a8ccc2 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -38,7 +38,7 @@ async fn already_async() -> impl Future { async { 42 } } -struct S {} +struct S; impl S { async fn inh_fut() -> i32 { // NOTE: this code is here just to check that the indentation is correct in the suggested fix diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index ddc453ffdb750..b05429da66225 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -52,7 +52,7 @@ async fn already_async() -> impl Future { async { 42 } } -struct S {} +struct S; impl S { fn inh_fut() -> impl Future { async { diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 05d6c56f2aca0..7d68978216c9c 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -78,7 +78,7 @@ fn result_unwrap_or() { (Ok(1) as Result).unwrap_or(42); // method call case, suggestion must not surround Result expr `s.method()` with parentheses - struct S {} + struct S; impl S { fn method(self) -> Option { Some(42) diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 09f62c69b71de..b937fe6f977e5 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -102,7 +102,7 @@ fn result_unwrap_or() { }; // method call case, suggestion must not surround Result expr `s.method()` with parentheses - struct S {} + struct S; impl S { fn method(self) -> Option { Some(42) diff --git a/tests/ui/map_identity.fixed b/tests/ui/map_identity.fixed index 4a1452b25f343..2256e51f2d09c 100644 --- a/tests/ui/map_identity.fixed +++ b/tests/ui/map_identity.fixed @@ -16,6 +16,8 @@ fn main() { let _: Result = Err(2.3).map(|x: i8| { return x + 3; }); + let _: Result = Ok(1); + let _: Result = Ok(1).map_err(|a: u32| a * 42); } fn not_identity(x: &u16) -> u16 { diff --git a/tests/ui/map_identity.rs b/tests/ui/map_identity.rs index 65c7e6e1ea554..ccfdc9ea76d52 100644 --- a/tests/ui/map_identity.rs +++ b/tests/ui/map_identity.rs @@ -18,6 +18,8 @@ fn main() { let _: Result = Err(2.3).map(|x: i8| { return x + 3; }); + let _: Result = Ok(1).map_err(|a| a); + let _: Result = Ok(1).map_err(|a: u32| a * 42); } fn not_identity(x: &u16) -> u16 { diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr index e4a0320cbda55..b6a77281f6de2 100644 --- a/tests/ui/map_identity.stderr +++ b/tests/ui/map_identity.stderr @@ -33,5 +33,11 @@ LL | | return x; LL | | }); | |______^ help: remove the call to `map` -error: aborting due to 5 previous errors +error: unnecessary map of the identity function + --> $DIR/map_identity.rs:21:36 + | +LL | let _: Result = Ok(1).map_err(|a| a); + | ^^^^^^^^^^^^^^^ help: remove the call to `map_err` + +error: aborting due to 6 previous errors diff --git a/tests/ui/map_unit_fn.rs b/tests/ui/map_unit_fn.rs index 9a74da4e3b8b6..e7f07b50f3ab1 100644 --- a/tests/ui/map_unit_fn.rs +++ b/tests/ui/map_unit_fn.rs @@ -1,5 +1,5 @@ #![allow(unused)] -struct Mappable {} +struct Mappable; impl Mappable { pub fn map(&self) {} diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index c5f221220ece7..f83c3e0e281ca 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -99,7 +99,7 @@ pub fn manual_range_contains() { } pub fn use_self() { - struct Foo {} + struct Foo; impl Foo { fn new() -> Foo { @@ -145,6 +145,16 @@ fn int_from_bool() -> u8 { true as u8 } +fn err_expect() { + let x: Result = Ok(10); + x.err().expect("Testing expect_err"); +} + +fn cast_abs_to_unsigned() { + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + fn main() { filter_map_next(); checked_conversion(); @@ -162,6 +172,8 @@ fn main() { missing_const_for_fn(); unnest_or_patterns(); int_from_bool(); + err_expect(); + cast_abs_to_unsigned(); } mod just_under_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 6b3fdb0844b49..de225eb740d03 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:186:24 + --> $DIR/min_rust_version_attr.rs:198:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:185:9 + --> $DIR/min_rust_version_attr.rs:197:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL ~ assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:198:24 + --> $DIR/min_rust_version_attr.rs:210:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:197:9 + --> $DIR/min_rust_version_attr.rs:209:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/missing_inline.rs b/tests/ui/missing_inline.rs index b73b24b8e0a3b..07f8e3888c998 100644 --- a/tests/ui/missing_inline.rs +++ b/tests/ui/missing_inline.rs @@ -7,8 +7,8 @@ type Typedef = String; pub type PubTypedef = String; -struct Foo {} // ok -pub struct PubFoo {} // ok +struct Foo; // ok +pub struct PubFoo; // ok enum FooE {} // ok pub enum PubFooE {} // ok @@ -63,4 +63,4 @@ impl PubFoo { // do not lint this since users cannot control the external code #[derive(Debug)] -pub struct S {} +pub struct S; diff --git a/tests/ui/module_name_repetitions.rs b/tests/ui/module_name_repetitions.rs index f5908cb5701fb..ebaa77cc283e0 100644 --- a/tests/ui/module_name_repetitions.rs +++ b/tests/ui/module_name_repetitions.rs @@ -7,7 +7,7 @@ mod foo { pub fn foo() {} pub fn foo_bar() {} pub fn bar_foo() {} - pub struct FooCake {} + pub struct FooCake; pub enum CakeFoo {} pub struct Foo7Bar; diff --git a/tests/ui/module_name_repetitions.stderr b/tests/ui/module_name_repetitions.stderr index bdd217a969c05..3f343a3e43018 100644 --- a/tests/ui/module_name_repetitions.stderr +++ b/tests/ui/module_name_repetitions.stderr @@ -15,8 +15,8 @@ LL | pub fn bar_foo() {} error: item name starts with its containing module's name --> $DIR/module_name_repetitions.rs:10:5 | -LL | pub struct FooCake {} - | ^^^^^^^^^^^^^^^^^^^^^ +LL | pub struct FooCake; + | ^^^^^^^^^^^^^^^^^^^ error: item name ends with its containing module's name --> $DIR/module_name_repetitions.rs:11:5 diff --git a/tests/ui/modulo_arithmetic_integral_const.rs b/tests/ui/modulo_arithmetic_integral_const.rs index 047a29fa1e327..3ebe46bc5be7c 100644 --- a/tests/ui/modulo_arithmetic_integral_const.rs +++ b/tests/ui/modulo_arithmetic_integral_const.rs @@ -1,5 +1,10 @@ #![warn(clippy::modulo_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::modulo_one, + clippy::identity_op +)] fn main() { // Lint when both sides are const and of the opposite sign diff --git a/tests/ui/modulo_arithmetic_integral_const.stderr b/tests/ui/modulo_arithmetic_integral_const.stderr index 64335f35f0f82..11b5f77461ba2 100644 --- a/tests/ui/modulo_arithmetic_integral_const.stderr +++ b/tests/ui/modulo_arithmetic_integral_const.stderr @@ -1,5 +1,5 @@ error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:6:5 + --> $DIR/modulo_arithmetic_integral_const.rs:11:5 | LL | -1 % 2; | ^^^^^^ @@ -9,7 +9,7 @@ LL | -1 % 2; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:7:5 + --> $DIR/modulo_arithmetic_integral_const.rs:12:5 | LL | 1 % -2; | ^^^^^^ @@ -18,7 +18,7 @@ LL | 1 % -2; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 3` - --> $DIR/modulo_arithmetic_integral_const.rs:8:5 + --> $DIR/modulo_arithmetic_integral_const.rs:13:5 | LL | (1 - 2) % (1 + 2); | ^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | (1 - 2) % (1 + 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `3 % -1` - --> $DIR/modulo_arithmetic_integral_const.rs:9:5 + --> $DIR/modulo_arithmetic_integral_const.rs:14:5 | LL | (1 + 2) % (1 - 2); | ^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | (1 + 2) % (1 - 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-35 % 300000` - --> $DIR/modulo_arithmetic_integral_const.rs:10:5 + --> $DIR/modulo_arithmetic_integral_const.rs:15:5 | LL | 35 * (7 - 4 * 2) % (-500 * -600); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | 35 * (7 - 4 * 2) % (-500 * -600); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:12:5 + --> $DIR/modulo_arithmetic_integral_const.rs:17:5 | LL | -1i8 % 2i8; | ^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | -1i8 % 2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:13:5 + --> $DIR/modulo_arithmetic_integral_const.rs:18:5 | LL | 1i8 % -2i8; | ^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | 1i8 % -2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:14:5 + --> $DIR/modulo_arithmetic_integral_const.rs:19:5 | LL | -1i16 % 2i16; | ^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | -1i16 % 2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:15:5 + --> $DIR/modulo_arithmetic_integral_const.rs:20:5 | LL | 1i16 % -2i16; | ^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL | 1i16 % -2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:16:5 + --> $DIR/modulo_arithmetic_integral_const.rs:21:5 | LL | -1i32 % 2i32; | ^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | -1i32 % 2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:17:5 + --> $DIR/modulo_arithmetic_integral_const.rs:22:5 | LL | 1i32 % -2i32; | ^^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL | 1i32 % -2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:18:5 + --> $DIR/modulo_arithmetic_integral_const.rs:23:5 | LL | -1i64 % 2i64; | ^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL | -1i64 % 2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:19:5 + --> $DIR/modulo_arithmetic_integral_const.rs:24:5 | LL | 1i64 % -2i64; | ^^^^^^^^^^^^ @@ -117,7 +117,7 @@ LL | 1i64 % -2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:20:5 + --> $DIR/modulo_arithmetic_integral_const.rs:25:5 | LL | -1i128 % 2i128; | ^^^^^^^^^^^^^^ @@ -126,7 +126,7 @@ LL | -1i128 % 2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:21:5 + --> $DIR/modulo_arithmetic_integral_const.rs:26:5 | LL | 1i128 % -2i128; | ^^^^^^^^^^^^^^ @@ -135,7 +135,7 @@ LL | 1i128 % -2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> $DIR/modulo_arithmetic_integral_const.rs:22:5 + --> $DIR/modulo_arithmetic_integral_const.rs:27:5 | LL | -1isize % 2isize; | ^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | -1isize % 2isize; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> $DIR/modulo_arithmetic_integral_const.rs:23:5 + --> $DIR/modulo_arithmetic_integral_const.rs:28:5 | LL | 1isize % -2isize; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.rs b/tests/ui/needless_arbitrary_self_type_unfixable.rs index ad0d694a2174a..02b43cce2bd4c 100644 --- a/tests/ui/needless_arbitrary_self_type_unfixable.rs +++ b/tests/ui/needless_arbitrary_self_type_unfixable.rs @@ -14,7 +14,7 @@ mod issue_6089 { fn test(self: &Self); } - struct S1 {} + struct S1; impl T1 for S1 { fn test(self: &Self) {} @@ -32,7 +32,7 @@ mod issue_6089 { fn call_with_mut_self(&mut self); } - struct S2 {} + struct S2; // The method's signature will be expanded to: // fn call_with_mut_self<'life0>(self: &'life0 mut Self) {} diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index f3eafe8e9279e..1456204ca8692 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -268,7 +268,7 @@ mod issue4291 { mod issue2944 { trait Foo {} - struct Bar {} + struct Bar; struct Baz<'a> { bar: &'a Bar, } diff --git a/tests/ui/needless_match.fixed b/tests/ui/needless_match.fixed index ece18ad737fda..9ccccaa1725a6 100644 --- a/tests/ui/needless_match.fixed +++ b/tests/ui/needless_match.fixed @@ -4,38 +4,35 @@ #![allow(dead_code)] #[derive(Clone, Copy)] -enum Choice { +enum Simple { A, B, C, D, } -#[allow(unused_mut)] fn useless_match() { - let mut i = 10; + let i = 10; let _: i32 = i; - let _: i32 = i; - let mut _i_mut = i; - let s = "test"; let _: &str = s; } -fn custom_type_match(se: Choice) { - let _: Choice = se; +fn custom_type_match() { + let se = Simple::A; + let _: Simple = se; // Don't trigger - let _: Choice = match se { - Choice::A => Choice::A, - Choice::B => Choice::B, - _ => Choice::C, + let _: Simple = match se { + Simple::A => Simple::A, + Simple::B => Simple::B, + _ => Simple::C, }; // Mingled, don't trigger - let _: Choice = match se { - Choice::A => Choice::B, - Choice::B => Choice::C, - Choice::C => Choice::D, - Choice::D => Choice::A, + let _: Simple = match se { + Simple::A => Simple::B, + Simple::B => Simple::C, + Simple::C => Simple::D, + Simple::D => Simple::A, }; } @@ -55,29 +52,146 @@ fn func_ret_err(err: T) -> Result { fn result_match() { let _: Result = Ok(1); let _: Result = func_ret_err(0_i32); + // as ref, don't trigger + let res = &func_ret_err(0_i32); + let _: Result<&i32, &i32> = match *res { + Ok(ref x) => Ok(x), + Err(ref x) => Err(x), + }; } -fn if_let_option() -> Option { - Some(1) +fn if_let_option() { + let _ = Some(1); + + fn do_something() {} + + // Don't trigger + let _ = if let Some(a) = Some(1) { + Some(a) + } else { + do_something(); + None + }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { + do_something(); + Some(a) + } else { + None + }; } -fn if_let_result(x: Result<(), i32>) { - let _: Result<(), i32> = x; - let _: Result<(), i32> = x; +fn if_let_result() { + let x: Result = Ok(1); + let _: Result = x; + let _: Result = x; // Input type mismatch, don't trigger - let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x }; + let _: Result = if let Err(e) = Ok(1) { Err(e) } else { x }; } -fn if_let_custom_enum(x: Choice) { - let _: Choice = x; +fn if_let_custom_enum(x: Simple) { + let _: Simple = x; + // Don't trigger - let _: Choice = if let Choice::A = x { - Choice::A + let _: Simple = if let Simple::A = x { + Simple::A } else if true { - Choice::B + Simple::B } else { x }; } +mod issue8542 { + #[derive(Clone, Copy)] + enum E { + VariantA(u8, u8), + VariantB(u8, bool), + } + + enum Complex { + A(u8), + B(u8, bool), + C(u8, i32, f64), + D(E, bool), + } + + fn match_test() { + let ce = Complex::B(8, false); + let aa = 0_u8; + let bb = false; + + let _: Complex = ce; + + // Don't trigger + let _: Complex = match ce { + Complex::A(_) => Complex::A(aa), + Complex::B(_, b) => Complex::B(aa, b), + Complex::C(_, b, _) => Complex::C(aa, b, 64_f64), + Complex::D(e, b) => Complex::D(e, b), + }; + + // Don't trigger + let _: Complex = match ce { + Complex::A(a) => Complex::A(a), + Complex::B(a, _) => Complex::B(a, bb), + Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64), + _ => ce, + }; + } +} + +/// Lint triggered when type coercions happen. +/// Do NOT trigger on any of these. +mod issue8551 { + trait Trait {} + struct Struct; + impl Trait for Struct {} + + fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> { + match s { + Some(s) => Some(s), + None => None, + } + } + + fn lint_tests() { + let option: Option<&Struct> = None; + let _: Option<&dyn Trait> = match option { + Some(s) => Some(s), + None => None, + }; + + let _: Option<&dyn Trait> = if true { + match option { + Some(s) => Some(s), + None => None, + } + } else { + None + }; + + let result: Result<&Struct, i32> = Err(0); + let _: Result<&dyn Trait, i32> = match result { + Ok(s) => Ok(s), + Err(e) => Err(e), + }; + + let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None }; + } +} + +trait Tr { + fn as_mut(&mut self) -> Result<&mut i32, &mut i32>; +} +impl Tr for Result { + fn as_mut(&mut self) -> Result<&mut i32, &mut i32> { + match self { + Ok(x) => Ok(x), + Err(e) => Err(e), + } + } +} + fn main() {} diff --git a/tests/ui/needless_match.rs b/tests/ui/needless_match.rs index 36649de35a603..d210ecff7f163 100644 --- a/tests/ui/needless_match.rs +++ b/tests/ui/needless_match.rs @@ -4,33 +4,21 @@ #![allow(dead_code)] #[derive(Clone, Copy)] -enum Choice { +enum Simple { A, B, C, D, } -#[allow(unused_mut)] fn useless_match() { - let mut i = 10; + let i = 10; let _: i32 = match i { 0 => 0, 1 => 1, 2 => 2, _ => i, }; - let _: i32 = match i { - 0 => 0, - 1 => 1, - ref i => *i, - }; - let mut _i_mut = match i { - 0 => 0, - 1 => 1, - ref mut i => *i, - }; - let s = "test"; let _: &str = match s { "a" => "a", @@ -39,25 +27,26 @@ fn useless_match() { }; } -fn custom_type_match(se: Choice) { - let _: Choice = match se { - Choice::A => Choice::A, - Choice::B => Choice::B, - Choice::C => Choice::C, - Choice::D => Choice::D, +fn custom_type_match() { + let se = Simple::A; + let _: Simple = match se { + Simple::A => Simple::A, + Simple::B => Simple::B, + Simple::C => Simple::C, + Simple::D => Simple::D, }; // Don't trigger - let _: Choice = match se { - Choice::A => Choice::A, - Choice::B => Choice::B, - _ => Choice::C, + let _: Simple = match se { + Simple::A => Simple::A, + Simple::B => Simple::B, + _ => Simple::C, }; // Mingled, don't trigger - let _: Choice = match se { - Choice::A => Choice::B, - Choice::B => Choice::C, - Choice::C => Choice::D, - Choice::D => Choice::A, + let _: Simple = match se { + Simple::A => Simple::B, + Simple::B => Simple::C, + Simple::C => Simple::D, + Simple::D => Simple::A, }; } @@ -86,37 +75,160 @@ fn result_match() { Err(err) => Err(err), Ok(a) => Ok(a), }; + // as ref, don't trigger + let res = &func_ret_err(0_i32); + let _: Result<&i32, &i32> = match *res { + Ok(ref x) => Ok(x), + Err(ref x) => Err(x), + }; } -fn if_let_option() -> Option { - if let Some(a) = Some(1) { Some(a) } else { None } +fn if_let_option() { + let _ = if let Some(a) = Some(1) { Some(a) } else { None }; + + fn do_something() {} + + // Don't trigger + let _ = if let Some(a) = Some(1) { + Some(a) + } else { + do_something(); + None + }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { + do_something(); + Some(a) + } else { + None + }; } -fn if_let_result(x: Result<(), i32>) { - let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x }; - let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x }; +fn if_let_result() { + let x: Result = Ok(1); + let _: Result = if let Err(e) = x { Err(e) } else { x }; + let _: Result = if let Ok(val) = x { Ok(val) } else { x }; // Input type mismatch, don't trigger - let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x }; + let _: Result = if let Err(e) = Ok(1) { Err(e) } else { x }; } -fn if_let_custom_enum(x: Choice) { - let _: Choice = if let Choice::A = x { - Choice::A - } else if let Choice::B = x { - Choice::B - } else if let Choice::C = x { - Choice::C +fn if_let_custom_enum(x: Simple) { + let _: Simple = if let Simple::A = x { + Simple::A + } else if let Simple::B = x { + Simple::B + } else if let Simple::C = x { + Simple::C } else { x }; + // Don't trigger - let _: Choice = if let Choice::A = x { - Choice::A + let _: Simple = if let Simple::A = x { + Simple::A } else if true { - Choice::B + Simple::B } else { x }; } +mod issue8542 { + #[derive(Clone, Copy)] + enum E { + VariantA(u8, u8), + VariantB(u8, bool), + } + + enum Complex { + A(u8), + B(u8, bool), + C(u8, i32, f64), + D(E, bool), + } + + fn match_test() { + let ce = Complex::B(8, false); + let aa = 0_u8; + let bb = false; + + let _: Complex = match ce { + Complex::A(a) => Complex::A(a), + Complex::B(a, b) => Complex::B(a, b), + Complex::C(a, b, c) => Complex::C(a, b, c), + Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b), + Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b), + }; + + // Don't trigger + let _: Complex = match ce { + Complex::A(_) => Complex::A(aa), + Complex::B(_, b) => Complex::B(aa, b), + Complex::C(_, b, _) => Complex::C(aa, b, 64_f64), + Complex::D(e, b) => Complex::D(e, b), + }; + + // Don't trigger + let _: Complex = match ce { + Complex::A(a) => Complex::A(a), + Complex::B(a, _) => Complex::B(a, bb), + Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64), + _ => ce, + }; + } +} + +/// Lint triggered when type coercions happen. +/// Do NOT trigger on any of these. +mod issue8551 { + trait Trait {} + struct Struct; + impl Trait for Struct {} + + fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> { + match s { + Some(s) => Some(s), + None => None, + } + } + + fn lint_tests() { + let option: Option<&Struct> = None; + let _: Option<&dyn Trait> = match option { + Some(s) => Some(s), + None => None, + }; + + let _: Option<&dyn Trait> = if true { + match option { + Some(s) => Some(s), + None => None, + } + } else { + None + }; + + let result: Result<&Struct, i32> = Err(0); + let _: Result<&dyn Trait, i32> = match result { + Ok(s) => Ok(s), + Err(e) => Err(e), + }; + + let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None }; + } +} + +trait Tr { + fn as_mut(&mut self) -> Result<&mut i32, &mut i32>; +} +impl Tr for Result { + fn as_mut(&mut self) -> Result<&mut i32, &mut i32> { + match self { + Ok(x) => Ok(x), + Err(e) => Err(e), + } + } +} + fn main() {} diff --git a/tests/ui/needless_match.stderr b/tests/ui/needless_match.stderr index ad1525406ade7..34c5226f06057 100644 --- a/tests/ui/needless_match.stderr +++ b/tests/ui/needless_match.stderr @@ -1,5 +1,5 @@ error: this match expression is unnecessary - --> $DIR/needless_match.rs:17:18 + --> $DIR/needless_match.rs:16:18 | LL | let _: i32 = match i { | __________________^ @@ -13,29 +13,7 @@ LL | | }; = note: `-D clippy::needless-match` implied by `-D warnings` error: this match expression is unnecessary - --> $DIR/needless_match.rs:23:18 - | -LL | let _: i32 = match i { - | __________________^ -LL | | 0 => 0, -LL | | 1 => 1, -LL | | ref i => *i, -LL | | }; - | |_____^ help: replace it with: `i` - -error: this match expression is unnecessary - --> $DIR/needless_match.rs:28:22 - | -LL | let mut _i_mut = match i { - | ______________________^ -LL | | 0 => 0, -LL | | 1 => 1, -LL | | ref mut i => *i, -LL | | }; - | |_____^ help: replace it with: `i` - -error: this match expression is unnecessary - --> $DIR/needless_match.rs:35:19 + --> $DIR/needless_match.rs:23:19 | LL | let _: &str = match s { | ___________________^ @@ -46,19 +24,19 @@ LL | | }; | |_____^ help: replace it with: `s` error: this match expression is unnecessary - --> $DIR/needless_match.rs:43:21 + --> $DIR/needless_match.rs:32:21 | -LL | let _: Choice = match se { +LL | let _: Simple = match se { | _____________________^ -LL | | Choice::A => Choice::A, -LL | | Choice::B => Choice::B, -LL | | Choice::C => Choice::C, -LL | | Choice::D => Choice::D, +LL | | Simple::A => Simple::A, +LL | | Simple::B => Simple::B, +LL | | Simple::C => Simple::C, +LL | | Simple::D => Simple::D, LL | | }; | |_____^ help: replace it with: `se` error: this match expression is unnecessary - --> $DIR/needless_match.rs:65:26 + --> $DIR/needless_match.rs:54:26 | LL | let _: Option = match x { | __________________________^ @@ -68,7 +46,7 @@ LL | | }; | |_____^ help: replace it with: `x` error: this match expression is unnecessary - --> $DIR/needless_match.rs:81:31 + --> $DIR/needless_match.rs:70:31 | LL | let _: Result = match Ok(1) { | _______________________________^ @@ -78,7 +56,7 @@ LL | | }; | |_____^ help: replace it with: `Ok(1)` error: this match expression is unnecessary - --> $DIR/needless_match.rs:85:31 + --> $DIR/needless_match.rs:74:31 | LL | let _: Result = match func_ret_err(0_i32) { | _______________________________^ @@ -88,35 +66,48 @@ LL | | }; | |_____^ help: replace it with: `func_ret_err(0_i32)` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:92:5 + --> $DIR/needless_match.rs:87:13 | -LL | if let Some(a) = Some(1) { Some(a) } else { None } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)` +LL | let _ = if let Some(a) = Some(1) { Some(a) } else { None }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:96:30 + --> $DIR/needless_match.rs:110:31 | -LL | let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` +LL | let _: Result = if let Err(e) = x { Err(e) } else { x }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:97:30 + --> $DIR/needless_match.rs:111:31 | -LL | let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` +LL | let _: Result = if let Ok(val) = x { Ok(val) } else { x }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:103:21 + --> $DIR/needless_match.rs:117:21 | -LL | let _: Choice = if let Choice::A = x { +LL | let _: Simple = if let Simple::A = x { | _____________________^ -LL | | Choice::A -LL | | } else if let Choice::B = x { -LL | | Choice::B +LL | | Simple::A +LL | | } else if let Simple::B = x { +LL | | Simple::B ... | LL | | x LL | | }; | |_____^ help: replace it with: `x` -error: aborting due to 12 previous errors +error: this match expression is unnecessary + --> $DIR/needless_match.rs:156:26 + | +LL | let _: Complex = match ce { + | __________________________^ +LL | | Complex::A(a) => Complex::A(a), +LL | | Complex::B(a, b) => Complex::B(a, b), +LL | | Complex::C(a, b, c) => Complex::C(a, b, c), +LL | | Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b), +LL | | Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b), +LL | | }; + | |_________^ help: replace it with: `ce` + +error: aborting due to 11 previous errors diff --git a/tests/ui/needless_option_as_deref.fixed b/tests/ui/needless_option_as_deref.fixed index d721452ae8880..c09b07db3dca9 100644 --- a/tests/ui/needless_option_as_deref.fixed +++ b/tests/ui/needless_option_as_deref.fixed @@ -1,13 +1,41 @@ // run-rustfix -#[warn(clippy::needless_option_as_deref)] +#![allow(unused)] +#![warn(clippy::needless_option_as_deref)] fn main() { // should lint let _: Option<&usize> = Some(&1); let _: Option<&mut usize> = Some(&mut 1); + let mut y = 0; + let mut x = Some(&mut y); + let _ = x; + // should not lint let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + + // #7846 + let mut i = 0; + let mut opt_vec = vec![Some(&mut i)]; + opt_vec[0].as_deref_mut().unwrap(); + + let mut i = 0; + let x = &mut Some(&mut i); + (*x).as_deref_mut(); + + // #8047 + let mut y = 0; + let mut x = Some(&mut y); + x.as_deref_mut(); + dbg!(x); +} + +struct S<'a> { + opt: Option<&'a mut usize>, +} + +fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> { + s.opt.as_deref_mut() } diff --git a/tests/ui/needless_option_as_deref.rs b/tests/ui/needless_option_as_deref.rs index bb15512adf6aa..c3ba27ecccf22 100644 --- a/tests/ui/needless_option_as_deref.rs +++ b/tests/ui/needless_option_as_deref.rs @@ -1,13 +1,41 @@ // run-rustfix -#[warn(clippy::needless_option_as_deref)] +#![allow(unused)] +#![warn(clippy::needless_option_as_deref)] fn main() { // should lint let _: Option<&usize> = Some(&1).as_deref(); let _: Option<&mut usize> = Some(&mut 1).as_deref_mut(); + let mut y = 0; + let mut x = Some(&mut y); + let _ = x.as_deref_mut(); + // should not lint let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + + // #7846 + let mut i = 0; + let mut opt_vec = vec![Some(&mut i)]; + opt_vec[0].as_deref_mut().unwrap(); + + let mut i = 0; + let x = &mut Some(&mut i); + (*x).as_deref_mut(); + + // #8047 + let mut y = 0; + let mut x = Some(&mut y); + x.as_deref_mut(); + dbg!(x); +} + +struct S<'a> { + opt: Option<&'a mut usize>, +} + +fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> { + s.opt.as_deref_mut() } diff --git a/tests/ui/needless_option_as_deref.stderr b/tests/ui/needless_option_as_deref.stderr index 5dd507b4a71e8..bc07db5b38ed3 100644 --- a/tests/ui/needless_option_as_deref.stderr +++ b/tests/ui/needless_option_as_deref.stderr @@ -1,5 +1,5 @@ error: derefed type is same as origin - --> $DIR/needless_option_as_deref.rs:7:29 + --> $DIR/needless_option_as_deref.rs:8:29 | LL | let _: Option<&usize> = Some(&1).as_deref(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)` @@ -7,10 +7,16 @@ LL | let _: Option<&usize> = Some(&1).as_deref(); = note: `-D clippy::needless-option-as-deref` implied by `-D warnings` error: derefed type is same as origin - --> $DIR/needless_option_as_deref.rs:8:33 + --> $DIR/needless_option_as_deref.rs:9:33 | LL | let _: Option<&mut usize> = Some(&mut 1).as_deref_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)` -error: aborting due to 2 previous errors +error: derefed type is same as origin + --> $DIR/needless_option_as_deref.rs:13:13 + | +LL | let _ = x.as_deref_mut(); + | ^^^^^^^^^^^^^^^^ help: try this: `x` + +error: aborting due to 3 previous errors diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index 5427c88faf348..7ece66a1ccb6f 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -78,7 +78,7 @@ impl FnOnce<(&str,)> for GreetStruct2 { } } -struct GreetStruct3 {} +struct GreetStruct3; impl FnOnce<(&str,)> for GreetStruct3 { type Output = (); diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index 7d29445e66c8b..1290bd8efebd2 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -80,6 +80,9 @@ fn option_map_unit_fn() { if let Some(ref value) = x.field { do_nothing(value + captured) } - if let Some(a) = option() { do_nothing(a) }} + if let Some(a) = option() { do_nothing(a) } + + if let Some(value) = option() { println!("{:?}", value) } +} fn main() {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index b6f834f686f9f..f3e5b62c65b7f 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -80,6 +80,9 @@ fn option_map_unit_fn() { x.field.map(|ref value| { do_nothing(value + captured) }); - option().map(do_nothing);} + option().map(do_nothing); + + option().map(|value| println!("{:?}", value)); +} fn main() {} diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index 8abdbcafb6e93..ab2a294a060f0 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -139,10 +139,18 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:83:5 | -LL | option().map(do_nothing);} +LL | option().map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^^- | | | help: try this: `if let Some(a) = option() { do_nothing(a) }` -error: aborting due to 18 previous errors +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> $DIR/option_map_unit_fn_fixable.rs:85:5 + | +LL | option().map(|value| println!("{:?}", value)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = option() { println!("{:?}", value) }` + +error: aborting due to 19 previous errors diff --git a/tests/ui/or_then_unwrap.fixed b/tests/ui/or_then_unwrap.fixed index 27d4b795a5eeb..6e0d5a87f6807 100644 --- a/tests/ui/or_then_unwrap.fixed +++ b/tests/ui/or_then_unwrap.fixed @@ -3,7 +3,7 @@ #![warn(clippy::or_then_unwrap)] #![allow(clippy::map_identity)] -struct SomeStruct {} +struct SomeStruct; impl SomeStruct { fn or(self, _: Option) -> Self { self @@ -11,7 +11,7 @@ impl SomeStruct { fn unwrap(&self) {} } -struct SomeOtherStruct {} +struct SomeOtherStruct; impl SomeOtherStruct { fn or(self) -> Self { self diff --git a/tests/ui/or_then_unwrap.rs b/tests/ui/or_then_unwrap.rs index 0dab5ae2f1c04..e406a71d46d00 100644 --- a/tests/ui/or_then_unwrap.rs +++ b/tests/ui/or_then_unwrap.rs @@ -3,7 +3,7 @@ #![warn(clippy::or_then_unwrap)] #![allow(clippy::map_identity)] -struct SomeStruct {} +struct SomeStruct; impl SomeStruct { fn or(self, _: Option) -> Self { self @@ -11,7 +11,7 @@ impl SomeStruct { fn unwrap(&self) {} } -struct SomeOtherStruct {} +struct SomeOtherStruct; impl SomeOtherStruct { fn or(self) -> Self { self diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index 7338064646244..12a0c776ae2fd 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -1,8 +1,23 @@ -#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] #![allow(clippy::assertions_on_constants, clippy::eq_op)] +#![feature(inline_const)] +#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] extern crate core; +const _: () = { + if 1 == 0 { + panic!("A balanced diet means a cupcake in each hand"); + } +}; + +fn inline_const() { + let _ = const { + if 1 == 0 { + panic!("When nothing goes right, go left") + } + }; +} + fn panic() { let a = 2; panic!(); diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr index bfd1c7a380149..4ceb6d1440f61 100644 --- a/tests/ui/panicking_macros.stderr +++ b/tests/ui/panicking_macros.stderr @@ -1,5 +1,5 @@ error: `panic` should not be present in production code - --> $DIR/panicking_macros.rs:8:5 + --> $DIR/panicking_macros.rs:23:5 | LL | panic!(); | ^^^^^^^^ @@ -7,19 +7,19 @@ LL | panic!(); = note: `-D clippy::panic` implied by `-D warnings` error: `panic` should not be present in production code - --> $DIR/panicking_macros.rs:9:5 + --> $DIR/panicking_macros.rs:24:5 | LL | panic!("message"); | ^^^^^^^^^^^^^^^^^ error: `panic` should not be present in production code - --> $DIR/panicking_macros.rs:10:5 + --> $DIR/panicking_macros.rs:25:5 | LL | panic!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:16:5 + --> $DIR/panicking_macros.rs:31:5 | LL | todo!(); | ^^^^^^^ @@ -27,19 +27,19 @@ LL | todo!(); = note: `-D clippy::todo` implied by `-D warnings` error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:17:5 + --> $DIR/panicking_macros.rs:32:5 | LL | todo!("message"); | ^^^^^^^^^^^^^^^^ error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:18:5 + --> $DIR/panicking_macros.rs:33:5 | LL | todo!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:24:5 + --> $DIR/panicking_macros.rs:39:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ @@ -47,19 +47,19 @@ LL | unimplemented!(); = note: `-D clippy::unimplemented` implied by `-D warnings` error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:25:5 + --> $DIR/panicking_macros.rs:40:5 | LL | unimplemented!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:26:5 + --> $DIR/panicking_macros.rs:41:5 | LL | unimplemented!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> $DIR/panicking_macros.rs:32:5 + --> $DIR/panicking_macros.rs:47:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ @@ -67,37 +67,37 @@ LL | unreachable!(); = note: `-D clippy::unreachable` implied by `-D warnings` error: usage of the `unreachable!` macro - --> $DIR/panicking_macros.rs:33:5 + --> $DIR/panicking_macros.rs:48:5 | LL | unreachable!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> $DIR/panicking_macros.rs:34:5 + --> $DIR/panicking_macros.rs:49:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `panic` should not be present in production code - --> $DIR/panicking_macros.rs:40:5 + --> $DIR/panicking_macros.rs:55:5 | LL | panic!(); | ^^^^^^^^ error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:41:5 + --> $DIR/panicking_macros.rs:56:5 | LL | todo!(); | ^^^^^^^ error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:42:5 + --> $DIR/panicking_macros.rs:57:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> $DIR/panicking_macros.rs:43:5 + --> $DIR/panicking_macros.rs:58:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index 03dd938a2339e..814bbc7af713b 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -112,7 +112,7 @@ mod issue_5644 { ) { } - struct S {} + struct S; impl S { fn allowed( #[allow(clippy::ptr_arg)] _v: &Vec, diff --git a/tests/ui/recursive_format_impl.rs b/tests/ui/recursive_format_impl.rs index 9241bf7ed7402..f72fc77ab9977 100644 --- a/tests/ui/recursive_format_impl.rs +++ b/tests/ui/recursive_format_impl.rs @@ -66,7 +66,7 @@ impl std::fmt::Display for D { // Check for use of self as Display, in Display impl // Triggers on direct use of self -struct G {} +struct G; impl std::fmt::Display for G { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -75,7 +75,7 @@ impl std::fmt::Display for G { } // Triggers on reference to self -struct H {} +struct H; impl std::fmt::Display for H { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -90,7 +90,7 @@ impl std::fmt::Debug for H { } // Triggers on multiple reference to self -struct H2 {} +struct H2; impl std::fmt::Display for H2 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -99,7 +99,7 @@ impl std::fmt::Display for H2 { } // Doesn't trigger on correct deref -struct I {} +struct I; impl std::ops::Deref for I { type Target = str; @@ -122,7 +122,7 @@ impl std::fmt::Debug for I { } // Doesn't trigger on multiple correct deref -struct I2 {} +struct I2; impl std::ops::Deref for I2 { type Target = str; @@ -139,7 +139,7 @@ impl std::fmt::Display for I2 { } // Doesn't trigger on multiple correct deref -struct I3 {} +struct I3; impl std::ops::Deref for I3 { type Target = str; @@ -156,7 +156,7 @@ impl std::fmt::Display for I3 { } // Does trigger when deref resolves to self -struct J {} +struct J; impl std::ops::Deref for J { type Target = str; @@ -178,7 +178,7 @@ impl std::fmt::Debug for J { } } -struct J2 {} +struct J2; impl std::ops::Deref for J2 { type Target = str; @@ -194,7 +194,7 @@ impl std::fmt::Display for J2 { } } -struct J3 {} +struct J3; impl std::ops::Deref for J3 { type Target = str; @@ -210,7 +210,7 @@ impl std::fmt::Display for J3 { } } -struct J4 {} +struct J4; impl std::ops::Deref for J4 { type Target = str; @@ -227,7 +227,7 @@ impl std::fmt::Display for J4 { } // Doesn't trigger on Debug from Display -struct K {} +struct K; impl std::fmt::Debug for K { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -242,7 +242,7 @@ impl std::fmt::Display for K { } // Doesn't trigger on Display from Debug -struct K2 {} +struct K2; impl std::fmt::Debug for K2 { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs index 52fbc91e32555..80f94e5f3cbbb 100644 --- a/tests/ui/redundant_allocation.rs +++ b/tests/ui/redundant_allocation.rs @@ -3,7 +3,7 @@ #![allow(clippy::blacklisted_name, unused_variables, dead_code)] #![allow(unused_imports)] -pub struct MyStruct {} +pub struct MyStruct; pub struct SubT { foo: T, diff --git a/tests/ui/redundant_allocation_fixable.fixed b/tests/ui/redundant_allocation_fixable.fixed index ef089b25f47fd..e7ed84731c02e 100644 --- a/tests/ui/redundant_allocation_fixable.fixed +++ b/tests/ui/redundant_allocation_fixable.fixed @@ -4,7 +4,7 @@ #![allow(clippy::blacklisted_name, unused_variables, dead_code)] #![allow(unused_imports)] -pub struct MyStruct {} +pub struct MyStruct; pub struct SubT { foo: T, diff --git a/tests/ui/redundant_allocation_fixable.rs b/tests/ui/redundant_allocation_fixable.rs index fefa87721d720..de763f98b5c89 100644 --- a/tests/ui/redundant_allocation_fixable.rs +++ b/tests/ui/redundant_allocation_fixable.rs @@ -4,7 +4,7 @@ #![allow(clippy::blacklisted_name, unused_variables, dead_code)] #![allow(unused_imports)] -pub struct MyStruct {} +pub struct MyStruct; pub struct SubT { foo: T, diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index 16b40dcd90286..1525f6a93dfdd 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -1,7 +1,7 @@ // run-rustfix // rustfix-only-machine-applicable -#![allow(clippy::implicit_clone)] +#![allow(clippy::implicit_clone, clippy::drop_non_drop)] use std::ffi::OsString; use std::path::Path; diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index bd3d7365229fb..2f82aefd92830 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -1,7 +1,7 @@ // run-rustfix // rustfix-only-machine-applicable -#![allow(clippy::implicit_clone)] +#![allow(clippy::implicit_clone, clippy::drop_non_drop)] use std::ffi::OsString; use std::path::Path; diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed index 921249606ad27..acc8f1e25b6ed 100644 --- a/tests/ui/redundant_static_lifetimes.fixed +++ b/tests/ui/redundant_static_lifetimes.fixed @@ -3,7 +3,7 @@ #![allow(unused)] #[derive(Debug)] -struct Foo {} +struct Foo; const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static. diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs index 4d4b249d076ff..f2f0f78659c93 100644 --- a/tests/ui/redundant_static_lifetimes.rs +++ b/tests/ui/redundant_static_lifetimes.rs @@ -3,7 +3,7 @@ #![allow(unused)] #[derive(Debug)] -struct Foo {} +struct Foo; const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. diff --git a/tests/ui/result_map_unit_fn_fixable.fixed b/tests/ui/result_map_unit_fn_fixable.fixed index 631042c616bc0..14c331f67e739 100644 --- a/tests/ui/result_map_unit_fn_fixable.fixed +++ b/tests/ui/result_map_unit_fn_fixable.fixed @@ -75,6 +75,8 @@ fn result_map_unit_fn() { if let Ok(ref value) = x.field { do_nothing(value + captured) } + + if let Ok(value) = x.field { println!("{:?}", value) } } fn main() {} diff --git a/tests/ui/result_map_unit_fn_fixable.rs b/tests/ui/result_map_unit_fn_fixable.rs index 679eb2326268c..8b0fca9ece1a3 100644 --- a/tests/ui/result_map_unit_fn_fixable.rs +++ b/tests/ui/result_map_unit_fn_fixable.rs @@ -75,6 +75,8 @@ fn result_map_unit_fn() { x.field.map(|ref value| { do_nothing(value + captured) }); + + x.field.map(|value| println!("{:?}", value)); } fn main() {} diff --git a/tests/ui/result_map_unit_fn_fixable.stderr b/tests/ui/result_map_unit_fn_fixable.stderr index 4f3a8c6b79239..782febd526441 100644 --- a/tests/ui/result_map_unit_fn_fixable.stderr +++ b/tests/ui/result_map_unit_fn_fixable.stderr @@ -136,5 +136,13 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | | | help: try this: `if let Ok(ref value) = x.field { do_nothing(value + captured) }` -error: aborting due to 17 previous errors +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` + --> $DIR/result_map_unit_fn_fixable.rs:79:5 + | +LL | x.field.map(|value| println!("{:?}", value)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { println!("{:?}", value) }` + +error: aborting due to 18 previous errors diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 9d420ec672a26..99964f0de075c 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -120,7 +120,7 @@ fn main() { } // Fix #5979 #[derive(Clone)] - struct S {} + struct S; trait T {} impl T for S {} diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed index c307afffcb863..63d31ff83f9b5 100644 --- a/tests/ui/single_element_loop.fixed +++ b/tests/ui/single_element_loop.fixed @@ -6,11 +6,31 @@ fn main() { let item1 = 2; { let item = &item1; - println!("{}", item); + dbg!(item); } { let item = &item1; - println!("{:?}", item); + dbg!(item); + } + + { + let item = &(0..5); + dbg!(item); + } + + { + let item = &mut (0..5); + dbg!(item); + } + + { + let item = 0..5; + dbg!(item); + } + + { + let item = 0..5; + dbg!(item); } } diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs index 2c0c03b721199..2cda5a329d254 100644 --- a/tests/ui/single_element_loop.rs +++ b/tests/ui/single_element_loop.rs @@ -5,10 +5,26 @@ fn main() { let item1 = 2; for item in &[item1] { - println!("{}", item); + dbg!(item); } for item in [item1].iter() { - println!("{:?}", item); + dbg!(item); + } + + for item in &[0..5] { + dbg!(item); + } + + for item in [0..5].iter_mut() { + dbg!(item); + } + + for item in [0..5] { + dbg!(item); + } + + for item in [0..5].into_iter() { + dbg!(item); } } diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr index f52ca8c5a9b0c..0aeb8da1a2e23 100644 --- a/tests/ui/single_element_loop.stderr +++ b/tests/ui/single_element_loop.stderr @@ -2,7 +2,7 @@ error: for loop over a single element --> $DIR/single_element_loop.rs:7:5 | LL | / for item in &[item1] { -LL | | println!("{}", item); +LL | | dbg!(item); LL | | } | |_____^ | @@ -11,7 +11,7 @@ help: try | LL ~ { LL + let item = &item1; -LL + println!("{}", item); +LL + dbg!(item); LL + } | @@ -19,7 +19,7 @@ error: for loop over a single element --> $DIR/single_element_loop.rs:11:5 | LL | / for item in [item1].iter() { -LL | | println!("{:?}", item); +LL | | dbg!(item); LL | | } | |_____^ | @@ -27,9 +27,73 @@ help: try | LL ~ { LL + let item = &item1; -LL + println!("{:?}", item); +LL + dbg!(item); LL + } | -error: aborting due to 2 previous errors +error: for loop over a single element + --> $DIR/single_element_loop.rs:15:5 + | +LL | / for item in &[0..5] { +LL | | dbg!(item); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let item = &(0..5); +LL + dbg!(item); +LL + } + | + +error: for loop over a single element + --> $DIR/single_element_loop.rs:19:5 + | +LL | / for item in [0..5].iter_mut() { +LL | | dbg!(item); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let item = &mut (0..5); +LL + dbg!(item); +LL + } + | + +error: for loop over a single element + --> $DIR/single_element_loop.rs:23:5 + | +LL | / for item in [0..5] { +LL | | dbg!(item); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let item = 0..5; +LL + dbg!(item); +LL + } + | + +error: for loop over a single element + --> $DIR/single_element_loop.rs:27:5 + | +LL | / for item in [0..5].into_iter() { +LL | | dbg!(item); +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let item = 0..5; +LL + dbg!(item); +LL + } + | + +error: aborting due to 6 previous errors diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index 21de19a260147..f5ca91143af25 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -62,7 +62,7 @@ trait BadTrait: Default + Clone { } #[derive(Default, Clone)] -struct Life {} +struct Life; impl T for Life { // this should not warn @@ -85,7 +85,7 @@ trait Iter: Iterator { } } -struct Foo {} +struct Foo; trait FooIter: Iterator { fn bar() diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 9b681a79aae7a..5b688ce4d644f 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -73,6 +73,10 @@ fn crosspointer() { fn int_to_char() { let _: char = unsafe { std::mem::transmute(0_u32) }; let _: char = unsafe { std::mem::transmute(0_i32) }; + + // These shouldn't warn + const _: char = unsafe { std::mem::transmute(0_u32) }; + const _: char = unsafe { std::mem::transmute(0_i32) }; } #[warn(clippy::transmute_int_to_bool)] @@ -130,9 +134,12 @@ mod num_to_bytes { } } -fn bytes_to_str(b: &[u8], mb: &mut [u8]) { - let _: &str = unsafe { std::mem::transmute(b) }; +fn bytes_to_str(mb: &mut [u8]) { + const B: &[u8] = b""; + + let _: &str = unsafe { std::mem::transmute(B) }; let _: &mut str = unsafe { std::mem::transmute(mb) }; + const _: &str = unsafe { std::mem::transmute(B) }; } fn main() {} diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 86537153e3228..72a386e8fa618 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -107,7 +107,7 @@ LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:80:28 + --> $DIR/transmute.rs:84:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -115,7 +115,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:86:31 + --> $DIR/transmute.rs:90:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` @@ -123,25 +123,25 @@ LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:87:31 + --> $DIR/transmute.rs:91:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `u64` to a `f64` - --> $DIR/transmute.rs:88:31 + --> $DIR/transmute.rs:92:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` error: transmute from a `i64` to a `f64` - --> $DIR/transmute.rs:89:31 + --> $DIR/transmute.rs:93:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:109:30 + --> $DIR/transmute.rs:113:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -149,96 +149,102 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:110:30 + --> $DIR/transmute.rs:114:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:111:31 + --> $DIR/transmute.rs:115:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:112:30 + --> $DIR/transmute.rs:116:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:113:30 + --> $DIR/transmute.rs:117:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:114:31 + --> $DIR/transmute.rs:118:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> $DIR/transmute.rs:115:30 + --> $DIR/transmute.rs:119:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> $DIR/transmute.rs:116:30 + --> $DIR/transmute.rs:120:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:121:30 + --> $DIR/transmute.rs:125:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:122:30 + --> $DIR/transmute.rs:126:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:123:31 + --> $DIR/transmute.rs:127:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:124:30 + --> $DIR/transmute.rs:128:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:125:30 + --> $DIR/transmute.rs:129:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:126:31 + --> $DIR/transmute.rs:130:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:134:28 + --> $DIR/transmute.rs:140:28 | -LL | let _: &str = unsafe { std::mem::transmute(b) }; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` +LL | let _: &str = unsafe { std::mem::transmute(B) }; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` | = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:135:32 + --> $DIR/transmute.rs:141:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 38 previous errors +error: transmute from a `&[u8]` to a `&str` + --> $DIR/transmute.rs:142:30 + | +LL | const _: &str = unsafe { std::mem::transmute(B) }; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` + +error: aborting due to 39 previous errors diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index 380303d8152aa..afa337c45f417 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -1,5 +1,9 @@ +// aux-build:proc_macro_unsafe.rs + #![warn(clippy::undocumented_unsafe_blocks)] +extern crate proc_macro_unsafe; + // Valid comments fn nested_local() { @@ -89,11 +93,6 @@ fn block_comment_newlines() { unsafe {} } -#[rustfmt::skip] -fn inline_block_comment() { - /* Safety: */unsafe {} -} - fn block_comment_with_extras() { /* This is a description * SAFETY: @@ -209,8 +208,54 @@ fn local_nest() { let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})]; } +fn in_fn_call(x: *const u32) { + fn f(x: u32) {} + + // Safety: reason + f(unsafe { *x }); +} + +fn multi_in_fn_call(x: *const u32) { + fn f(x: u32, y: u32) {} + + // Safety: reason + f(unsafe { *x }, unsafe { *x }); +} + +fn in_multiline_fn_call(x: *const u32) { + fn f(x: u32, y: u32) {} + + f( + // Safety: reason + unsafe { *x }, + 0, + ); +} + +fn in_macro_call(x: *const u32) { + // Safety: reason + println!("{}", unsafe { *x }); +} + +fn in_multiline_macro_call(x: *const u32) { + println!( + "{}", + // Safety: reason + unsafe { *x }, + ); +} + +fn from_proc_macro() { + proc_macro_unsafe::unsafe_block!(token); +} + // Invalid comments +#[rustfmt::skip] +fn inline_block_comment() { + /* Safety: */ unsafe {} +} + fn no_comment() { unsafe {} } diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index f69d0da54e0d6..856a07fd31685 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -1,114 +1,110 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:215:5 + --> $DIR/undocumented_unsafe_blocks.rs:256:19 + | +LL | /* Safety: */ unsafe {} + | ^^^^^^^^^ + | + = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:260:5 | LL | unsafe {} | ^^^^^^^^^ | - = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` -help: consider adding a safety comment + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:264:14 | -LL ~ // SAFETY: ... -LL + unsafe {} +LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; + | ^^^^^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:219:5 + --> $DIR/undocumented_unsafe_blocks.rs:264:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ | -help: consider adding a safety comment + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:264:48 | -LL ~ // SAFETY: ... -LL + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; +LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; + | ^^^^^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:223:5 + --> $DIR/undocumented_unsafe_blocks.rs:268:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ | -help: consider adding a safety comment + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:268:37 | -LL ~ // SAFETY: ... -LL + let _ = (42, unsafe {}, "test", unsafe {}); +LL | let _ = (42, unsafe {}, "test", unsafe {}); + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:227:5 + --> $DIR/undocumented_unsafe_blocks.rs:272:14 | LL | let _ = *unsafe { &42 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = *unsafe { &42 }; + | ^^^^^^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:232:5 + --> $DIR/undocumented_unsafe_blocks.rs:277:19 | LL | let _ = match unsafe {} { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = match unsafe {} { + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:238:5 + --> $DIR/undocumented_unsafe_blocks.rs:283:14 | LL | let _ = &unsafe {}; - | ^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = &unsafe {}; + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:242:5 + --> $DIR/undocumented_unsafe_blocks.rs:287:14 | LL | let _ = [unsafe {}; 5]; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = [unsafe {}; 5]; + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:246:5 + --> $DIR/undocumented_unsafe_blocks.rs:291:13 | LL | let _ = unsafe {}; - | ^^^^^^^^^^^^^^^^^^ - | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + let _ = unsafe {}; + | ^^^^^^^^^ | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:256:8 + --> $DIR/undocumented_unsafe_blocks.rs:301:8 | LL | t!(unsafe {}); | ^^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ t!(// SAFETY: ... -LL ~ unsafe {}); - | + = help: consider adding a safety comment on the preceding line -error: unsafe block in macro expansion missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:262:13 +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:307:13 | LL | unsafe {} | ^^^^^^^^^ @@ -116,56 +112,40 @@ LL | unsafe {} LL | t!(); | ---- in this macro invocation | - = help: consider adding a safety comment in the macro definition + = help: consider adding a safety comment on the preceding line = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:5 + --> $DIR/undocumented_unsafe_blocks.rs:315:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL ~ unsafe {} // SAFETY: - | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:274:5 + --> $DIR/undocumented_unsafe_blocks.rs:319:5 | LL | unsafe { | ^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL + unsafe { - | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:284:5 + --> $DIR/undocumented_unsafe_blocks.rs:329:5 | LL | unsafe {}; | ^^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ // SAFETY: ... -LL ~ unsafe {}; - | + = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:288:20 + --> $DIR/undocumented_unsafe_blocks.rs:333:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: consider adding a safety comment - | -LL ~ println!("{}", // SAFETY: ... -LL ~ unsafe { String::from_utf8_unchecked(vec![]) }); - | + = help: consider adding a safety comment on the preceding line -error: aborting due to 14 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index b77c19f2ba596..62c3e9636866a 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -30,4 +30,10 @@ fn main() { // do not lint cast to cfg-dependant type 1 as std::os::raw::c_char; + + // do not lint cast to alias type + 1 as I32Alias; + &1 as &I32Alias; } + +type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 3332f49c80c97..36800c5340db2 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -42,4 +42,9 @@ fn main() { let _ = -1_i32; let _ = -1.0_f32; + + let _ = 1 as I32Alias; + let _ = &1 as &I32Alias; } + +type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index ec01e93877927..d4b6bb952ab35 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -42,4 +42,9 @@ fn main() { let _ = -1 as i32; let _ = -1.0 as f32; + + let _ = 1 as I32Alias; + let _ = &1 as &I32Alias; } + +type I32Alias = i32; diff --git a/tests/ui/unsafe_derive_deserialize.rs b/tests/ui/unsafe_derive_deserialize.rs index 690d705573d3f..bafca91917aa4 100644 --- a/tests/ui/unsafe_derive_deserialize.rs +++ b/tests/ui/unsafe_derive_deserialize.rs @@ -6,7 +6,7 @@ extern crate serde; use serde::Deserialize; #[derive(Deserialize)] -pub struct A {} +pub struct A; impl A { pub unsafe fn new(_a: i32, _b: i32) -> Self { Self {} @@ -14,13 +14,13 @@ impl A { } #[derive(Deserialize)] -pub struct B {} +pub struct B; impl B { pub unsafe fn unsafe_method(&self) {} } #[derive(Deserialize)] -pub struct C {} +pub struct C; impl C { pub fn unsafe_block(&self) { unsafe {} @@ -28,7 +28,7 @@ impl C { } #[derive(Deserialize)] -pub struct D {} +pub struct D; impl D { pub fn inner_unsafe_fn(&self) { unsafe fn inner() {} @@ -36,7 +36,7 @@ impl D { } // Does not derive `Deserialize`, should be ignored -pub struct E {} +pub struct E; impl E { pub unsafe fn new(_a: i32, _b: i32) -> Self { Self {} @@ -55,12 +55,12 @@ impl E { // Does not have methods using `unsafe`, should be ignored #[derive(Deserialize)] -pub struct F {} +pub struct F; // Check that we honor the `allow` attribute on the ADT #[allow(clippy::unsafe_derive_deserialize)] #[derive(Deserialize)] -pub struct G {} +pub struct G; impl G { pub fn unsafe_block(&self) { unsafe {} diff --git a/tests/ui/unsafe_removed_from_name.rs b/tests/ui/unsafe_removed_from_name.rs index a1f616733bd92..cde4e96d668c2 100644 --- a/tests/ui/unsafe_removed_from_name.rs +++ b/tests/ui/unsafe_removed_from_name.rs @@ -14,8 +14,8 @@ use std::cell::UnsafeCell as Dangerunsafe; use std::cell::UnsafeCell as Bombsawayunsafe; mod mod_with_some_unsafe_things { - pub struct Safe {} - pub struct Unsafe {} + pub struct Safe; + pub struct Unsafe; } use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety; diff --git a/tests/ui/unused_self.rs b/tests/ui/unused_self.rs index 7a4bbdda1ab27..08bf58fec7c3e 100644 --- a/tests/ui/unused_self.rs +++ b/tests/ui/unused_self.rs @@ -5,7 +5,7 @@ mod unused_self { use std::pin::Pin; use std::sync::{Arc, Mutex}; - struct A {} + struct A; impl A { fn unused_self_move(self) {} @@ -27,7 +27,7 @@ mod unused_self { } mod unused_self_allow { - struct A {} + struct A; impl A { // shouldn't trigger @@ -35,7 +35,7 @@ mod unused_self_allow { fn unused_self_move(self) {} } - struct B {} + struct B; // shouldn't trigger #[allow(clippy::unused_self)] @@ -43,7 +43,7 @@ mod unused_self_allow { fn unused_self_move(self) {} } - struct C {} + struct C; #[allow(clippy::unused_self)] impl C { @@ -120,7 +120,7 @@ mod used_self { mod not_applicable { use std::fmt; - struct A {} + struct A; impl fmt::Debug for A { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 9d216f56ae60c..3e62ffe74fedd 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -16,7 +16,7 @@ extern crate proc_macro_derive; fn main() {} mod use_self { - struct Foo {} + struct Foo; impl Foo { fn new() -> Self { @@ -35,7 +35,7 @@ mod use_self { } mod better { - struct Foo {} + struct Foo; impl Foo { fn new() -> Self { @@ -123,7 +123,7 @@ mod macros { }; } - struct Foo {} + struct Foo; impl Foo { use_self_expand!(); // Should not lint in local macros @@ -134,7 +134,7 @@ mod macros { } mod nesting { - struct Foo {} + struct Foo; impl Foo { fn foo() { #[allow(unused_imports)] @@ -209,7 +209,7 @@ mod issue3410 { #[allow(clippy::no_effect, path_statements)] mod rustfix { mod nested { - pub struct A {} + pub struct A; } impl nested::A { @@ -227,7 +227,7 @@ mod rustfix { } mod issue3567 { - struct TestStruct {} + struct TestStruct; impl TestStruct { fn from_something() -> Self { Self {} @@ -248,7 +248,7 @@ mod issue3567 { mod paths_created_by_lowering { use std::ops::Range; - struct S {} + struct S; impl S { const A: usize = 0; @@ -382,7 +382,7 @@ mod issue4305 { } mod lint_at_item_level { - struct Foo {} + struct Foo; #[allow(clippy::use_self)] impl Foo { @@ -400,7 +400,7 @@ mod lint_at_item_level { } mod lint_at_impl_item_level { - struct Foo {} + struct Foo; impl Foo { #[allow(clippy::use_self)] @@ -433,8 +433,8 @@ mod issue4734 { mod nested_paths { use std::convert::Into; mod submod { - pub struct B {} - pub struct C {} + pub struct B; + pub struct C; impl Into for B { fn into(self) -> C { diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 5f604fe25e416..da2faddee12a7 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -16,7 +16,7 @@ extern crate proc_macro_derive; fn main() {} mod use_self { - struct Foo {} + struct Foo; impl Foo { fn new() -> Foo { @@ -35,7 +35,7 @@ mod use_self { } mod better { - struct Foo {} + struct Foo; impl Foo { fn new() -> Self { @@ -123,7 +123,7 @@ mod macros { }; } - struct Foo {} + struct Foo; impl Foo { use_self_expand!(); // Should not lint in local macros @@ -134,7 +134,7 @@ mod macros { } mod nesting { - struct Foo {} + struct Foo; impl Foo { fn foo() { #[allow(unused_imports)] @@ -209,7 +209,7 @@ mod issue3410 { #[allow(clippy::no_effect, path_statements)] mod rustfix { mod nested { - pub struct A {} + pub struct A; } impl nested::A { @@ -227,7 +227,7 @@ mod rustfix { } mod issue3567 { - struct TestStruct {} + struct TestStruct; impl TestStruct { fn from_something() -> Self { Self {} @@ -248,7 +248,7 @@ mod issue3567 { mod paths_created_by_lowering { use std::ops::Range; - struct S {} + struct S; impl S { const A: usize = 0; @@ -382,7 +382,7 @@ mod issue4305 { } mod lint_at_item_level { - struct Foo {} + struct Foo; #[allow(clippy::use_self)] impl Foo { @@ -400,7 +400,7 @@ mod lint_at_item_level { } mod lint_at_impl_item_level { - struct Foo {} + struct Foo; impl Foo { #[allow(clippy::use_self)] @@ -433,8 +433,8 @@ mod issue4734 { mod nested_paths { use std::convert::Into; mod submod { - pub struct B {} - pub struct C {} + pub struct B; + pub struct C; impl Into for B { fn into(self) -> C { diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index a5fcde768f183..ce58a80347b55 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -42,7 +42,7 @@ mod a { mod b { #[allow(dead_code)] #[allow(unreachable_pub)] - pub struct C {} + pub struct C; } #[allow(unreachable_pub)] diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 0396d39e3d54e..c82bb9ba07fd7 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -42,7 +42,7 @@ mod a { mod b { #[allow(dead_code)] #[allow(unreachable_pub)] - pub struct C {} + pub struct C; } #[allow(unreachable_pub)] diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 77102b8cac0c9..38498ebdcf2c1 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -3,34 +3,32 @@ #![allow(clippy::single_match_else)] use rustc_tools_util::VersionInfo; +use std::fs; #[test] fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { + fn read_version(path: &str) -> String { + let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{}`: {:?}", path, e)); + contents + .lines() + .filter_map(|l| l.split_once('=')) + .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim())) + .unwrap_or_else(|| panic!("error finding version in `{}`", path)) + .to_string() + } + // do not run this test inside the upstream rustc repo: // https://github.com/rust-lang/rust-clippy/issues/6683 if option_env!("RUSTC_TEST_SUITE").is_some() { return; } - let clippy_meta = cargo_metadata::MetadataCommand::new() - .no_deps() - .exec() - .expect("could not obtain cargo metadata"); + let clippy_version = read_version("Cargo.toml"); + let clippy_lints_version = read_version("clippy_lints/Cargo.toml"); + let clippy_utils_version = read_version("clippy_utils/Cargo.toml"); - for krate in &["clippy_lints", "clippy_utils"] { - let krate_meta = cargo_metadata::MetadataCommand::new() - .current_dir(std::env::current_dir().unwrap().join(krate)) - .no_deps() - .exec() - .expect("could not obtain cargo metadata"); - assert_eq!(krate_meta.packages[0].version, clippy_meta.packages[0].version); - for package in &clippy_meta.packages[0].dependencies { - if package.name == *krate { - assert!(package.req.matches(&krate_meta.packages[0].version)); - break; - } - } - } + assert_eq!(clippy_version, clippy_lints_version); + assert_eq!(clippy_version, clippy_utils_version); } #[test] From da26623d5420396304fd581450143f6edc5969e5 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 8 Apr 2022 10:41:55 +0100 Subject: [PATCH 013/129] Update Cargo.lock --- clippy_dev/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index c2ebba0683ca9..81faa5fe5e148 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -9,7 +9,7 @@ indoc = "1.0" itertools = "0.10.1" opener = "0.5" shell-escape = "0.1" -tempfile = "3.3" +tempfile = "3.2" walkdir = "2.3" [features] From b79f13ea5cbe07d571fed747b8a77c9e033ce598 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 8 Apr 2022 15:57:44 +0000 Subject: [PATCH 014/129] Avoid looking at the internals of Interned directly --- .../src/case_sensitive_file_extension_comparisons.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index df780747a0c75..a8f9c189adec8 100644 --- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_ast::ast::LitKind; -use rustc_data_structures::intern::Interned; use rustc_hir::{Expr, ExprKind, PathSegment}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -56,8 +55,8 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: & ty::Str => { return Some(span); }, - ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did, .. }, _)), _) => { - if ctx.tcx.is_diagnostic_item(sym::String, did) { + ty::Adt(def, _) => { + if ctx.tcx.is_diagnostic_item(sym::String, def.did()) { return Some(span); } }, From 224916823a709cb16b77e8582c8433dc728ecc6c Mon Sep 17 00:00:00 2001 From: Miguel Guarniz Date: Sun, 3 Apr 2022 15:50:33 -0400 Subject: [PATCH 015/129] Refactor HIR item-like traversal (part 1) - Create hir_crate_items query which traverses tcx.hir_crate(()).owners to return a hir::ModuleItems - use tcx.hir_crate_items in tcx.hir().items() to return an iterator of hir::ItemId - add par_items(impl Fn(hir::ItemId)) to traverse all items in parallel Signed-off-by: Miguel Guarniz --- clippy_lints/src/same_name_method.rs | 3 ++- tests/ui/same_name_method.stderr | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 22b4589695512..c76061867145e 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -50,7 +50,8 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { let mut map = FxHashMap::::default(); - for item in cx.tcx.hir().items() { + for id in cx.tcx.hir().items() { + let item = cx.tcx.hir().item(id); if let ItemKind::Impl(Impl { items, of_trait, diff --git a/tests/ui/same_name_method.stderr b/tests/ui/same_name_method.stderr index c32c3dd9880f6..cf06eb32e0c7f 100644 --- a/tests/ui/same_name_method.stderr +++ b/tests/ui/same_name_method.stderr @@ -11,6 +11,19 @@ note: existing `foo` defined here LL | fn foo() {} | ^^^^^^^^^^^ +error: method's name is the same as an existing method in a trait + --> $DIR/same_name_method.rs:34:13 + | +LL | fn clone() {} + | ^^^^^^^^^^^^^ + | +note: existing `clone` defined here + --> $DIR/same_name_method.rs:30:18 + | +LL | #[derive(Clone)] + | ^^^^^ + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + error: method's name is the same as an existing method in a trait --> $DIR/same_name_method.rs:44:13 | @@ -47,18 +60,5 @@ note: existing `foo` defined here LL | impl T1 for S {} | ^^^^^^^^^^^^^^^^ -error: method's name is the same as an existing method in a trait - --> $DIR/same_name_method.rs:34:13 - | -LL | fn clone() {} - | ^^^^^^^^^^^^^ - | -note: existing `clone` defined here - --> $DIR/same_name_method.rs:30:18 - | -LL | #[derive(Clone)] - | ^^^^^ - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) - error: aborting due to 5 previous errors From 3363a62068a5e8babb0dd9521207ead4bef90cac Mon Sep 17 00:00:00 2001 From: Miguel Guarniz Date: Thu, 7 Apr 2022 16:47:40 -0400 Subject: [PATCH 016/129] remove CheckVisitor, CollectExternCrateVisitor and ItemLikeVisitor impls Signed-off-by: Miguel Guarniz --- clippy_lints/src/same_name_method.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index c76061867145e..a01e2f2db3afb 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -51,6 +51,10 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { let mut map = FxHashMap::::default(); for id in cx.tcx.hir().items() { + if !matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) { + continue; + } + let item = cx.tcx.hir().item(id); if let ItemKind::Impl(Impl { items, From 63f6a79bf87cd5960c069bb45c88646519d096f8 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 8 Apr 2022 16:51:40 -0400 Subject: [PATCH 017/129] Don't lint various match lints when expanded by a proc-macro --- clippy_lints/src/matches/mod.rs | 5 +++- clippy_utils/src/source.rs | 19 +++++++++++++ tests/ui/auxiliary/proc_macro_with_span.rs | 32 ++++++++++++++++++++++ tests/ui/single_match_else.rs | 18 ++++++++++-- tests/ui/single_match_else.stderr | 11 ++++---- 5 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 tests/ui/auxiliary/proc_macro_with_span.rs diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index e93b494653fc0..854cf06ed8198 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,4 +1,4 @@ -use clippy_utils::source::{snippet_opt, walk_span_to_context}; +use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context}; use clippy_utils::{meets_msrv, msrvs}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; @@ -653,6 +653,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } if let ExprKind::Match(ex, arms, source) = expr.kind { + if !span_starts_with(cx, expr.span, "match") { + return; + } if !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index dbad607c58ea3..beed73268037e 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -7,9 +7,28 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::hygiene; +use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Pos, Span, SyntaxContext}; use std::borrow::Cow; +/// Checks if the span starts with the given text. This will return false if the span crosses +/// multiple files or if source is not available. +/// +/// This is used to check for proc macros giving unhelpful spans to things. +pub fn span_starts_with(cx: &T, span: Span, text: &str) -> bool { + fn helper(sm: &SourceMap, span: Span, text: &str) -> bool { + let pos = sm.lookup_byte_offset(span.lo()); + let Some(ref src) = pos.sf.src else { + return false; + }; + let end = span.hi() - pos.sf.start_pos; + src.get(pos.pos.0 as usize..end.0 as usize) + // Expression spans can include wrapping parenthesis. Remove them first. + .map_or(false, |s| s.trim_start_matches('(').starts_with(text)) + } + helper(cx.sess().source_map(), span, text) +} + /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. /// Also takes an `Option` which can be put inside the braces. pub fn expr_block<'a, T: LintContext>( diff --git a/tests/ui/auxiliary/proc_macro_with_span.rs b/tests/ui/auxiliary/proc_macro_with_span.rs new file mode 100644 index 0000000000000..8ea631f2bbd42 --- /dev/null +++ b/tests/ui/auxiliary/proc_macro_with_span.rs @@ -0,0 +1,32 @@ +// compile-flags: --emit=link +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree}; + +#[proc_macro] +pub fn with_span(input: TokenStream) -> TokenStream { + let mut iter = input.into_iter(); + let span = iter.next().unwrap().span(); + let mut res = TokenStream::new(); + write_with_span(span, iter, &mut res); + res +} + +fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) { + for mut tt in input { + if let TokenTree::Group(g) = tt { + let mut stream = TokenStream::new(); + write_with_span(s, g.stream().into_iter(), &mut stream); + let mut group = Group::new(g.delimiter(), stream); + group.set_span(s); + out.extend([TokenTree::Group(group)]); + } else { + tt.set_span(s); + out.extend([tt]); + } + } +} diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index b624a41a29b2d..82387f3d80b77 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,7 +1,12 @@ +// aux-build: proc_macro_with_span.rs + #![warn(clippy::single_match_else)] #![allow(clippy::needless_return)] #![allow(clippy::no_effect)] +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + enum ExprNode { ExprAddrOf, Butterflies, @@ -11,13 +16,22 @@ enum ExprNode { static NODE: ExprNode = ExprNode::Unicorns; fn unwrap_addr() -> Option<&'static ExprNode> { - match ExprNode::Butterflies { + let _ = match ExprNode::Butterflies { ExprNode::ExprAddrOf => Some(&NODE), _ => { let x = 5; None }, - } + }; + + // Don't lint + with_span!(span match ExprNode::Butterflies { + ExprNode::ExprAddrOf => Some(&NODE), + _ => { + let x = 5; + None + }, + }) } macro_rules! unwrap_addr { diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 21ea704b62ab5..7756c6f204e67 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,22 +1,23 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:14:5 + --> $DIR/single_match_else.rs:19:13 | -LL | / match ExprNode::Butterflies { +LL | let _ = match ExprNode::Butterflies { + | _____________^ LL | | ExprNode::ExprAddrOf => Some(&NODE), LL | | _ => { LL | | let x = 5; LL | | None LL | | }, -LL | | } +LL | | }; | |_____^ | = note: `-D clippy::single-match-else` implied by `-D warnings` help: try this | -LL ~ if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { +LL ~ let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { LL + let x = 5; LL + None -LL + } +LL ~ }; | error: aborting due to previous error From 744b0ff9035298a8f62accf7f48701fa56d2df49 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 8 Apr 2022 23:15:18 -0400 Subject: [PATCH 018/129] Don't lint `iter_with_drain` on references --- clippy_lints/src/methods/iter_with_drain.rs | 91 ++++++++------------- tests/ui/iter_with_drain.fixed | 9 ++ tests/ui/iter_with_drain.rs | 9 ++ 3 files changed, 51 insertions(+), 58 deletions(-) diff --git a/clippy_lints/src/methods/iter_with_drain.rs b/clippy_lints/src/methods/iter_with_drain.rs index 958c3773087b6..152072e09c772 100644 --- a/clippy_lints/src/methods/iter_with_drain.rs +++ b/clippy_lints/src/methods/iter_with_drain.rs @@ -1,72 +1,47 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher::Range; use clippy_utils::is_integer_const; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{ - higher::{self, Range}, - SpanlessEq, -}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::sym; use rustc_span::Span; use super::ITER_WITH_DRAIN; -const DRAIN_TYPES: &[Symbol] = &[sym::Vec, sym::VecDeque]; - pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) { - let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if let Some(drained_type) = DRAIN_TYPES.iter().find(|&&sym| is_type_diagnostic_item(cx, ty, sym)) { - // Refuse to emit `into_iter` suggestion on draining struct fields due - // to the strong possibility of processing unmovable field. - if let ExprKind::Field(..) = recv.kind { - return; - } + if !matches!(recv.kind, ExprKind::Field(..)) + && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() + && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did()) + && matches!(ty_name, sym::Vec | sym::VecDeque) + && let Some(range) = Range::hir(arg) + && is_full_range(cx, recv, range) + { + span_lint_and_sugg( + cx, + ITER_WITH_DRAIN, + span.with_hi(expr.span.hi()), + &format!("`drain(..)` used on a `{}`", ty_name), + "try this", + "into_iter()".to_string(), + Applicability::MaybeIncorrect, + ); + }; +} - if let Some(range) = higher::Range::hir(arg) { - let left_full = match range { - Range { start: Some(start), .. } if is_integer_const(cx, start, 0) => true, - Range { start: None, .. } => true, - _ => false, - }; - let full = left_full - && match range { - Range { - end: Some(end), - limits: RangeLimits::HalfOpen, - .. - } => { - // `x.drain(..x.len())` call - if_chain! { - if let ExprKind::MethodCall(len_path, len_args, _) = end.kind; - if len_path.ident.name == sym::len && len_args.len() == 1; - if let ExprKind::Path(QPath::Resolved(_, drain_path)) = recv.kind; - if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path(drain_path, len_path); - then { true } - else { false } - } - }, - Range { - end: None, - limits: RangeLimits::HalfOpen, - .. - } => true, - _ => false, - }; - if full { - span_lint_and_sugg( - cx, - ITER_WITH_DRAIN, - span.with_hi(expr.span.hi()), - &format!("`drain(..)` used on a `{}`", drained_type), - "try this", - "into_iter()".to_string(), - Applicability::MaybeIncorrect, - ); +fn is_full_range(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool { + range.start.map_or(true, |e| is_integer_const(cx, e, 0)) + && range.end.map_or(true, |e| { + if range.limits == RangeLimits::HalfOpen + && let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind + && let ExprKind::MethodCall(name, [self_arg], _) = e.kind + && name.ident.name == sym::len + && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind + { + container_path.res == path.res + } else { + false } - } - } + }) } diff --git a/tests/ui/iter_with_drain.fixed b/tests/ui/iter_with_drain.fixed index aea4dba9dd5a0..0330d5549264a 100644 --- a/tests/ui/iter_with_drain.fixed +++ b/tests/ui/iter_with_drain.fixed @@ -39,6 +39,15 @@ fn should_not_help() { let _: Vec<_> = b.drain(0..a.len()).collect(); } +fn _closed_range(mut x: Vec) { + let _: Vec = x.drain(0..=x.len()).collect(); +} + +fn _with_mut(x: &mut Vec, y: &mut VecDeque) { + let _: Vec = x.drain(..).collect(); + let _: Vec = y.drain(..).collect(); +} + #[derive(Default)] struct Bomb { fire: Vec, diff --git a/tests/ui/iter_with_drain.rs b/tests/ui/iter_with_drain.rs index 271878cffb44c..993936fb8de3d 100644 --- a/tests/ui/iter_with_drain.rs +++ b/tests/ui/iter_with_drain.rs @@ -39,6 +39,15 @@ fn should_not_help() { let _: Vec<_> = b.drain(0..a.len()).collect(); } +fn _closed_range(mut x: Vec) { + let _: Vec = x.drain(0..=x.len()).collect(); +} + +fn _with_mut(x: &mut Vec, y: &mut VecDeque) { + let _: Vec = x.drain(..).collect(); + let _: Vec = y.drain(..).collect(); +} + #[derive(Default)] struct Bomb { fire: Vec, From a9511482d6d22188e8097261c17c69dece9a198e Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Sat, 9 Apr 2022 20:34:43 +0900 Subject: [PATCH 019/129] fix comments in test for split_once split_once was stabilized in 1.52 --- tests/ui/manual_split_once.fixed | 2 +- tests/ui/manual_split_once.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed index d5113df569a08..24ac364ffd9f7 100644 --- a/tests/ui/manual_split_once.fixed +++ b/tests/ui/manual_split_once.fixed @@ -46,7 +46,7 @@ fn main() { fn _msrv_1_51() { #![clippy::msrv = "1.51"] - // `str::split_once` was stabilized in 1.16. Do not lint this + // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); } diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs index 80e02952dbd07..85c0c7d69778b 100644 --- a/tests/ui/manual_split_once.rs +++ b/tests/ui/manual_split_once.rs @@ -46,7 +46,7 @@ fn main() { fn _msrv_1_51() { #![clippy::msrv = "1.51"] - // `str::split_once` was stabilized in 1.16. Do not lint this + // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); } From ffde78b4564301888fcb4d5b970d8e9475bd33e9 Mon Sep 17 00:00:00 2001 From: Yoav Lavi Date: Fri, 8 Apr 2022 15:02:49 +0200 Subject: [PATCH 020/129] Allow passing --remove to `cargo dev setup ` add missing args unque name not needed more descriptive help formatting fixes missing quote --- clippy_dev/src/main.rs | 51 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index b1fe35a0243f0..833b7393ff8b9 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -36,13 +36,31 @@ fn main() { } }, ("setup", Some(sub_command)) => match sub_command.subcommand() { - ("intellij", Some(matches)) => setup::intellij::setup_rustc_src( - matches - .value_of("rustc-repo-path") - .expect("this field is mandatory and therefore always valid"), - ), - ("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")), - ("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")), + ("intellij", Some(matches)) => { + if matches.is_present("remove") { + setup::intellij::remove_rustc_src(); + } else { + setup::intellij::setup_rustc_src( + matches + .value_of("rustc-repo-path") + .expect("this field is mandatory and therefore always valid"), + ); + } + }, + ("git-hook", Some(matches)) => { + if matches.is_present("remove") { + setup::git_hook::remove_hook(); + } else { + setup::git_hook::install_hook(matches.is_present("force-override")); + } + }, + ("vscode-tasks", Some(matches)) => { + if matches.is_present("remove") { + setup::vscode::remove_tasks(); + } else { + setup::vscode::install_tasks(matches.is_present("force-override")); + } + }, _ => {}, }, ("remove", Some(sub_command)) => match sub_command.subcommand() { @@ -167,6 +185,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .subcommand( SubCommand::with_name("intellij") .about("Alter dependencies so Intellij Rust can find rustc internals") + .arg( + Arg::with_name("remove") + .long("remove") + .help("Remove the dependencies added with 'cargo dev setup intellij'") + .required(false), + ) .arg( Arg::with_name("rustc-repo-path") .long("repo-path") @@ -174,12 +198,19 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .help("The path to a rustc repo that will be used for setting the dependencies") .takes_value(true) .value_name("path") + .conflicts_with("remove") .required(true), ), ) .subcommand( SubCommand::with_name("git-hook") .about("Add a pre-commit git hook that formats your code to make it look pretty") + .arg( + Arg::with_name("remove") + .long("remove") + .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") + .required(false), + ) .arg( Arg::with_name("force-override") .long("force-override") @@ -191,6 +222,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .subcommand( SubCommand::with_name("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") + .arg( + Arg::with_name("remove") + .long("remove") + .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") + .required(false), + ) .arg( Arg::with_name("force-override") .long("force-override") From 1450c989aeb5de1959759b6ed8b7b41c703f9969 Mon Sep 17 00:00:00 2001 From: Andy Kurnia Date: Sun, 10 Apr 2022 01:07:01 +0800 Subject: [PATCH 021/129] fix typo --- clippy_lints/src/drop_forget_ref.rs | 2 +- tests/ui/drop_non_drop.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 88c54828da834..25014bfa1a5b1 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -180,7 +180,7 @@ const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that imp const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \ Forgetting a copy leaves the original intact"; const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \ - Dropping such a type only extends it's contained lifetimes"; + Dropping such a type only extends its contained lifetimes"; const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \ Forgetting such a type is the same as dropping it"; diff --git a/tests/ui/drop_non_drop.stderr b/tests/ui/drop_non_drop.stderr index f73068901c503..30121033de7ea 100644 --- a/tests/ui/drop_non_drop.stderr +++ b/tests/ui/drop_non_drop.stderr @@ -1,4 +1,4 @@ -error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes --> $DIR/drop_non_drop.rs:22:5 | LL | drop(Foo); @@ -11,7 +11,7 @@ note: argument has type `main::Foo` LL | drop(Foo); | ^^^ -error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes --> $DIR/drop_non_drop.rs:37:5 | LL | drop(Baz(Foo)); From 719a040397bedd1ca1444df066acdf41758b2c9a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 10 Apr 2022 00:54:41 -0400 Subject: [PATCH 022/129] Compare inline constants by their bodies rather than value in `SpanlessEq` --- clippy_utils/src/hir_utils.rs | 37 +++++++++++-------- tests/ui/same_functions_in_if_condition.rs | 19 ++++++++++ .../ui/same_functions_in_if_condition.stderr | 24 ++++++------ 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 00594f4d42add..20206ce82a72d 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant_context, constant_simple}; +use crate::consts::constant_simple; use crate::source::snippet_opt; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; @@ -16,15 +16,14 @@ use rustc_span::Symbol; use std::hash::{Hash, Hasher}; /// Type used to check whether two ast are the same. This is different from the -/// operator -/// `==` on ast types as this operator would compare true equality with ID and -/// span. +/// operator `==` on ast types as this operator would compare true equality with +/// ID and span. /// /// Note that some expressions kinds are not considered but could be added. pub struct SpanlessEq<'a, 'tcx> { /// Context used to evaluate constant expressions. cx: &'a LateContext<'tcx>, - maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, + maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>, allow_side_effects: bool, expr_fallback: Option, &Expr<'_>) -> bool + 'a>>, } @@ -33,7 +32,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, - maybe_typeck_results: cx.maybe_typeck_results(), + maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)), allow_side_effects: true, expr_fallback: None, } @@ -102,9 +101,9 @@ impl HirEqInterExpr<'_, '_, '_> { (&StmtKind::Local(l), &StmtKind::Local(r)) => { // This additional check ensures that the type of the locals are equivalent even if the init // expression or type have some inferred parts. - if let Some(typeck) = self.inner.maybe_typeck_results { - let l_ty = typeck.pat_ty(l.pat); - let r_ty = typeck.pat_ty(r.pat); + if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { + let l_ty = typeck_lhs.pat_ty(l.pat); + let r_ty = typeck_rhs.pat_ty(r.pat); if l_ty != r_ty { return false; } @@ -182,9 +181,17 @@ impl HirEqInterExpr<'_, '_, '_> { } 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); - eval_const(left) == eval_const(right) + // swap out TypeckResults when hashing a body + let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace(( + self.inner.cx.tcx.typeck_body(left), + self.inner.cx.tcx.typeck_body(right), + )); + let res = self.eq_expr( + &self.inner.cx.tcx.hir().body(left).value, + &self.inner.cx.tcx.hir().body(right).value, + ); + self.inner.maybe_typeck_results = old_maybe_typeck_results; + res } #[allow(clippy::similar_names)] @@ -193,10 +200,10 @@ impl HirEqInterExpr<'_, '_, '_> { return false; } - if let Some(typeck_results) = self.inner.maybe_typeck_results { + if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { if let (Some(l), Some(r)) = ( - constant_simple(self.inner.cx, typeck_results, left), - constant_simple(self.inner.cx, typeck_results, right), + constant_simple(self.inner.cx, typeck_lhs, left), + constant_simple(self.inner.cx, typeck_rhs, right), ) { if l == r { return true; diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index 7f28f02579045..3d2295912c9fd 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -1,3 +1,5 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] #![warn(clippy::same_functions_in_if_condition)] #![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`. #![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks @@ -87,4 +89,21 @@ fn main() { "linux" }; println!("{}", os); + + #[derive(PartialEq, Eq)] + enum E { + A, + B, + } + fn generic() -> bool { + match P { + E::A => true, + E::B => false, + } + } + if generic::<{ E::A }>() { + println!("A"); + } else if generic::<{ E::B }>() { + println!("B"); + } } diff --git a/tests/ui/same_functions_in_if_condition.stderr b/tests/ui/same_functions_in_if_condition.stderr index 363a03846d236..71e82910ef752 100644 --- a/tests/ui/same_functions_in_if_condition.stderr +++ b/tests/ui/same_functions_in_if_condition.stderr @@ -1,72 +1,72 @@ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:29:15 + --> $DIR/same_functions_in_if_condition.rs:31:15 | LL | } else if function() { | ^^^^^^^^^^ | = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings` note: same as this - --> $DIR/same_functions_in_if_condition.rs:28:8 + --> $DIR/same_functions_in_if_condition.rs:30:8 | LL | if function() { | ^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:34:15 + --> $DIR/same_functions_in_if_condition.rs:36:15 | LL | } else if fn_arg(a) { | ^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:33:8 + --> $DIR/same_functions_in_if_condition.rs:35:8 | LL | if fn_arg(a) { | ^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:39:15 + --> $DIR/same_functions_in_if_condition.rs:41:15 | LL | } else if obj.method() { | ^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:38:8 + --> $DIR/same_functions_in_if_condition.rs:40:8 | LL | if obj.method() { | ^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:44:15 + --> $DIR/same_functions_in_if_condition.rs:46:15 | LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:43:8 + --> $DIR/same_functions_in_if_condition.rs:45:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:51:15 + --> $DIR/same_functions_in_if_condition.rs:53:15 | LL | } else if v.pop() == None { | ^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:49:8 + --> $DIR/same_functions_in_if_condition.rs:51:8 | LL | if v.pop() == None { | ^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:56:15 + --> $DIR/same_functions_in_if_condition.rs:58:15 | LL | } else if v.len() == 42 { | ^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:54:8 + --> $DIR/same_functions_in_if_condition.rs:56:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ From 5e335a52bc49991f6ffd40030b9948f3390f499d Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 10 Apr 2022 14:02:58 +0100 Subject: [PATCH 023/129] Check for loops/closures in `local_used_after_expr` --- clippy_lints/src/eta_reduction.rs | 5 ++-- clippy_utils/src/usage.rs | 33 +++++++++++++++++++++++-- tests/ui/eta.fixed | 4 +++ tests/ui/eta.rs | 4 +++ tests/ui/eta.stderr | 10 ++++++-- tests/ui/needless_option_as_deref.fixed | 14 +++++++++++ tests/ui/needless_option_as_deref.rs | 14 +++++++++++ 7 files changed, 77 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 845863bd209c6..c2e32f1d9a23e 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -3,7 +3,7 @@ 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 clippy_utils::{higher, path_to_local, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -125,8 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if_chain! { if let ty::Closure(_, substs) = callee_ty.peel_refs().kind(); if substs.as_closure().kind() == ClosureKind::FnMut; - if get_enclosing_loop_or_closure(cx.tcx, expr).is_some() - || path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee)); + if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)); then { // Mutable closure is used after current expr; we cannot consume it. diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 405e306359bc9..4236e3aae2fbd 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -3,7 +3,7 @@ use crate::visitors::{expr_visitor, expr_visitor_no_bodies}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirIdSet; -use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_hir::{Expr, ExprKind, HirId, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -169,6 +169,32 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool { let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false }; + + // for _ in 1..3 { + // local + // } + // + // let closure = || local; + // closure(); + // closure(); + let in_loop_or_closure = cx + .tcx + .hir() + .parent_iter(after.hir_id) + .take_while(|&(id, _)| id != block.hir_id) + .any(|(_, node)| { + matches!( + node, + Node::Expr(Expr { + kind: ExprKind::Loop(..) | ExprKind::Closure(..), + .. + }) + ) + }); + if in_loop_or_closure { + return true; + } + let mut used_after_expr = false; let mut past_expr = false; expr_visitor(cx, |expr| { @@ -178,7 +204,10 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr if expr.hir_id == after.hir_id { past_expr = true; - } else if past_expr && utils::path_to_local_id(expr, local_id) { + return false; + } + + if past_expr && utils::path_to_local_id(expr, local_id) { used_after_expr = true; } !used_after_expr diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 5aedbea381f23..873ed689a5be7 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -211,6 +211,10 @@ fn mutable_closure_in_loop() { let mut closure = |n| value += n; for _ in 0..5 { Some(1).map(&mut closure); + + let mut value = 0; + let mut in_loop = |n| value += n; + Some(1).map(in_loop); } } diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 5fdf7fb977169..4cb58eec94cd7 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -211,6 +211,10 @@ fn mutable_closure_in_loop() { let mut closure = |n| value += n; for _ in 0..5 { Some(1).map(|n| closure(n)); + + let mut value = 0; + let mut in_loop = |n| value += n; + Some(1).map(|n| in_loop(n)); } } diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index cda84982c9b75..d1ae889d6f360 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -117,10 +117,16 @@ LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> $DIR/eta.rs:232:21 + --> $DIR/eta.rs:217:21 + | +LL | Some(1).map(|n| in_loop(n)); + | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` + +error: redundant closure + --> $DIR/eta.rs:236:21 | LL | map_str_to_path(|s| s.as_ref()); | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref` -error: aborting due to 20 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/needless_option_as_deref.fixed b/tests/ui/needless_option_as_deref.fixed index c09b07db3dca9..acd22c6bb4337 100644 --- a/tests/ui/needless_option_as_deref.fixed +++ b/tests/ui/needless_option_as_deref.fixed @@ -16,6 +16,20 @@ fn main() { let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + let mut y = 0; + let mut x = Some(&mut y); + for _ in 0..3 { + let _ = x.as_deref_mut(); + } + + let mut y = 0; + let mut x = Some(&mut y); + let mut closure = || { + let _ = x.as_deref_mut(); + }; + closure(); + closure(); + // #7846 let mut i = 0; let mut opt_vec = vec![Some(&mut i)]; diff --git a/tests/ui/needless_option_as_deref.rs b/tests/ui/needless_option_as_deref.rs index c3ba27ecccf22..61eda5052a21e 100644 --- a/tests/ui/needless_option_as_deref.rs +++ b/tests/ui/needless_option_as_deref.rs @@ -16,6 +16,20 @@ fn main() { let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + let mut y = 0; + let mut x = Some(&mut y); + for _ in 0..3 { + let _ = x.as_deref_mut(); + } + + let mut y = 0; + let mut x = Some(&mut y); + let mut closure = || { + let _ = x.as_deref_mut(); + }; + closure(); + closure(); + // #7846 let mut i = 0; let mut opt_vec = vec![Some(&mut i)]; From 6fba89751bfacf59109d37331ec79a8f87ee0aaf Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 4 Apr 2022 16:30:38 +0100 Subject: [PATCH 024/129] Remove overlap between `manual_split_once` and `needless_splitn` Also fixes some incorrect suggestions for rsplitn --- clippy_lints/src/methods/mod.rs | 7 +- clippy_lints/src/methods/str_splitn.rs | 246 ++++++++----------------- tests/ui/crashes/ice-8250.stderr | 10 +- tests/ui/manual_split_once.fixed | 21 +-- tests/ui/manual_split_once.rs | 21 +-- tests/ui/manual_split_once.stderr | 86 ++++----- tests/ui/needless_splitn.fixed | 20 ++ tests/ui/needless_splitn.rs | 20 ++ tests/ui/needless_splitn.stderr | 44 ++++- 9 files changed, 216 insertions(+), 259 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 021337280d139..7047aa4c69b5d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2516,12 +2516,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("splitn" | "rsplitn", [count_arg, pat_arg]) => { if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); - if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) { - str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg); - } - if count >= 2 { - str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count); - } + str_splitn::check(cx, name, expr, recv, pat_arg, count, msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 8125930b30461..6eaa9eb2ef0ec 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,36 +1,77 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::{is_diag_item_method, match_def_path, paths}; +use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, adjustment::Adjust}; +use rustc_middle::ty; +use rustc_semver::RustcVersion; use rustc_span::{symbol::sym, Span, SyntaxContext}; -use super::MANUAL_SPLIT_ONCE; +use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; -pub(super) fn check_manual_split_once( +pub(super) fn check( cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, + count: u128, + msrv: Option<&RustcVersion>, ) { - if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { + if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { return; } let ctxt = expr.span.ctxt(); - let (method_name, msg, reverse) = if method_name == "splitn" { - ("split_once", "manual implementation of `split_once`", false) - } else { - ("rsplit_once", "manual implementation of `rsplit_once`", true) + let Some(usage) = parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) else { return }; + + let needless = match usage.kind { + IterUsageKind::Nth(n) => count > n + 1, + IterUsageKind::NextTuple => count > 2, }; - let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) { - Some(x) => x, - None => return, + + if needless { + let mut app = Applicability::MachineApplicable; + let (r, message) = if method_name == "splitn" { + ("", "unnecessary use of `splitn`") + } else { + ("r", "unnecessary use of `rsplitn`") + }; + + span_lint_and_sugg( + cx, + NEEDLESS_SPLITN, + expr.span, + message, + "try this", + format!( + "{}.{r}split({})", + snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0, + snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0, + ), + app, + ); + } else if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) { + check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage); + } +} + +fn check_manual_split_once( + cx: &LateContext<'_>, + method_name: &str, + expr: &Expr<'_>, + self_arg: &Expr<'_>, + pat_arg: &Expr<'_>, + usage: &IterUsage, +) { + let ctxt = expr.span.ctxt(); + let (msg, reverse) = if method_name == "splitn" { + ("manual implementation of `split_once`", false) + } else { + ("manual implementation of `rsplit_once`", true) }; let mut app = Applicability::MachineApplicable; @@ -39,77 +80,36 @@ pub(super) fn check_manual_split_once( let sugg = match usage.kind { IterUsageKind::NextTuple => { - format!("{}.{}({})", self_snip, method_name, pat_snip) - }, - IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip), - IterUsageKind::Next | IterUsageKind::Second => { - let self_deref = { - let adjust = cx.typeck_results().expr_adjustments(self_arg); - if adjust.len() < 2 { - String::new() - } else if cx.typeck_results().expr_ty(self_arg).is_box() - || adjust - .iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box()) - { - format!("&{}", "*".repeat(adjust.len().saturating_sub(1))) - } else { - "*".repeat(adjust.len().saturating_sub(2)) - } - }; - if matches!(usage.kind, IterUsageKind::Next) { - match usage.unwrap_kind { - Some(UnwrapKind::Unwrap) => { - if reverse { - format!("{}.{}({}).unwrap().0", self_snip, method_name, pat_snip) - } else { - format!( - "{}.{}({}).map_or({}{}, |x| x.0)", - self_snip, method_name, pat_snip, self_deref, &self_snip - ) - } - }, - Some(UnwrapKind::QuestionMark) => { - format!( - "{}.{}({}).map_or({}{}, |x| x.0)", - self_snip, method_name, pat_snip, self_deref, &self_snip - ) - }, - None => { - format!( - "Some({}.{}({}).map_or({}{}, |x| x.0))", - &self_snip, method_name, pat_snip, self_deref, &self_snip - ) - }, - } + if reverse { + format!("{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))") } else { - match usage.unwrap_kind { - Some(UnwrapKind::Unwrap) => { - if reverse { - // In this case, no better suggestion is offered. - return; - } - format!("{}.{}({}).unwrap().1", self_snip, method_name, pat_snip) - }, - Some(UnwrapKind::QuestionMark) => { - format!("{}.{}({})?.1", self_snip, method_name, pat_snip) - }, - None => { - format!("{}.{}({}).map(|x| x.1)", self_snip, method_name, pat_snip) - }, - } + format!("{self_snip}.split_once({pat_snip})") } }, + IterUsageKind::Nth(1) => { + let (r, field) = if reverse { ("r", 0) } else { ("", 1) }; + + match usage.unwrap_kind { + Some(UnwrapKind::Unwrap) => { + format!("{self_snip}.{r}split_once({pat_snip}).unwrap().{field}") + }, + Some(UnwrapKind::QuestionMark) => { + format!("{self_snip}.{r}split_once({pat_snip})?.{field}") + }, + None => { + format!("{self_snip}.{r}split_once({pat_snip}).map(|x| x.{field})") + }, + } + }, + IterUsageKind::Nth(_) => return, }; span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app); } enum IterUsageKind { - Next, - Second, + Nth(u128), NextTuple, - RNextTuple, } enum UnwrapKind { @@ -128,7 +128,6 @@ fn parse_iter_usage<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, mut iter: impl Iterator)>, - reverse: bool, ) -> Option { let (kind, span) = match iter.next() { Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { @@ -141,13 +140,7 @@ fn parse_iter_usage<'tcx>( let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?; match (name.ident.as_str(), args) { - ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if reverse { - (IterUsageKind::Second, e.span) - } else { - (IterUsageKind::Next, e.span) - } - }, + ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span), ("next_tuple", []) => { return if_chain! { if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE); @@ -157,7 +150,7 @@ fn parse_iter_usage<'tcx>( if subs.len() == 2; then { Some(IterUsage { - kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple }, + kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None }) @@ -185,11 +178,7 @@ fn parse_iter_usage<'tcx>( } } }; - match if reverse { idx ^ 1 } else { idx } { - 0 => (IterUsageKind::Next, span), - 1 => (IterUsageKind::Second, span), - _ => return None, - } + (IterUsageKind::Nth(idx), span) } else { return None; } @@ -238,86 +227,3 @@ fn parse_iter_usage<'tcx>( span, }) } - -use super::NEEDLESS_SPLITN; - -pub(super) fn check_needless_splitn( - cx: &LateContext<'_>, - method_name: &str, - expr: &Expr<'_>, - self_arg: &Expr<'_>, - pat_arg: &Expr<'_>, - count: u128, -) { - if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { - return; - } - let ctxt = expr.span.ctxt(); - let mut app = Applicability::MachineApplicable; - let (reverse, message) = if method_name == "splitn" { - (false, "unnecessary use of `splitn`") - } else { - (true, "unnecessary use of `rsplitn`") - }; - if_chain! { - if count >= 2; - if check_iter(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), count); - then { - span_lint_and_sugg( - cx, - NEEDLESS_SPLITN, - expr.span, - message, - "try this", - format!( - "{}.{}({})", - snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0, - if reverse {"rsplit"} else {"split"}, - snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0 - ), - app, - ); - } - } -} - -fn check_iter<'tcx>( - cx: &LateContext<'tcx>, - ctxt: SyntaxContext, - mut iter: impl Iterator)>, - count: u128, -) -> bool { - match iter.next() { - Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { - let (name, args) = if let ExprKind::MethodCall(name, [_, args @ ..], _) = e.kind { - (name, args) - } else { - return false; - }; - if_chain! { - if let Some(did) = cx.typeck_results().type_dependent_def_id(e.hir_id); - if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - then { - match (name.ident.as_str(), args) { - ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - return true; - }, - ("next_tuple", []) if count > 2 => { - return true; - }, - ("nth", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) { - if count > idx + 1 { - return true; - } - } - }, - _ => return false, - } - } - } - }, - _ => return false, - }; - false -} diff --git a/tests/ui/crashes/ice-8250.stderr b/tests/ui/crashes/ice-8250.stderr index 04ea445665652..8ed8f3b3a0642 100644 --- a/tests/ui/crashes/ice-8250.stderr +++ b/tests/ui/crashes/ice-8250.stderr @@ -1,11 +1,3 @@ -error: manual implementation of `split_once` - --> $DIR/ice-8250.rs:2:13 - | -LL | let _ = s[1..].splitn(2, '.').next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split_once('.').map_or(s[1..], |x| x.0)` - | - = note: `-D clippy::manual-split-once` implied by `-D warnings` - error: unnecessary use of `splitn` --> $DIR/ice-8250.rs:2:13 | @@ -14,5 +6,5 @@ LL | let _ = s[1..].splitn(2, '.').next()?; | = note: `-D clippy::needless-splitn` implied by `-D warnings` -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed index d5113df569a08..8621ddc746724 100644 --- a/tests/ui/manual_split_once.fixed +++ b/tests/ui/manual_split_once.fixed @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] -#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)] +#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)] extern crate itertools; @@ -10,27 +10,25 @@ extern crate itertools; use itertools::Itertools; fn main() { - let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0)); let _ = "key=value".splitn(2, '=').nth(2); - let _ = "key=value".split_once('=').map_or("key=value", |x| x.0); - let _ = "key=value".split_once('=').map_or("key=value", |x| x.0); let _ = "key=value".split_once('=').unwrap().1; let _ = "key=value".split_once('=').unwrap().1; let (_, _) = "key=value".split_once('=').unwrap(); let s = String::from("key=value"); - let _ = s.split_once('=').map_or(&*s, |x| x.0); + let _ = s.split_once('=').unwrap().1; let s = Box::::from("key=value"); - let _ = s.split_once('=').map_or(&*s, |x| x.0); + let _ = s.split_once('=').unwrap().1; let s = &"key=value"; - let _ = s.split_once('=').map_or(*s, |x| x.0); + let _ = s.split_once('=').unwrap().1; fn _f(s: &str) -> Option<&str> { - let _ = s.split_once("key=value").map_or(s, |x| x.0); - let _ = s.split_once("key=value")?.1; - let _ = s.split_once("key=value")?.1; + let _ = s.split_once('=')?.1; + let _ = s.split_once('=')?.1; + let _ = s.rsplit_once('=')?.0; + let _ = s.rsplit_once('=')?.0; None } @@ -38,10 +36,9 @@ fn main() { let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); // `rsplitn` gives the results in the reverse order of `rsplit_once` - let _ = "key=value".rsplitn(2, '=').next().unwrap(); let _ = "key=value".rsplit_once('=').unwrap().0; - let _ = "key=value".rsplit_once('=').map(|x| x.1); let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap(); + let _ = s.rsplit_once('=').map(|x| x.0); } fn _msrv_1_51() { diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs index 80e02952dbd07..c8a1d7b91e5d1 100644 --- a/tests/ui/manual_split_once.rs +++ b/tests/ui/manual_split_once.rs @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] -#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)] +#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)] extern crate itertools; @@ -10,27 +10,25 @@ extern crate itertools; use itertools::Itertools; fn main() { - let _ = "key=value".splitn(2, '=').next(); let _ = "key=value".splitn(2, '=').nth(2); - let _ = "key=value".splitn(2, '=').next().unwrap(); - let _ = "key=value".splitn(2, '=').nth(0).unwrap(); let _ = "key=value".splitn(2, '=').nth(1).unwrap(); let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); let s = String::from("key=value"); - let _ = s.splitn(2, '=').next().unwrap(); + let _ = s.splitn(2, '=').nth(1).unwrap(); let s = Box::::from("key=value"); - let _ = s.splitn(2, '=').nth(0).unwrap(); + let _ = s.splitn(2, '=').nth(1).unwrap(); let s = &"key=value"; - let _ = s.splitn(2, '=').skip(0).next().unwrap(); + let _ = s.splitn(2, '=').skip(1).next().unwrap(); fn _f(s: &str) -> Option<&str> { - let _ = s.splitn(2, "key=value").next()?; - let _ = s.splitn(2, "key=value").nth(1)?; - let _ = s.splitn(2, "key=value").skip(1).next()?; + let _ = s.splitn(2, '=').nth(1)?; + let _ = s.splitn(2, '=').skip(1).next()?; + let _ = s.rsplitn(2, '=').nth(1)?; + let _ = s.rsplitn(2, '=').skip(1).next()?; None } @@ -38,10 +36,9 @@ fn main() { let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); // `rsplitn` gives the results in the reverse order of `rsplit_once` - let _ = "key=value".rsplitn(2, '=').next().unwrap(); let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); - let _ = "key=value".rsplitn(2, '=').nth(0); let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); + let _ = s.rsplitn(2, '=').nth(1); } fn _msrv_1_51() { diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr index af9c7a2d41bff..9635e6c5e4b46 100644 --- a/tests/ui/manual_split_once.stderr +++ b/tests/ui/manual_split_once.stderr @@ -1,100 +1,88 @@ error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:13:13 + --> $DIR/manual_split_once.rs:14:13 | -LL | let _ = "key=value".splitn(2, '=').next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))` +LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` | = note: `-D clippy::manual-split-once` implied by `-D warnings` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:15:13 | -LL | let _ = "key=value".splitn(2, '=').next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:16:13 - | -LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:17:13 - | -LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:18:13 - | LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:19:18 + --> $DIR/manual_split_once.rs:16:18 | LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')` +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:19:13 + | +LL | let _ = s.splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` + error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:22:13 | -LL | let _ = s.splitn(2, '=').next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)` +LL | let _ = s.splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:25:13 | -LL | let _ = s.splitn(2, '=').nth(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)` +LL | let _ = s.splitn(2, '=').skip(1).next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:28:13 + --> $DIR/manual_split_once.rs:28:17 | -LL | let _ = s.splitn(2, '=').skip(0).next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)` +LL | let _ = s.splitn(2, '=').nth(1)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:31:17 + --> $DIR/manual_split_once.rs:29:17 | -LL | let _ = s.splitn(2, "key=value").next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)` +LL | let _ = s.splitn(2, '=').skip(1).next()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:32:17 +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:30:17 | -LL | let _ = s.splitn(2, "key=value").nth(1)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1` +LL | let _ = s.rsplitn(2, '=').nth(1)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:33:17 +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:31:17 | -LL | let _ = s.splitn(2, "key=value").skip(1).next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1` +LL | let _ = s.rsplitn(2, '=').skip(1).next()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:42:13 + --> $DIR/manual_split_once.rs:39:13 | LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:43:13 + --> $DIR/manual_split_once.rs:40:18 | -LL | let _ = "key=value".rsplitn(2, '=').nth(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)` +LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:44:18 + --> $DIR/manual_split_once.rs:41:13 | -LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` +LL | let _ = s.rsplitn(2, '=').nth(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:55:13 + --> $DIR/manual_split_once.rs:52:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` -error: aborting due to 16 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/needless_splitn.fixed b/tests/ui/needless_splitn.fixed index f6a4b2f17d33d..61f5fc4e679ed 100644 --- a/tests/ui/needless_splitn.fixed +++ b/tests/ui/needless_splitn.fixed @@ -24,4 +24,24 @@ fn main() { let _ = str.rsplitn(2, '=').nth(1); let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap(); let (_, _) = str.rsplit('=').next_tuple().unwrap(); + + let _ = str.split('=').next(); + let _ = str.split('=').nth(3); + let _ = str.splitn(5, '=').nth(4); + let _ = str.splitn(5, '=').nth(5); +} + +fn _question_mark(s: &str) -> Option<()> { + let _ = s.split('=').next()?; + let _ = s.split('=').nth(0)?; + let _ = s.rsplit('=').next()?; + let _ = s.rsplit('=').nth(0)?; + + Some(()) +} + +fn _test_msrv() { + #![clippy::msrv = "1.51"] + // `manual_split_once` MSRV shouldn't apply to `needless_splitn` + let _ = "key=value".split('=').nth(0).unwrap(); } diff --git a/tests/ui/needless_splitn.rs b/tests/ui/needless_splitn.rs index 6ba32255bb2d7..71d9a7077faa6 100644 --- a/tests/ui/needless_splitn.rs +++ b/tests/ui/needless_splitn.rs @@ -24,4 +24,24 @@ fn main() { let _ = str.rsplitn(2, '=').nth(1); let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap(); let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap(); + + let _ = str.splitn(5, '=').next(); + let _ = str.splitn(5, '=').nth(3); + let _ = str.splitn(5, '=').nth(4); + let _ = str.splitn(5, '=').nth(5); +} + +fn _question_mark(s: &str) -> Option<()> { + let _ = s.splitn(2, '=').next()?; + let _ = s.splitn(2, '=').nth(0)?; + let _ = s.rsplitn(2, '=').next()?; + let _ = s.rsplitn(2, '=').nth(0)?; + + Some(()) +} + +fn _test_msrv() { + #![clippy::msrv = "1.51"] + // `manual_split_once` MSRV shouldn't apply to `needless_splitn` + let _ = "key=value".splitn(2, '=').nth(0).unwrap(); } diff --git a/tests/ui/needless_splitn.stderr b/tests/ui/needless_splitn.stderr index 66de2256554e3..f112b29e7f206 100644 --- a/tests/ui/needless_splitn.stderr +++ b/tests/ui/needless_splitn.stderr @@ -36,5 +36,47 @@ error: unnecessary use of `rsplitn` LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` -error: aborting due to 6 previous errors +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:28:13 + | +LL | let _ = str.splitn(5, '=').next(); + | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:29:13 + | +LL | let _ = str.splitn(5, '=').nth(3); + | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:35:13 + | +LL | let _ = s.splitn(2, '=').next()?; + | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:36:13 + | +LL | let _ = s.splitn(2, '=').nth(0)?; + | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` + +error: unnecessary use of `rsplitn` + --> $DIR/needless_splitn.rs:37:13 + | +LL | let _ = s.rsplitn(2, '=').next()?; + | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` + +error: unnecessary use of `rsplitn` + --> $DIR/needless_splitn.rs:38:13 + | +LL | let _ = s.rsplitn(2, '=').nth(0)?; + | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:46:13 + | +LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')` + +error: aborting due to 13 previous errors From a4d1837f07f65e4c440646799a0730f1d9a5f798 Mon Sep 17 00:00:00 2001 From: Yoav Lavi Date: Thu, 7 Apr 2022 20:21:47 +0200 Subject: [PATCH 025/129] unnecessary_string_new --- 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 + .../src/unnecessary_owned_empty_string.rs | 81 +++++++++++++++++++ clippy_utils/src/paths.rs | 1 + tests/ui/unnecessary_owned_empty_string.fixed | 22 +++++ tests/ui/unnecessary_owned_empty_string.rs | 22 +++++ .../ui/unnecessary_owned_empty_string.stderr | 16 ++++ 10 files changed, 148 insertions(+) create mode 100644 clippy_lints/src/unnecessary_owned_empty_string.rs create mode 100644 tests/ui/unnecessary_owned_empty_string.fixed create mode 100644 tests/ui/unnecessary_owned_empty_string.rs create mode 100644 tests/ui/unnecessary_owned_empty_string.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index b4097ea86a518..e48413dc30c5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3650,6 +3650,7 @@ Released 2018-09-13 [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation +[`unnecessary_owned_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_string [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 14ca93b5f3c14..b601e37a4c876 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -310,6 +310,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(unit_types::UNIT_CMP), LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING), LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 532590aaa5a3d..0a23e60fd2c4c 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -523,6 +523,7 @@ store.register_lints(&[ unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, + unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 3114afac8863e..b014c11f0f4a3 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -106,6 +106,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_unit::UNUSED_UNIT), LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c9b836f95808a..576a661126757 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -383,6 +383,7 @@ mod unit_hash; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; +mod unnecessary_owned_empty_string; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; @@ -868,6 +869,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); + store.register_late_pass(|| Box::new(unnecessary_owned_empty_string::UnnecessaryOwnedEmptyString)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unnecessary_owned_empty_string.rs b/clippy_lints/src/unnecessary_owned_empty_string.rs new file mode 100644 index 0000000000000..bb42c7816e7b2 --- /dev/null +++ b/clippy_lints/src/unnecessary_owned_empty_string.rs @@ -0,0 +1,81 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item}; +use clippy_utils::{match_def_path, paths}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Detects cases of owned empty strings being passed as an argument to a function expecting `&str` + /// + /// ### Why is this bad? + /// + /// This results in longer and less readable code + /// + /// ### Example + /// ```rust + /// vec!["1", "2", "3"].join(&String::new()); + /// ``` + /// Use instead: + /// ```rust + /// vec!["1", "2", "3"].join(""); + /// ``` + #[clippy::version = "1.62.0"] + pub UNNECESSARY_OWNED_EMPTY_STRING, + style, + "detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`" +} +declare_lint_pass!(UnnecessaryOwnedEmptyString => [UNNECESSARY_OWNED_EMPTY_STRING]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyString { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind; + if let ExprKind::Call(fun, args) = inner_expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); + if let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); + if inner_str.is_str(); + then { + if match_def_path(cx, fun_def_id, &paths::STRING_NEW) { + span_lint_and_sugg( + cx, + UNNECESSARY_OWNED_EMPTY_STRING, + expr.span, + "usage of `&String::new()` for a function expecting a `&str` argument", + "try", + "\"\"".to_owned(), + Applicability::MachineApplicable, + ); + } else { + if_chain! { + if match_def_path(cx, fun_def_id, &paths::FROM_FROM); + if let [.., last_arg] = args; + if let ExprKind::Lit(spanned) = &last_arg.kind; + if let LitKind::Str(symbol, _) = spanned.node; + if symbol.is_empty(); + let inner_expr_type = cx.typeck_results().expr_ty(inner_expr); + if is_type_diagnostic_item(cx, inner_expr_type, sym::String); + then { + span_lint_and_sugg( + cx, + UNNECESSARY_OWNED_EMPTY_STRING, + expr.span, + "usage of `&String::from(\"\")` for a function expecting a `&str` argument", + "try", + "\"\"".to_owned(), + Applicability::MachineApplicable, + ); + } + } + } + } + } + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 79e6e92dc0aaf..deb548daf2d0c 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -148,6 +148,7 @@ pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; +pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; 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"]; diff --git a/tests/ui/unnecessary_owned_empty_string.fixed b/tests/ui/unnecessary_owned_empty_string.fixed new file mode 100644 index 0000000000000..cd69e956fd4d3 --- /dev/null +++ b/tests/ui/unnecessary_owned_empty_string.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +#![warn(clippy::unnecessary_owned_empty_string)] + +fn ref_str_argument(_value: &str) {} + +#[allow(clippy::ptr_arg)] +fn ref_string_argument(_value: &String) {} + +fn main() { + // should be linted + ref_str_argument(""); + + // should be linted + ref_str_argument(""); + + // should not be linted + ref_str_argument(""); + + // should not be linted + ref_string_argument(&String::new()); +} diff --git a/tests/ui/unnecessary_owned_empty_string.rs b/tests/ui/unnecessary_owned_empty_string.rs new file mode 100644 index 0000000000000..3fbba156240c7 --- /dev/null +++ b/tests/ui/unnecessary_owned_empty_string.rs @@ -0,0 +1,22 @@ +// run-rustfix + +#![warn(clippy::unnecessary_owned_empty_string)] + +fn ref_str_argument(_value: &str) {} + +#[allow(clippy::ptr_arg)] +fn ref_string_argument(_value: &String) {} + +fn main() { + // should be linted + ref_str_argument(&String::new()); + + // should be linted + ref_str_argument(&String::from("")); + + // should not be linted + ref_str_argument(""); + + // should not be linted + ref_string_argument(&String::new()); +} diff --git a/tests/ui/unnecessary_owned_empty_string.stderr b/tests/ui/unnecessary_owned_empty_string.stderr new file mode 100644 index 0000000000000..c32372290d1e9 --- /dev/null +++ b/tests/ui/unnecessary_owned_empty_string.stderr @@ -0,0 +1,16 @@ +error: usage of `&String::new()` for a function expecting a `&str` argument + --> $DIR/unnecessary_owned_empty_string.rs:12:22 + | +LL | ref_str_argument(&String::new()); + | ^^^^^^^^^^^^^^ help: try: `""` + | + = note: `-D clippy::unnecessary-owned-empty-string` implied by `-D warnings` + +error: usage of `&String::from("")` for a function expecting a `&str` argument + --> $DIR/unnecessary_owned_empty_string.rs:15:22 + | +LL | ref_str_argument(&String::from("")); + | ^^^^^^^^^^^^^^^^^ help: try: `""` + +error: aborting due to 2 previous errors + From 10201370a1509b806b9e2a83ded6b132466291b8 Mon Sep 17 00:00:00 2001 From: Yoav Lavi Date: Mon, 11 Apr 2022 13:05:42 +0200 Subject: [PATCH 026/129] unnecessary_owned_empty_string -> unnecessary_owned_empty_strings --- CHANGELOG.md | 2 +- clippy_lints/src/lib.register_all.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_style.rs | 2 +- clippy_lints/src/lib.rs | 4 ++-- ...ty_string.rs => unnecessary_owned_empty_strings.rs} | 10 +++++----- ...ing.fixed => unnecessary_owned_empty_strings.fixed} | 2 +- ...ty_string.rs => unnecessary_owned_empty_strings.rs} | 2 +- ...g.stderr => unnecessary_owned_empty_strings.stderr} | 6 +++--- 9 files changed, 16 insertions(+), 16 deletions(-) rename clippy_lints/src/{unnecessary_owned_empty_string.rs => unnecessary_owned_empty_strings.rs} (90%) rename tests/ui/{unnecessary_owned_empty_string.fixed => unnecessary_owned_empty_strings.fixed} (87%) rename tests/ui/{unnecessary_owned_empty_string.rs => unnecessary_owned_empty_strings.rs} (88%) rename tests/ui/{unnecessary_owned_empty_string.stderr => unnecessary_owned_empty_strings.stderr} (69%) diff --git a/CHANGELOG.md b/CHANGELOG.md index e48413dc30c5d..44a36870108d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3650,7 +3650,7 @@ Released 2018-09-13 [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation -[`unnecessary_owned_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_string +[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index b601e37a4c876..02ba7835639e7 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -310,7 +310,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(unit_types::UNIT_CMP), LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING), + LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 0a23e60fd2c4c..704e79885cfb4 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -523,7 +523,7 @@ store.register_lints(&[ unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, - unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING, + unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index b014c11f0f4a3..f52fe97ed237c 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -106,7 +106,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING), + LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_unit::UNUSED_UNIT), LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 576a661126757..74ade422dc887 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -383,7 +383,7 @@ mod unit_hash; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; -mod unnecessary_owned_empty_string; +mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; @@ -869,7 +869,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); - store.register_late_pass(|| Box::new(unnecessary_owned_empty_string::UnnecessaryOwnedEmptyString)); + store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unnecessary_owned_empty_string.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs similarity index 90% rename from clippy_lints/src/unnecessary_owned_empty_string.rs rename to clippy_lints/src/unnecessary_owned_empty_strings.rs index bb42c7816e7b2..8a4f4c0ad9719 100644 --- a/clippy_lints/src/unnecessary_owned_empty_string.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -27,13 +27,13 @@ declare_clippy_lint! { /// vec!["1", "2", "3"].join(""); /// ``` #[clippy::version = "1.62.0"] - pub UNNECESSARY_OWNED_EMPTY_STRING, + pub UNNECESSARY_OWNED_EMPTY_STRINGS, style, "detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`" } -declare_lint_pass!(UnnecessaryOwnedEmptyString => [UNNECESSARY_OWNED_EMPTY_STRING]); +declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRINGS]); -impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyString { +impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind; @@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyString { if match_def_path(cx, fun_def_id, &paths::STRING_NEW) { span_lint_and_sugg( cx, - UNNECESSARY_OWNED_EMPTY_STRING, + UNNECESSARY_OWNED_EMPTY_STRINGS, expr.span, "usage of `&String::new()` for a function expecting a `&str` argument", "try", @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyString { then { span_lint_and_sugg( cx, - UNNECESSARY_OWNED_EMPTY_STRING, + UNNECESSARY_OWNED_EMPTY_STRINGS, expr.span, "usage of `&String::from(\"\")` for a function expecting a `&str` argument", "try", diff --git a/tests/ui/unnecessary_owned_empty_string.fixed b/tests/ui/unnecessary_owned_empty_strings.fixed similarity index 87% rename from tests/ui/unnecessary_owned_empty_string.fixed rename to tests/ui/unnecessary_owned_empty_strings.fixed index cd69e956fd4d3..f95f91329a2fa 100644 --- a/tests/ui/unnecessary_owned_empty_string.fixed +++ b/tests/ui/unnecessary_owned_empty_strings.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![warn(clippy::unnecessary_owned_empty_string)] +#![warn(clippy::unnecessary_owned_empty_strings)] fn ref_str_argument(_value: &str) {} diff --git a/tests/ui/unnecessary_owned_empty_string.rs b/tests/ui/unnecessary_owned_empty_strings.rs similarity index 88% rename from tests/ui/unnecessary_owned_empty_string.rs rename to tests/ui/unnecessary_owned_empty_strings.rs index 3fbba156240c7..0cbdc151ed9b1 100644 --- a/tests/ui/unnecessary_owned_empty_string.rs +++ b/tests/ui/unnecessary_owned_empty_strings.rs @@ -1,6 +1,6 @@ // run-rustfix -#![warn(clippy::unnecessary_owned_empty_string)] +#![warn(clippy::unnecessary_owned_empty_strings)] fn ref_str_argument(_value: &str) {} diff --git a/tests/ui/unnecessary_owned_empty_string.stderr b/tests/ui/unnecessary_owned_empty_strings.stderr similarity index 69% rename from tests/ui/unnecessary_owned_empty_string.stderr rename to tests/ui/unnecessary_owned_empty_strings.stderr index c32372290d1e9..46bc4597b335f 100644 --- a/tests/ui/unnecessary_owned_empty_string.stderr +++ b/tests/ui/unnecessary_owned_empty_strings.stderr @@ -1,13 +1,13 @@ error: usage of `&String::new()` for a function expecting a `&str` argument - --> $DIR/unnecessary_owned_empty_string.rs:12:22 + --> $DIR/unnecessary_owned_empty_strings.rs:12:22 | LL | ref_str_argument(&String::new()); | ^^^^^^^^^^^^^^ help: try: `""` | - = note: `-D clippy::unnecessary-owned-empty-string` implied by `-D warnings` + = note: `-D clippy::unnecessary-owned-empty-strings` implied by `-D warnings` error: usage of `&String::from("")` for a function expecting a `&str` argument - --> $DIR/unnecessary_owned_empty_string.rs:15:22 + --> $DIR/unnecessary_owned_empty_strings.rs:15:22 | LL | ref_str_argument(&String::from("")); | ^^^^^^^^^^^^^^^^^ help: try: `""` From 62d912e24dc1ec3816dfcddba96c792b660edc9b Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Tue, 5 Apr 2022 17:14:59 -0400 Subject: [PATCH 027/129] Add new `Deinit` statement kind --- clippy_utils/src/qualify_min_const_fn.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 891531951c1a0..fe41122048489 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -211,7 +211,8 @@ fn check_statement<'tcx>( StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body), // just an assignment - StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body), + StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => + check_place(tcx, **place, span, body), StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => { check_operand(tcx, dst, span, body)?; From c82dd0f36ebeba506d3722f9def3ec17ab03b528 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 11 Apr 2022 13:18:27 -0400 Subject: [PATCH 028/129] Fix ICE in `undocumented_unsafe_blocks` --- clippy_lints/src/undocumented_unsafe_blocks.rs | 7 ++++--- tests/ui/crashes/auxiliary/ice-8681-aux.rs | 6 ++++++ tests/ui/crashes/ice-8681.rs | 10 ++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 tests/ui/crashes/auxiliary/ice-8681-aux.rs create mode 100644 tests/ui/crashes/ice-8681.rs diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index c0947685fa1d5..bc57e9b992c61 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -156,8 +156,9 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> .array_windows::<2>() .rev() .map_while(|[start, end]| { - src.get(start.to_usize() - offset..end.to_usize() - offset) - .map(|text| (start.to_usize(), text.trim_start())) + let start = start.to_usize() - offset; + let end = end.to_usize() - offset; + src.get(start..end).map(|text| (start, text.trim_start())) }) .filter(|(_, text)| !text.is_empty()); @@ -182,7 +183,7 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> let (mut line_start, mut line) = (line_start, line); loop { if line.starts_with("/*") { - let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start(); + let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start(); let mut tokens = tokenize(src); return src[..tokens.next().unwrap().len] .to_ascii_uppercase() diff --git a/tests/ui/crashes/auxiliary/ice-8681-aux.rs b/tests/ui/crashes/auxiliary/ice-8681-aux.rs new file mode 100644 index 0000000000000..95b6315132526 --- /dev/null +++ b/tests/ui/crashes/auxiliary/ice-8681-aux.rs @@ -0,0 +1,6 @@ +pub fn foo(x: &u32) -> u32 { + /* Safety: + * This is totally ok. + */ + unsafe { *(x as *const u32) } +} diff --git a/tests/ui/crashes/ice-8681.rs b/tests/ui/crashes/ice-8681.rs new file mode 100644 index 0000000000000..ee14f011f631b --- /dev/null +++ b/tests/ui/crashes/ice-8681.rs @@ -0,0 +1,10 @@ +// aux-build: ice-8681-aux.rs + +#![warn(clippy::undocumented_unsafe_blocks)] + +#[path = "auxiliary/ice-8681-aux.rs"] +mod ice_8681_aux; + +fn main() { + let _ = ice_8681_aux::foo(&0u32); +} From 4a21082da22f6ff48a77f1ded2a6b5f02f31aa78 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 11 Apr 2022 18:54:44 +0100 Subject: [PATCH 029/129] Fix subtraction overflow in `cast_possible_truncation` --- .../src/casts/cast_possible_truncation.rs | 20 +++++++++---------- tests/ui/cast.rs | 14 ++++++++++--- tests/ui/cast.stderr | 14 ++++++++++++- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 421bd6f53f71b..64f87c80f8d14 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -29,21 +29,19 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)), ExprKind::Binary(op, left, right) => match op.node { BinOpKind::Div => { - apply_reductions(cx, nbits, left, signed) - - (if signed { - 0 // let's be conservative here - } else { - // by dividing by 1, we remove 0 bits, etc. - get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1)) - }) + apply_reductions(cx, nbits, left, signed).saturating_sub(if signed { + // let's be conservative here + 0 + } else { + // by dividing by 1, we remove 0 bits, etc. + get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1)) + }) }, BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right) .unwrap_or(u64::max_value()) .min(apply_reductions(cx, nbits, left, signed)), - BinOpKind::Shr => { - apply_reductions(cx, nbits, left, signed) - - constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high")) - }, + BinOpKind::Shr => apply_reductions(cx, nbits, left, signed) + .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))), _ => nbits, }, ExprKind::MethodCall(method, [left, right], _) => { diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index cf85a5ca931dd..e6031e9adaeb6 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -1,13 +1,13 @@ #![feature(repr128)] #![allow(incomplete_features)] - -#[warn( +#![warn( clippy::cast_precision_loss, clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_possible_wrap )] -#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)] + fn main() { // Test clippy::cast_precision_loss let x0 = 1i32; @@ -252,3 +252,11 @@ fn main() { } } } + +fn avoid_subtract_overflow(q: u32) { + let c = (q >> 16) as u8; + c as usize; + + let c = (q / 1000) as u8; + c as usize; +} diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 7a68c0984f140..0c63b4af30865 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -194,5 +194,17 @@ error: casting `main::E10` to `u16` may truncate the value LL | let _ = self as u16; | ^^^^^^^^^^^ -error: aborting due to 31 previous errors +error: casting `u32` to `u8` may truncate the value + --> $DIR/cast.rs:257:13 + | +LL | let c = (q >> 16) as u8; + | ^^^^^^^^^^^^^^^ + +error: casting `u32` to `u8` may truncate the value + --> $DIR/cast.rs:260:13 + | +LL | let c = (q / 1000) as u8; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 33 previous errors From 9716a9eff0d68c92fd064b293f0ffbd083645fa5 Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Tue, 12 Apr 2022 04:03:48 +0900 Subject: [PATCH 030/129] adding condition for map_clone message if msrv < 1.36, the message tells , but the suggestion is --- clippy_lints/src/map_clone.rs | 2 +- tests/ui-toml/min_rust_version/min_rust_version.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index e233300e26ab8..a03d5d540653d 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -143,7 +143,7 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { impl MapClone { fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { let mut applicability = Applicability::MachineApplicable; - let message = if is_copy { + let message = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { "you are using an explicit closure for copying elements" } else { "you are using an explicit closure for cloning elements" diff --git a/tests/ui-toml/min_rust_version/min_rust_version.stderr b/tests/ui-toml/min_rust_version/min_rust_version.stderr index a1e7361c0cb32..5dae5af7eb5a9 100644 --- a/tests/ui-toml/min_rust_version/min_rust_version.stderr +++ b/tests/ui-toml/min_rust_version/min_rust_version.stderr @@ -1,4 +1,4 @@ -error: you are using an explicit closure for copying elements +error: you are using an explicit closure for cloning elements --> $DIR/min_rust_version.rs:74:26 | LL | let _: Option = Some(&16).map(|b| *b); From 40224f46c0158ff8bf6657f2272dba9ec2ee96d7 Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Tue, 12 Apr 2022 09:49:00 +0900 Subject: [PATCH 031/129] refactor: Put together an if statement --- clippy_lints/src/map_clone.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index a03d5d540653d..ceb66947d02c6 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -143,15 +143,11 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { impl MapClone { fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { let mut applicability = Applicability::MachineApplicable; - let message = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { - "you are using an explicit closure for copying elements" - } else { - "you are using an explicit closure for cloning elements" - }; - let sugg_method = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { - "copied" + + let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { + ("you are using an explicit closure for copying elements", "copied") } else { - "cloned" + ("you are using an explicit closure for cloning elements", "cloned") }; span_lint_and_sugg( From 739f273739a1e4da98d1222f3316d831ae187bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20A=2E=20Muci=C3=B1o?= Date: Mon, 11 Apr 2022 22:47:04 -0500 Subject: [PATCH 032/129] Do not apply `rest_pat_in_fully_bound_structs` on `#[non_exhaustive]` structs --- .../src/matches/rest_pat_in_fully_bound_struct.rs | 1 + tests/ui/rest_pat_in_fully_bound_structs.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index 5076239a57c4d..0aadb482acdda 100644 --- a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -14,6 +14,7 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { if let ty::Adt(def, _) = ty.kind(); if def.is_struct() || def.is_union(); if fields.len() == def.non_enum_variant().fields.len(); + if !def.non_enum_variant().is_field_list_non_exhaustive(); then { span_lint_and_help( diff --git a/tests/ui/rest_pat_in_fully_bound_structs.rs b/tests/ui/rest_pat_in_fully_bound_structs.rs index 38fc9969804fa..086331af6b567 100644 --- a/tests/ui/rest_pat_in_fully_bound_structs.rs +++ b/tests/ui/rest_pat_in_fully_bound_structs.rs @@ -39,4 +39,19 @@ fn main() { // No lint foo!(a_struct); + + #[non_exhaustive] + struct B { + a: u32, + b: u32, + c: u64, + } + + let b_struct = B { a: 5, b: 42, c: 342 }; + + match b_struct { + B { a: 5, b: 42, .. } => {}, + B { a: 0, b: 0, c: 128, .. } => {}, // No Lint + _ => {}, + } } From 214fba7ed4a8e2199d42733cc191b956f0c2b017 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 12 Apr 2022 15:27:28 +0200 Subject: [PATCH 033/129] Prevent infinite (exponential) recursion in only_used_in_recursion This simplifies the visitor code a bit and prevents checking expressions multiple times. I still think this lint should be removed for now, because its code isn't really tested. --- clippy_lints/src/only_used_in_recursion.rs | 47 ++++++++-------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 8e61f2347767d..f946fc1119281 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -8,7 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; +use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor}; use rustc_hir::{ Arm, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp, @@ -145,7 +145,8 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { is_method: matches!(kind, FnKind::Method(..)), has_self, ty_res, - ty_ctx: cx.tcx, + tcx: cx.tcx, + visited_exprs: FxHashSet::default(), }; visitor.visit_expr(&body.value); @@ -206,19 +207,13 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { } pub fn is_primitive(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, - ty::Ref(_, t, _) => is_primitive(*t), - _ => false, - } + let ty = ty.peel_refs(); + ty.is_primitive() || ty.is_str() } pub fn is_array(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Array(..) | ty::Slice(..) => true, - ty::Ref(_, t, _) => is_array(*t), - _ => false, - } + let ty = ty.peel_refs(); + ty.is_array() || ty.is_array_slice() } /// This builds the graph of side effect. @@ -250,40 +245,30 @@ pub struct SideEffectVisit<'tcx> { is_method: bool, has_self: bool, ty_res: &'tcx TypeckResults<'tcx>, - ty_ctx: TyCtxt<'tcx>, + tcx: TyCtxt<'tcx>, + visited_exprs: FxHashSet, } impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> { - fn visit_block(&mut self, b: &'tcx Block<'tcx>) { - b.stmts.iter().for_each(|stmt| { - self.visit_stmt(stmt); - self.ret_vars.clear(); - }); - walk_list!(self, visit_expr, b.expr); - } - fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) { match s.kind { StmtKind::Local(Local { pat, init: Some(init), .. }) => { self.visit_pat_expr(pat, init, false); - self.ret_vars.clear(); }, - StmtKind::Item(i) => { - let item = self.ty_ctx.hir().item(i); - self.visit_item(item); - self.ret_vars.clear(); - }, - StmtKind::Expr(e) | StmtKind::Semi(e) => { - self.visit_expr(e); - self.ret_vars.clear(); + StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => { + walk_stmt(self, s); }, StmtKind::Local(_) => {}, } + self.ret_vars.clear(); } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if !self.visited_exprs.insert(ex.hir_id) { + return; + } match ex.kind { ExprKind::Array(exprs) | ExprKind::Tup(exprs) => { self.ret_vars = exprs @@ -307,7 +292,7 @@ impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> { ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms), // since analysing the closure is not easy, just set all variables in it to side-effect ExprKind::Closure(_, _, body_id, _, _) => { - let body = self.ty_ctx.hir().body(body_id); + let body = self.tcx.hir().body(body_id); self.visit_body(body); let vars = std::mem::take(&mut self.ret_vars); self.add_side_effect(vars); From dfdc5ad7d8f5d3787406a00bab2d1e049a57d1e2 Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Tue, 12 Apr 2022 22:55:48 +0900 Subject: [PATCH 034/129] fix unnecessary_to_owned about msrv --- clippy_lints/src/methods/mod.rs | 2 +- .../src/methods/unnecessary_to_owned.rs | 23 ++- tests/ui/unnecessary_to_owned.fixed | 12 ++ tests/ui/unnecessary_to_owned.rs | 12 ++ tests/ui/unnecessary_to_owned.stderr | 176 ++++++++++-------- 5 files changed, 138 insertions(+), 87 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 56a8782cee427..e2abce86791b6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2254,7 +2254,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_add_str::check(cx, expr, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args); single_char_pattern::check(cx, expr, method_call.ident.name, args); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, args); + unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref()); }, hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 1555758fc4ad8..02b882e8b55e0 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -5,6 +5,8 @@ use clippy_utils::source::snippet_opt; use clippy_utils::ty::{ contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs, }; +use clippy_utils::{meets_msrv, msrvs}; + use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind}; @@ -13,12 +15,19 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; +use rustc_semver::RustcVersion; use rustc_span::{sym, Symbol}; use std::cmp::max; use super::UNNECESSARY_TO_OWNED; -pub fn check<'tcx>(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>], + msrv: Option<&RustcVersion>, +) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let [receiver] = args; @@ -33,7 +42,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { return; } - if check_into_iter_call_arg(cx, expr, method_name, receiver) { + if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { return; } check_other_call_arg(cx, expr, method_name, receiver); @@ -178,7 +187,13 @@ 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<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { +fn check_into_iter_call_arg( + cx: &LateContext<'_>, + expr: &Expr<'_>, + method_name: Symbol, + receiver: &Expr<'_>, + msrv: Option<&RustcVersion>, +) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); if let Some(callee_def_id) = fn_def_id(cx, parent); @@ -192,7 +207,7 @@ fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: 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) { "copied" } else { "cloned" }; + let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { "copied" } 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. diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 38ba41ac54ecb..7455e22d49b21 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -2,6 +2,7 @@ #![allow(clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] +#![feature(custom_inner_attributes)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; @@ -213,6 +214,17 @@ fn get_file_path(_file_type: &FileType) -> Result Result $DIR/unnecessary_to_owned.rs:150:64 + --> $DIR/unnecessary_to_owned.rs:151:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | = note: `-D clippy::redundant-clone` implied by `-D warnings` note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:150:20 + --> $DIR/unnecessary_to_owned.rs:151:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:151:40 + --> $DIR/unnecessary_to_owned.rs:152:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:151:21 + --> $DIR/unnecessary_to_owned.rs:152:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:152:48 + --> $DIR/unnecessary_to_owned.rs:153:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:152:19 + --> $DIR/unnecessary_to_owned.rs:153:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:153:35 + --> $DIR/unnecessary_to_owned.rs:154:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:153:18 + --> $DIR/unnecessary_to_owned.rs:154:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:59:36 + --> $DIR/unnecessary_to_owned.rs:60:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -56,427 +56,427 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:60:19 + --> $DIR/unnecessary_to_owned.rs:61:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> $DIR/unnecessary_to_owned.rs:62:20 + --> $DIR/unnecessary_to_owned.rs:63:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:63:38 + --> $DIR/unnecessary_to_owned.rs:64:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:64:20 + --> $DIR/unnecessary_to_owned.rs:65:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> $DIR/unnecessary_to_owned.rs:66:18 + --> $DIR/unnecessary_to_owned.rs:67:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:67:34 + --> $DIR/unnecessary_to_owned.rs:68:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:68:18 + --> $DIR/unnecessary_to_owned.rs:69:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:70:17 + --> $DIR/unnecessary_to_owned.rs:71:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:71:30 + --> $DIR/unnecessary_to_owned.rs:72:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:72:17 + --> $DIR/unnecessary_to_owned.rs:73:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:73:17 + --> $DIR/unnecessary_to_owned.rs:74:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:75:19 + --> $DIR/unnecessary_to_owned.rs:76:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:76:36 + --> $DIR/unnecessary_to_owned.rs:77:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:77:19 + --> $DIR/unnecessary_to_owned.rs:78:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:78:19 + --> $DIR/unnecessary_to_owned.rs:79:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:79:19 + --> $DIR/unnecessary_to_owned.rs:80:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:80:19 + --> $DIR/unnecessary_to_owned.rs:81:19 | LL | require_slice(&x_ref.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:82:42 + --> $DIR/unnecessary_to_owned.rs:83:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:83:15 + --> $DIR/unnecessary_to_owned.rs:84:15 | LL | require_x(&x_ref.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:85:25 + --> $DIR/unnecessary_to_owned.rs:86:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:86:26 + --> $DIR/unnecessary_to_owned.rs:87:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:87:24 + --> $DIR/unnecessary_to_owned.rs:88:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:88:23 + --> $DIR/unnecessary_to_owned.rs:89:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:89:25 + --> $DIR/unnecessary_to_owned.rs:90:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:91:30 + --> $DIR/unnecessary_to_owned.rs:92:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:92:31 + --> $DIR/unnecessary_to_owned.rs:93:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:93:29 + --> $DIR/unnecessary_to_owned.rs:94:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:94:28 + --> $DIR/unnecessary_to_owned.rs:95:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:95:30 + --> $DIR/unnecessary_to_owned.rs:96:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:97:29 + --> $DIR/unnecessary_to_owned.rs:98:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:97:43 + --> $DIR/unnecessary_to_owned.rs:98:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:29 + --> $DIR/unnecessary_to_owned.rs:99:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:47 + --> $DIR/unnecessary_to_owned.rs:99:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:100:26 + --> $DIR/unnecessary_to_owned.rs:101:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:101:27 + --> $DIR/unnecessary_to_owned.rs:102:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:102:25 + --> $DIR/unnecessary_to_owned.rs:103:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:103:24 + --> $DIR/unnecessary_to_owned.rs:104:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:104:24 + --> $DIR/unnecessary_to_owned.rs:105:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:105:26 + --> $DIR/unnecessary_to_owned.rs:106:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:106:26 + --> $DIR/unnecessary_to_owned.rs:107:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:107:26 + --> $DIR/unnecessary_to_owned.rs:108:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:109:31 + --> $DIR/unnecessary_to_owned.rs:110:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:110:32 + --> $DIR/unnecessary_to_owned.rs:111:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:111:30 + --> $DIR/unnecessary_to_owned.rs:112:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:112:29 + --> $DIR/unnecessary_to_owned.rs:113:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:113:29 + --> $DIR/unnecessary_to_owned.rs:114:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:114:31 + --> $DIR/unnecessary_to_owned.rs:115:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:115:31 + --> $DIR/unnecessary_to_owned.rs:116:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:116:31 + --> $DIR/unnecessary_to_owned.rs:117:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:118:30 + --> $DIR/unnecessary_to_owned.rs:119:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:118:44 + --> $DIR/unnecessary_to_owned.rs:119:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:30 + --> $DIR/unnecessary_to_owned.rs:120:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:44 + --> $DIR/unnecessary_to_owned.rs:120:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:30 + --> $DIR/unnecessary_to_owned.rs:121:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:44 + --> $DIR/unnecessary_to_owned.rs:121:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:30 + --> $DIR/unnecessary_to_owned.rs:122:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:48 + --> $DIR/unnecessary_to_owned.rs:122:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:30 + --> $DIR/unnecessary_to_owned.rs:123:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:52 + --> $DIR/unnecessary_to_owned.rs:123:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:30 + --> $DIR/unnecessary_to_owned.rs:124:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:48 + --> $DIR/unnecessary_to_owned.rs:124:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:125:20 + --> $DIR/unnecessary_to_owned.rs:126:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:127:13 + --> $DIR/unnecessary_to_owned.rs:128:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:128:13 + --> $DIR/unnecessary_to_owned.rs:129:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:129:13 + --> $DIR/unnecessary_to_owned.rs:130:13 | LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:130:13 + --> $DIR/unnecessary_to_owned.rs:131:13 | LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:132:13 + --> $DIR/unnecessary_to_owned.rs:133:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:133:13 + --> $DIR/unnecessary_to_owned.rs:134:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:134:13 + --> $DIR/unnecessary_to_owned.rs:135:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:135:13 + --> $DIR/unnecessary_to_owned.rs:136:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:196:14 + --> $DIR/unnecessary_to_owned.rs:197:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -491,11 +491,23 @@ LL - let path = match get_file_path(&t) { LL + let path = match get_file_path(t) { | +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:220:14 + | +LL | let _ = &["x"][..].to_vec().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` + +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:225:14 + | +LL | let _ = &["x"][..].to_vec().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` + error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:260:24 + --> $DIR/unnecessary_to_owned.rs:272:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` -error: aborting due to 77 previous errors +error: aborting due to 79 previous errors From 5b7df246cffdfeeadb95412a0143442a36c08496 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 12 Apr 2022 09:34:40 +0100 Subject: [PATCH 035/129] errors: lazily load fallback fluent bundle Loading the fallback bundle in compilation sessions that won't go on to emit any errors unnecessarily degrades compile time performance, so lazily create the Fluent bundle when it is first required. Signed-off-by: David Wood --- clippy_lints/src/doc.rs | 6 ++++-- src/driver.rs | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 28d0c75fde6ba..503cef76775e4 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -621,8 +621,10 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = - rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle"); + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_errors::DEFAULT_LOCALE_RESOURCES, + false + ); let emitter = EmitterWriter::new( Box::new(io::sink()), None, diff --git a/src/driver.rs b/src/driver.rs index 00dc916b217ca..32a09fdb9d9ff 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -165,7 +165,8 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); - let fallback_bundle = rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle"); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, From 66d253f0f219578264ac7cb4e1c10f01dcfe8ad7 Mon Sep 17 00:00:00 2001 From: Yoav Lavi Date: Tue, 12 Apr 2022 11:09:34 +0200 Subject: [PATCH 036/129] pub_use --- 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 + clippy_lints/src/pub_use.rs | 56 ++++++++++++++++++++ tests/ui/pub_use.rs | 14 +++++ tests/ui/pub_use.stderr | 11 ++++ 7 files changed, 86 insertions(+) create mode 100644 clippy_lints/src/pub_use.rs create mode 100644 tests/ui/pub_use.rs create mode 100644 tests/ui/pub_use.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 44a36870108d5..444c649c84f01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3527,6 +3527,7 @@ Released 2018-09-13 [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names +[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use [`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark [`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one [`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 704e79885cfb4..4132802c9c6e6 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -432,6 +432,7 @@ store.register_lints(&[ ptr::PTR_ARG, ptr_eq::PTR_EQ, ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, + pub_use::PUB_USE, question_mark::QUESTION_MARK, ranges::MANUAL_RANGE_CONTAINS, ranges::RANGE_MINUS_ONE, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 4802dd877e99d..555a8ad841fec 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -53,6 +53,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(panic_unimplemented::UNIMPLEMENTED), LintId::of(panic_unimplemented::UNREACHABLE), LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), + LintId::of(pub_use::PUB_USE), LintId::of(redundant_slicing::DEREF_BY_SLICING), LintId::of(same_name_method::SAME_NAME_METHOD), LintId::of(shadow::SHADOW_REUSE), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 74ade422dc887..17552873b0a09 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -336,6 +336,7 @@ mod precedence; mod ptr; mod ptr_eq; mod ptr_offset_with_cast; +mod pub_use; mod question_mark; mod ranges; mod redundant_clone; @@ -870,6 +871,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); + store.register_early_pass(|| Box::new(pub_use::PubUse)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/pub_use.rs b/clippy_lints/src/pub_use.rs new file mode 100644 index 0000000000000..9d2b0cedb60a1 --- /dev/null +++ b/clippy_lints/src/pub_use.rs @@ -0,0 +1,56 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// + /// Restricts the usage of `pub use ...` + /// + /// ### Why is this bad? + /// + /// `pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent + /// unintentional exports or to encourage placing exported items directly in public modules + /// + /// ### Example + /// ```rust + /// pub mod outer { + /// mod inner { + /// pub struct Test {} + /// } + /// pub use inner::Test; + /// } + /// + /// use outer::Test; + /// ``` + /// Use instead: + /// ```rust + /// pub mod outer { + /// pub struct Test {} + /// } + /// + /// use outer::Test; + /// ``` + #[clippy::version = "1.62.0"] + pub PUB_USE, + restriction, + "restricts the usage of `pub use`" +} +declare_lint_pass!(PubUse => [PUB_USE]); + +impl EarlyLintPass for PubUse { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if let ItemKind::Use(_) = item.kind && + let VisibilityKind::Public = item.vis.kind { + span_lint_and_help( + cx, + PUB_USE, + item.span, + "using `pub use`", + None, + "move the exported item to a public module instead", + ); + } + } +} diff --git a/tests/ui/pub_use.rs b/tests/ui/pub_use.rs new file mode 100644 index 0000000000000..65542bedec7bc --- /dev/null +++ b/tests/ui/pub_use.rs @@ -0,0 +1,14 @@ +#![warn(clippy::pub_use)] +#![allow(unused_imports)] +#![no_main] + +pub mod outer { + mod inner { + pub struct Test {} + } + // should be linted + pub use inner::Test; +} + +// should not be linted +use std::fmt; diff --git a/tests/ui/pub_use.stderr b/tests/ui/pub_use.stderr new file mode 100644 index 0000000000000..9ab710df818ca --- /dev/null +++ b/tests/ui/pub_use.stderr @@ -0,0 +1,11 @@ +error: using `pub use` + --> $DIR/pub_use.rs:10:5 + | +LL | pub use inner::Test; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::pub-use` implied by `-D warnings` + = help: move the exported item to a public module instead + +error: aborting due to previous error + From ba15cdd3c159b5feda929b7af0d929e17a6f5c6d Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 10 Apr 2022 19:03:16 +0200 Subject: [PATCH 037/129] Add `usize` cast to `clippy::manual_bits` suggestion --- clippy_lints/src/manual_bits.rs | 46 ++++++++++-- tests/ui/manual_bits.fixed | 69 +++++++++-------- tests/ui/manual_bits.rs | 13 +++- tests/ui/manual_bits.stderr | 126 +++++++++++++++++++------------- 4 files changed, 168 insertions(+), 86 deletions(-) diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index dcf44303cf449..44ff151ea5269 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_parent_expr, meets_msrv, msrvs}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; @@ -59,16 +59,19 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { 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 { + let mut app = Applicability::MachineApplicable; + let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app); + let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); + 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, + sugg, + app, ); } } @@ -108,3 +111,36 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option< } } } + +fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String { + if let Some(parent_expr) = get_parent_expr(cx, expr) { + if is_ty_conversion(parent_expr) { + return base_sugg; + } + + // These expressions have precedence over casts, the suggestion therefore + // needs to be wrapped into parentheses + match parent_expr.kind { + ExprKind::Unary(..) | ExprKind::AddrOf(..) | ExprKind::MethodCall(..) => { + return format!("({base_sugg} as usize)"); + }, + _ => {}, + } + } + + format!("{base_sugg} as usize") +} + +fn is_ty_conversion(expr: &Expr<'_>) -> bool { + if let ExprKind::Cast(..) = expr.kind { + true + } else if let ExprKind::MethodCall(path, [_], _) = expr.kind + && path.ident.name == rustc_span::sym::try_into + { + // This is only called for `usize` which implements `TryInto`. Therefore, + // we don't have to check here if `self` implements the `TryInto` trait. + true + } else { + false + } +} diff --git a/tests/ui/manual_bits.fixed b/tests/ui/manual_bits.fixed index 4f1b19b75b8a1..386360dbdcdb8 100644 --- a/tests/ui/manual_bits.fixed +++ b/tests/ui/manual_bits.fixed @@ -1,38 +1,44 @@ // run-rustfix #![warn(clippy::manual_bits)] -#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] +#![allow( + clippy::no_effect, + clippy::useless_conversion, + 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; + i8::BITS as usize; + i16::BITS as usize; + i32::BITS as usize; + i64::BITS as usize; + i128::BITS as usize; + isize::BITS as usize; + + u8::BITS as usize; + u16::BITS as usize; + u32::BITS as usize; + u64::BITS as usize; + u128::BITS as usize; + usize::BITS as usize; + + i8::BITS as usize; + i16::BITS as usize; + i32::BITS as usize; + i64::BITS as usize; + i128::BITS as usize; + isize::BITS as usize; + + u8::BITS as usize; + u16::BITS as usize; + u32::BITS as usize; + u64::BITS as usize; + u128::BITS as usize; + usize::BITS as usize; size_of::() * 4; 4 * size_of::(); @@ -42,7 +48,12 @@ fn main() { size_of_val(&0u32) * 8; type Word = u32; - Word::BITS; + Word::BITS as usize; type Bool = bool; size_of::() * 8; + + let _: u32 = u128::BITS as u32; + let _: u32 = u128::BITS.try_into().unwrap(); + let _ = (u128::BITS as usize).pow(5); + let _ = &(u128::BITS as usize); } diff --git a/tests/ui/manual_bits.rs b/tests/ui/manual_bits.rs index f8a01313e6ad0..62638f047eb01 100644 --- a/tests/ui/manual_bits.rs +++ b/tests/ui/manual_bits.rs @@ -1,7 +1,13 @@ // run-rustfix #![warn(clippy::manual_bits)] -#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] +#![allow( + clippy::no_effect, + clippy::useless_conversion, + path_statements, + unused_must_use, + clippy::unnecessary_operation +)] use std::mem::{size_of, size_of_val}; @@ -45,4 +51,9 @@ fn main() { size_of::() * 8; type Bool = bool; size_of::() * 8; + + let _: u32 = (size_of::() * 8) as u32; + let _: u32 = (size_of::() * 8).try_into().unwrap(); + let _ = (size_of::() * 8).pow(5); + let _ = &(size_of::() * 8); } diff --git a/tests/ui/manual_bits.stderr b/tests/ui/manual_bits.stderr index c4f5af2dcb0ec..69c591a203d3f 100644 --- a/tests/ui/manual_bits.stderr +++ b/tests/ui/manual_bits.stderr @@ -1,154 +1,178 @@ error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:9:5 + --> $DIR/manual_bits.rs:15:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` | = 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 + --> $DIR/manual_bits.rs:16:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:11:5 + --> $DIR/manual_bits.rs:17:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:12:5 + --> $DIR/manual_bits.rs:18:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:13:5 + --> $DIR/manual_bits.rs:19:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:14:5 + --> $DIR/manual_bits.rs:20:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:16:5 + --> $DIR/manual_bits.rs:22:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:17:5 + --> $DIR/manual_bits.rs:23:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:18:5 + --> $DIR/manual_bits.rs:24:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:19:5 + --> $DIR/manual_bits.rs:25:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:20:5 + --> $DIR/manual_bits.rs:26:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:21:5 + --> $DIR/manual_bits.rs:27:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:23:5 + --> $DIR/manual_bits.rs:29:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:24:5 + --> $DIR/manual_bits.rs:30:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:25:5 + --> $DIR/manual_bits.rs:31:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:26:5 + --> $DIR/manual_bits.rs:32:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:27:5 + --> $DIR/manual_bits.rs:33:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:28:5 + --> $DIR/manual_bits.rs:34:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:30:5 + --> $DIR/manual_bits.rs:36:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:31:5 + --> $DIR/manual_bits.rs:37:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:32:5 + --> $DIR/manual_bits.rs:38:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:33:5 + --> $DIR/manual_bits.rs:39:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:34:5 + --> $DIR/manual_bits.rs:40:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:35:5 + --> $DIR/manual_bits.rs:41:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:45:5 + --> $DIR/manual_bits.rs:51:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:55:18 + | +LL | let _: u32 = (size_of::() * 8) as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:56:18 + | +LL | let _: u32 = (size_of::() * 8).try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:57:13 + | +LL | let _ = (size_of::() * 8).pow(5); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:58:14 + | +LL | let _ = &(size_of::() * 8); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` -error: aborting due to 25 previous errors +error: aborting due to 29 previous errors From 9f131e5a0bbaa2d4a9cd8cd2524695725680ceb4 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Thu, 31 Mar 2022 22:28:59 -0600 Subject: [PATCH 038/129] assertions_on_constants: ignore indirect `cfg!` --- clippy_utils/src/consts.rs | 18 +++++++++++++++++- tests/ui/assertions_on_constants.rs | 10 ++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 1d6f7acab139b..0e916ca81642e 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -5,7 +5,7 @@ use if_chain::if_chain; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp}; +use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::mir::interpret::Scalar; use rustc_middle::ty::subst::{Subst, SubstsRef}; @@ -400,6 +400,22 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let res = self.typeck_results.qpath_res(qpath, id); match res { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { + // Check if this constant is based on `cfg!(..)`, + // which is NOT constant for our purposes. + if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) && + let Node::Item(&Item { + kind: ItemKind::Const(_, body_id), + .. + }) = node && + let Node::Expr(&Expr { + kind: ExprKind::Lit(_), + span, + .. + }) = self.lcx.tcx.hir().get(body_id.hir_id) && + is_direct_expn_of(span, "cfg").is_some() { + return None; + } + let substs = self.typeck_results.node_substs(id); let substs = if self.substs.is_empty() { substs diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs index 7477c01ca7828..7bea9563d47d3 100644 --- a/tests/ui/assertions_on_constants.rs +++ b/tests/ui/assertions_on_constants.rs @@ -1,4 +1,4 @@ -#![allow(non_fmt_panics)] +#![allow(non_fmt_panics, clippy::needless_bool)] macro_rules! assert_const { ($len:expr) => { @@ -28,6 +28,12 @@ fn main() { assert_const!(3); assert_const!(-1); - // Don't lint on this: + // Don't lint if based on `cfg!(..)`: assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); + + let flag: bool = cfg!(not(feature = "asdf")); + assert!(flag); + + const CFG_FLAG: &bool = &cfg!(feature = "hey"); + assert!(!CFG_FLAG); } From 67badbeef6ce5452cc47f2463d7146048ce64625 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Sun, 3 Apr 2022 21:40:58 -0600 Subject: [PATCH 039/129] New lint `format_add_strings` --- CHANGELOG.md | 1 + clippy_dev/src/new_lint.rs | 6 +- clippy_dev/src/update_lints.rs | 16 ++-- clippy_lints/src/format_push_string.rs | 77 +++++++++++++++++++ .../src/inconsistent_struct_constructor.rs | 3 +- clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_perf.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/trait_bounds.rs | 9 ++- clippy_utils/src/sugg.rs | 10 +-- lintcheck/src/main.rs | 15 ++-- tests/ui/format_push_string.rs | 7 ++ tests/ui/format_push_string.stderr | 19 +++++ tests/ui/identity_op.rs | 4 +- tests/ui/identity_op.stderr | 36 ++++----- 16 files changed, 163 insertions(+), 45 deletions(-) create mode 100644 clippy_lints/src/format_push_string.rs create mode 100644 tests/ui/format_push_string.rs create mode 100644 tests/ui/format_push_string.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index ef21dbac820e7..39786c08156dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3314,6 +3314,7 @@ Released 2018-09-13 [`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args +[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into [`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10 diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 7a3fd1317619e..10f67d301f887 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,5 +1,6 @@ use crate::clippy_project_root; use indoc::indoc; +use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::io::{self, ErrorKind}; @@ -232,7 +233,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { ) }); - result.push_str(&format!( + let _ = write!( + result, indoc! {r#" declare_clippy_lint! {{ /// ### What it does @@ -256,7 +258,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { version = version, name_upper = name_upper, category = category, - )); + ); result.push_str(&if enable_msrv { format!( diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 59db51fbfac51..d25e42f2468bc 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -217,12 +217,13 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator String { let mut output = GENERATED_FILE_COMMENT.to_string(); output.push_str("{\n"); for lint in lints { - output.push_str(&format!( + let _ = write!( + output, concat!( " store.register_removed(\n", " \"clippy::{}\",\n", @@ -243,7 +245,7 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String { " );\n" ), lint.name, lint.reason, - )); + ); } output.push_str("}\n"); @@ -269,7 +271,7 @@ fn gen_register_lint_list<'a>( if !is_public { output.push_str(" #[cfg(feature = \"internal\")]\n"); } - output.push_str(&format!(" {}::{},\n", module_name, lint_name)); + let _ = writeln!(output, " {}::{},", module_name, lint_name); } output.push_str("])\n"); diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs new file mode 100644 index 0000000000000..ee15ae9f59aca --- /dev/null +++ b/clippy_lints/src/format_push_string.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_def_path, paths, peel_hir_expr_refs}; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects cases where the result of a `format!` call is + /// appended to an existing `String`. + /// + /// ### Why is this bad? + /// Introduces an extra, avoidable heap allocation. + /// + /// ### Example + /// ```rust + /// let mut s = String::new(); + /// s += &format!("0x{:X}", 1024); + /// s.push_str(&format!("0x{:X}", 1024)); + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt::Write as _; // import without risk of name clashing + /// + /// let mut s = String::new(); + /// let _ = write!(s, "0x{:X}", 1024); + /// ``` + #[clippy::version = "1.61.0"] + pub FORMAT_PUSH_STRING, + perf, + "`format!(..)` appended to existing `String`" +} +declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); + +fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String) +} +fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id { + cx.tcx.get_diagnostic_name(macro_def_id) == Some(sym::format_macro) + } else { + false + } +} + +impl<'tcx> LateLintPass<'tcx> for FormatPushString { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let arg = match expr.kind { + ExprKind::MethodCall(_, [_, arg], _) => { + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && + match_def_path(cx, fn_def_id, &paths::PUSH_STR) { + arg + } else { + return; + } + } + ExprKind::AssignOp(op, left, arg) + if op.node == BinOpKind::Add && is_string(cx, left) => { + arg + }, + _ => return, + }; + let (arg, _) = peel_hir_expr_refs(arg); + if is_format(cx, arg) { + span_lint_and_help( + cx, + FORMAT_PUSH_STRING, + expr.span, + "`format!(..)` appended to existing `String`", + None, + "consider using `write!` to avoid the extra allocation", + ); + } + } +} diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index c8ec2f4513707..14b22d2b50d05 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -7,6 +7,7 @@ use rustc_hir::{self as hir, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; +use std::fmt::Write as _; declare_clippy_lint! { /// ### What it does @@ -89,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { let mut fields_snippet = String::new(); let (last_ident, idents) = ordered_fields.split_last().unwrap(); for ident in idents { - fields_snippet.push_str(&format!("{}, ", ident)); + let _ = write!(fields_snippet, "{}, ", ident); } fields_snippet.push_str(&last_ident.to_string()); diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 52c440de9dd43..2fa5973336596 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -77,6 +77,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), + LintId::of(format_push_string::FORMAT_PUSH_STRING), LintId::of(formatting::POSSIBLE_MISSING_COMMA), LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 9820b7727af14..c608f634291f8 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -165,6 +165,7 @@ store.register_lints(&[ format_args::TO_STRING_IN_FORMAT_ARGS, format_impl::PRINT_IN_FORMAT_IMPL, format_impl::RECURSIVE_FORMAT_IMPL, + format_push_string::FORMAT_PUSH_STRING, formatting::POSSIBLE_MISSING_COMMA, formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, formatting::SUSPICIOUS_ELSE_FORMATTING, diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs index f2f5c988d8f90..8f361fdad4ac4 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/clippy_lints/src/lib.register_perf.rs @@ -7,6 +7,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(escape::BOXED_LOCAL), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), + LintId::of(format_push_string::FORMAT_PUSH_STRING), LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(loops::MANUAL_MEMCPY), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 604bd44c6c59a..88e8a0cc2af04 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -231,6 +231,7 @@ mod floating_point_arithmetic; mod format; mod format_args; mod format_impl; +mod format_push_string; mod formatting; mod from_over_into; mod from_str_radix_10; @@ -873,6 +874,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_early_pass(|| Box::new(pub_use::PubUse)); + store.register_late_pass(|| Box::new(format_push_string::FormatPushString)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 43e0132a7ec7b..1551d0ecb7402 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -14,6 +14,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; +use std::fmt::Write as _; declare_clippy_lint! { /// ### What it does @@ -184,19 +185,19 @@ impl TraitBounds { for b in v.iter() { if let GenericBound::Trait(ref poly_trait_ref, _) = b { let path = &poly_trait_ref.trait_ref.path; - hint_string.push_str(&format!( + let _ = write!(hint_string, " {} +", snippet_with_applicability(cx, path.span, "..", &mut applicability) - )); + ); } } for b in p.bounds.iter() { if let GenericBound::Trait(ref poly_trait_ref, _) = b { let path = &poly_trait_ref.trait_ref.path; - hint_string.push_str(&format!( + let _ = write!(hint_string, " {} +", snippet_with_applicability(cx, path.span, "..", &mut applicability) - )); + ); } } hint_string.truncate(hint_string.len() - 2); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 1fc9979f3dd7d..ffd2b5aabcf0e 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -18,7 +18,7 @@ use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext}; use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::borrow::Cow; use std::convert::TryInto; -use std::fmt::Display; +use std::fmt::{Display, Write as _}; use std::iter; use std::ops::{Add, Neg, Not, Sub}; @@ -901,7 +901,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { if cmt.place.projections.is_empty() { // handle item without any projection, that needs an explicit borrowing // i.e.: suggest `&x` instead of `x` - self.suggestion_start.push_str(&format!("{}&{}", start_snip, ident_str)); + let _ = write!(self.suggestion_start, "{}&{}", start_snip, ident_str); } else { // cases where a parent `Call` or `MethodCall` is using the item // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()` @@ -916,8 +916,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // given expression is the self argument and will be handled completely by the compiler // i.e.: `|x| x.is_something()` ExprKind::MethodCall(_, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => { - self.suggestion_start - .push_str(&format!("{}{}", start_snip, ident_str_with_proj)); + let _ = write!(self.suggestion_start, "{}{}", start_snip, ident_str_with_proj); self.next_pos = span.hi(); return; }, @@ -1025,8 +1024,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { } } - self.suggestion_start - .push_str(&format!("{}{}", start_snip, replacement_str)); + let _ = write!(self.suggestion_start, "{}{}", start_snip, replacement_str); } self.next_pos = span.hi(); } diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 9af8dcc7726f0..16cf728e3cb0f 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -8,6 +8,7 @@ #![allow(clippy::collapsible_else_if)] use std::ffi::OsStr; +use std::fmt::Write as _; use std::process::Command; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{collections::HashMap, io::ErrorKind}; @@ -110,11 +111,12 @@ impl ClippyWarning { let lint = format!("`{}`", self.linttype); let mut output = String::from("| "); - output.push_str(&format!( + let _ = write!( + output, "[`{}`](../target/lintcheck/sources/{}#L{})", file_with_pos, file, self.line - )); - output.push_str(&format!(r#" | {:<50} | "{}" |"#, lint, self.message)); + ); + let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message); output.push('\n'); output } else { @@ -835,10 +837,11 @@ pub fn main() { text.push_str("| file | lint | message |\n"); text.push_str("| --- | --- | --- |\n"); } - text.push_str(&format!("{}", all_msgs.join(""))); + write!(text, "{}", all_msgs.join("")); text.push_str("\n\n### ICEs:\n"); - ices.iter() - .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg))); + for (cratename, msg) in ices.iter() { + let _ = write!(text, "{}: '{}'", cratename, msg); + } println!("Writing logs to {}", config.lintcheck_results_path.display()); std::fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap(); diff --git a/tests/ui/format_push_string.rs b/tests/ui/format_push_string.rs new file mode 100644 index 0000000000000..4db13d650eb18 --- /dev/null +++ b/tests/ui/format_push_string.rs @@ -0,0 +1,7 @@ +#![warn(clippy::format_push_string)] + +fn main() { + let mut string = String::new(); + string += &format!("{:?}", 1234); + string.push_str(&format!("{:?}", 5678)); +} diff --git a/tests/ui/format_push_string.stderr b/tests/ui/format_push_string.stderr new file mode 100644 index 0000000000000..953784bcc0684 --- /dev/null +++ b/tests/ui/format_push_string.stderr @@ -0,0 +1,19 @@ +error: `format!(..)` appended to existing `String` + --> $DIR/format_push_string.rs:5:5 + | +LL | string += &format!("{:?}", 1234); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::format-push-string` implied by `-D warnings` + = help: consider using `write!` to avoid the extra allocation + +error: `format!(..)` appended to existing `String` + --> $DIR/format_push_string.rs:6:5 + | +LL | string.push_str(&format!("{:?}", 5678)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `write!` to avoid the extra allocation + +error: aborting due to 2 previous errors + diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index edc3fe1aec13a..9d39d7696354c 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -1,3 +1,5 @@ +use std::fmt::Write as _; + const ONE: i64 = 1; const NEG_ONE: i64 = -1; const ZERO: i64 = 0; @@ -7,7 +9,7 @@ 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)); + let _ = write!(self.0, "{}", other); self } } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 706f01a3dd6c4..e3c156b54291e 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:37:5 + --> $DIR/identity_op.rs:39:5 | LL | x + 0; | ^^^^^ @@ -7,103 +7,103 @@ 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:38:5 + --> $DIR/identity_op.rs:40:5 | LL | x + (1 - 1); | ^^^^^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:40:5 + --> $DIR/identity_op.rs:42:5 | LL | 0 + x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:43:5 + --> $DIR/identity_op.rs:45:5 | LL | x | (0); | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:46:5 + --> $DIR/identity_op.rs:48:5 | LL | x * 1; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:47:5 + --> $DIR/identity_op.rs:49:5 | LL | 1 * x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:53:5 + --> $DIR/identity_op.rs:55:5 | LL | -1 & x; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `u` - --> $DIR/identity_op.rs:56:5 + --> $DIR/identity_op.rs:58:5 | LL | u & 255; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:59:5 + --> $DIR/identity_op.rs:61:5 | LL | 42 << 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:60:5 + --> $DIR/identity_op.rs:62:5 | LL | 1 >> 0; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:61:5 + --> $DIR/identity_op.rs:63:5 | LL | 42 >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `&x` - --> $DIR/identity_op.rs:62:5 + --> $DIR/identity_op.rs:64:5 | LL | &x >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:63:5 + --> $DIR/identity_op.rs:65:5 | LL | x >> &0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:70:5 + --> $DIR/identity_op.rs:72:5 | LL | 2 % 3; | ^^^^^ error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:71:5 + --> $DIR/identity_op.rs:73:5 | LL | -2 % 3; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:72:5 + --> $DIR/identity_op.rs:74:5 | LL | 2 % -3 + x; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:73:5 + --> $DIR/identity_op.rs:75:5 | LL | -2 % -3 + x; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:74:9 + --> $DIR/identity_op.rs:76:9 | LL | x + 1 % 3; | ^^^^^ From 3bd0ac7a8d21b5b2c4e64dc39abf99c75af12916 Mon Sep 17 00:00:00 2001 From: Fridtjof Stoldt Date: Thu, 14 Apr 2022 07:02:59 +0200 Subject: [PATCH 040/129] Update lint description of `clippy::manual_bits` to include the `as usize` cast --- clippy_lints/src/manual_bits.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 44ff151ea5269..ac3d9447b6bd3 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -24,7 +24,7 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// usize::BITS; + /// usize::BITS as usize; /// ``` #[clippy::version = "1.60.0"] pub MANUAL_BITS, From 262b35ea2c4dd9a16c28bf40cb5c124f664ce96c Mon Sep 17 00:00:00 2001 From: infrandomness Date: Fri, 8 Apr 2022 20:07:19 +0200 Subject: [PATCH 041/129] Introduce option_take_on_temporary lints This lint checks if Option::take() is used on a temporary value (a value that is not of type &mut Option and that is not a Place expression) to suggest omitting take() --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_complexity.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/methods/mod.rs | 21 +++++++++++++++++ .../src/methods/needless_option_take.rs | 23 +++++++++++++++++++ tests/ui/needless_option_take.fixed | 15 ++++++++++++ tests/ui/needless_option_take.rs | 5 ++++ tests/ui/needless_option_take.stderr | 10 ++++++++ 10 files changed, 79 insertions(+) create mode 100644 clippy_lints/src/methods/needless_option_take.rs create mode 100644 tests/ui/needless_option_take.fixed create mode 100644 tests/ui/needless_option_take.rs create mode 100644 tests/ui/needless_option_take.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 858547c84c185..d4096ca89df83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3473,6 +3473,7 @@ Released 2018-09-13 [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref +[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 52c440de9dd43..84e3176c00303 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -183,6 +183,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), + LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::OK_EXPECT), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 10369a855ae6e..2f6ebd445ebd8 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -45,6 +45,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), + LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index cfaa6871b50b1..aa9435f3b59a0 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -322,6 +322,7 @@ store.register_lints(&[ methods::MAP_IDENTITY, methods::MAP_UNWRAP_OR, methods::NEEDLESS_OPTION_AS_DEREF, + methods::NEEDLESS_OPTION_TAKE, methods::NEEDLESS_SPLITN, methods::NEW_RET_NO_SELF, methods::OK_EXPECT, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 82f45b5fd58b9..6588ca4a4a719 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -22,6 +22,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(loops::EMPTY_LOOP), LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::MUT_RANGE_BOUND), + LintId::of(methods::OPTION_TAKE_ON_TEMPORARY), LintId::of(methods::SUSPICIOUS_MAP), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 82f4769a69977..3d4c193691af4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -43,6 +43,7 @@ mod map_flatten; mod map_identity; mod map_unwrap_or; mod needless_option_as_deref; +mod needless_option_take; mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; @@ -2162,6 +2163,24 @@ declare_clippy_lint! { "use of `char::is_digit(..)` with literal radix of 10 or 16" } +declare_clippy_lint! { + /// + /// ### Why is this bad? + /// + /// ### Example + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + #[clippy::version = "1.61.0"] + pub NEEDLESS_OPTION_TAKE, + suspicious, + "using `.as_ref().take()` on a temporary value" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2251,6 +2270,7 @@ impl_lint_pass!(Methods => [ ERR_EXPECT, NEEDLESS_OPTION_AS_DEREF, IS_DIGIT_ASCII_RADIX, + NEEDLESS_OPTION_TAKE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2623,6 +2643,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } } }, + ("take", []) => needless_option_take::check(cx, expr, recv), ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv); }, diff --git a/clippy_lints/src/methods/needless_option_take.rs b/clippy_lints/src/methods/needless_option_take.rs new file mode 100644 index 0000000000000..b3696273e1d05 --- /dev/null +++ b/clippy_lints/src/methods/needless_option_take.rs @@ -0,0 +1,23 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::sym::Result as sym_result; + +use super::NEEDLESS_OPTION_TAKE; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { + if_chain! { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym_result); + let result_type = cx.typeck_results().expr_ty(recv); + + then { + span_lint( + cx, + NEEDLESS_OPTION_TAKE, + expr.span, + "Format test" + ); + } + }; +} diff --git a/tests/ui/needless_option_take.fixed b/tests/ui/needless_option_take.fixed new file mode 100644 index 0000000000000..29691e81666f7 --- /dev/null +++ b/tests/ui/needless_option_take.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +fn main() { + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + println!("Testing erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); +} diff --git a/tests/ui/needless_option_take.rs b/tests/ui/needless_option_take.rs new file mode 100644 index 0000000000000..2b32b9467f506 --- /dev/null +++ b/tests/ui/needless_option_take.rs @@ -0,0 +1,5 @@ +fn main() { + println!("Testing option_take_on_temporary"); + let x = Some(3); + let y = x.as_ref().take(); +} diff --git a/tests/ui/needless_option_take.stderr b/tests/ui/needless_option_take.stderr new file mode 100644 index 0000000000000..cb3bf015b369d --- /dev/null +++ b/tests/ui/needless_option_take.stderr @@ -0,0 +1,10 @@ +error: called `Option::take()` on a temporary value + --> $DIR/needless_option_take.rs:14:5 + | +LL | x.as_ref().take(); + | ^^^^^^^^^^^^^^^^^ help: try: `x.as_ref()` + | + = note: `-D clippy::needless-option-take` implied by `-D warnings` + +error: aborting due to previous error + From ee9281d7a241c448f6bcbbb6cb8bdf8b17251a92 Mon Sep 17 00:00:00 2001 From: infrandomness Date: Fri, 8 Apr 2022 21:27:01 +0200 Subject: [PATCH 042/129] Implement checks to the expression The implemented checks are for checking if the expression is either of type `Option` and isn't a syntactical place --- .../src/methods/needless_option_take.rs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/methods/needless_option_take.rs b/clippy_lints/src/methods/needless_option_take.rs index b3696273e1d05..27ef900932896 100644 --- a/clippy_lints/src/methods/needless_option_take.rs +++ b/clippy_lints/src/methods/needless_option_take.rs @@ -2,15 +2,17 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_span::sym::Result as sym_result; +use rustc_span::sym; use super::NEEDLESS_OPTION_TAKE; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { - if_chain! { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym_result); - let result_type = cx.typeck_results().expr_ty(recv); - +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, _recv: &'tcx Expr<'_>) { + // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place + if is_expr_option(cx, expr) && !expr.is_syntactic_place_expr() { + span_lint(cx, OPTION_TAKE_ON_TEMPORARY, expr.span, "Format test"); + } + /* if_chain! { + is_expr_option(cx, expr); then { span_lint( cx, @@ -19,5 +21,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' "Format test" ); } - }; + };*/ +} + +fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_type = cx.typeck_results().expr_ty(expr); + is_type_diagnostic_item(cx, expr_type, sym::Option) } From f8f144117da48b52f2c65292402dedcb1c59d9c1 Mon Sep 17 00:00:00 2001 From: infrandomness Date: Fri, 8 Apr 2022 21:54:44 +0200 Subject: [PATCH 043/129] Swap span_lint for span_lint_and_sugg This implements a machine applicable suggestion to any matched usage of `.as_ref().take()`` --- .../src/methods/needless_option_take.rs | 31 ++++++++++--------- tests/ui/needless_option_take.rs | 2 ++ tests/ui/option_take_on_temporary.fixed | 7 +++++ 3 files changed, 26 insertions(+), 14 deletions(-) create mode 100644 tests/ui/option_take_on_temporary.fixed diff --git a/clippy_lints/src/methods/needless_option_take.rs b/clippy_lints/src/methods/needless_option_take.rs index 27ef900932896..57a43ff606a56 100644 --- a/clippy_lints/src/methods/needless_option_take.rs +++ b/clippy_lints/src/methods/needless_option_take.rs @@ -1,27 +1,30 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; use super::NEEDLESS_OPTION_TAKE; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, _recv: &'tcx Expr<'_>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place if is_expr_option(cx, expr) && !expr.is_syntactic_place_expr() { - span_lint(cx, OPTION_TAKE_ON_TEMPORARY, expr.span, "Format test"); + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + NEEDLESS_OPTION_TAKE, + expr.span, + "Called `Option::take()` on a temporary value", + "try", + format!( + "{}", + snippet_with_applicability(cx, recv.span, "..", &mut applicability) + ), + applicability, + ); } - /* if_chain! { - is_expr_option(cx, expr); - then { - span_lint( - cx, - NEEDLESS_OPTION_TAKE, - expr.span, - "Format test" - ); - } - };*/ } fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { diff --git a/tests/ui/needless_option_take.rs b/tests/ui/needless_option_take.rs index 2b32b9467f506..eb57ffeab453b 100644 --- a/tests/ui/needless_option_take.rs +++ b/tests/ui/needless_option_take.rs @@ -1,3 +1,5 @@ +// run-rustfix + fn main() { println!("Testing option_take_on_temporary"); let x = Some(3); diff --git a/tests/ui/option_take_on_temporary.fixed b/tests/ui/option_take_on_temporary.fixed new file mode 100644 index 0000000000000..9b4ca5a4ad16c --- /dev/null +++ b/tests/ui/option_take_on_temporary.fixed @@ -0,0 +1,7 @@ +// run-rustfix + +fn main() { + println!("Testing option_take_on_temporary"); + let x = Some(3); + let y = x.as_ref(); +} From cf83e18106160382cd86cc8b35add250373491b6 Mon Sep 17 00:00:00 2001 From: infrandomness Date: Fri, 8 Apr 2022 21:59:59 +0200 Subject: [PATCH 044/129] Swap the category of the lint This changes the lint from the suspicious category to the complexity category --- clippy_lints/src/lib.register_suspicious.rs | 1 - clippy_lints/src/methods/mod.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 6588ca4a4a719..82f45b5fd58b9 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -22,7 +22,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(loops::EMPTY_LOOP), LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::MUT_RANGE_BOUND), - LintId::of(methods::OPTION_TAKE_ON_TEMPORARY), LintId::of(methods::SUSPICIOUS_MAP), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3d4c193691af4..36844b3609b92 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2177,7 +2177,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.61.0"] pub NEEDLESS_OPTION_TAKE, - suspicious, + complexity, "using `.as_ref().take()` on a temporary value" } From 8b2b343b23c50e136e14c303f2579ae2a2fda5f2 Mon Sep 17 00:00:00 2001 From: infrandomness Date: Sat, 9 Apr 2022 18:35:18 +0200 Subject: [PATCH 045/129] Delete unused variable `y` in test This fixes the errors occuring while running the ui tests --- tests/ui/needless_option_take.rs | 2 +- tests/ui/option_take_on_temporary.fixed | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/needless_option_take.rs b/tests/ui/needless_option_take.rs index eb57ffeab453b..27cc9a2e61e74 100644 --- a/tests/ui/needless_option_take.rs +++ b/tests/ui/needless_option_take.rs @@ -3,5 +3,5 @@ fn main() { println!("Testing option_take_on_temporary"); let x = Some(3); - let y = x.as_ref().take(); + x.as_ref().take(); } diff --git a/tests/ui/option_take_on_temporary.fixed b/tests/ui/option_take_on_temporary.fixed index 9b4ca5a4ad16c..7e2930766824b 100644 --- a/tests/ui/option_take_on_temporary.fixed +++ b/tests/ui/option_take_on_temporary.fixed @@ -3,5 +3,5 @@ fn main() { println!("Testing option_take_on_temporary"); let x = Some(3); - let y = x.as_ref(); + x.as_ref(); } From b52bc9b96bb3b22b4ab4a793e3706e7a9b718952 Mon Sep 17 00:00:00 2001 From: infrandomness Date: Sun, 10 Apr 2022 10:59:43 +0200 Subject: [PATCH 046/129] Swap type checked expression Instead of type checking the entire expression (causing a false positive), only type check for a subset of the expression (the receiver of the matched function: `take()`) --- clippy_lints/src/methods/needless_option_take.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/needless_option_take.rs b/clippy_lints/src/methods/needless_option_take.rs index 57a43ff606a56..297d83e9e37da 100644 --- a/clippy_lints/src/methods/needless_option_take.rs +++ b/clippy_lints/src/methods/needless_option_take.rs @@ -10,7 +10,7 @@ use super::NEEDLESS_OPTION_TAKE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place - if is_expr_option(cx, expr) && !expr.is_syntactic_place_expr() { + if is_expr_option(cx, recv) && !recv.is_syntactic_place_expr() { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, From 76268c0d5590ca06129722fed4afb306a847caab Mon Sep 17 00:00:00 2001 From: infrandomness Date: Sun, 10 Apr 2022 23:42:33 +0200 Subject: [PATCH 047/129] Introduce new lint check This checks if the expression has one of `core`, `option`, `Option` or `as_ref` in its path, this avoids false positives --- clippy_lints/src/methods/needless_option_take.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/needless_option_take.rs b/clippy_lints/src/methods/needless_option_take.rs index 297d83e9e37da..5596515637e54 100644 --- a/clippy_lints/src/methods/needless_option_take.rs +++ b/clippy_lints/src/methods/needless_option_take.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::match_def_path; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; @@ -10,7 +11,7 @@ use super::NEEDLESS_OPTION_TAKE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place - if is_expr_option(cx, recv) && !recv.is_syntactic_place_expr() { + if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) && has_expr_as_ref_path(cx, recv) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -31,3 +32,10 @@ fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_type = cx.typeck_results().expr_ty(expr); is_type_diagnostic_item(cx, expr_type, sym::Option) } + +fn has_expr_as_ref_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(ref_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + return match_def_path(cx, ref_id, &["core", "option", "Option", "as_ref"]); + } + false +} From 822993675ff45d5b4b2f0a310c65d4abd1aef20a Mon Sep 17 00:00:00 2001 From: infrandomness Date: Mon, 11 Apr 2022 17:15:53 +0200 Subject: [PATCH 048/129] Modify lint description --- clippy_lints/src/methods/needless_option_take.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/needless_option_take.rs b/clippy_lints/src/methods/needless_option_take.rs index 5596515637e54..829c118d29163 100644 --- a/clippy_lints/src/methods/needless_option_take.rs +++ b/clippy_lints/src/methods/needless_option_take.rs @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' cx, NEEDLESS_OPTION_TAKE, expr.span, - "Called `Option::take()` on a temporary value", + "called `Option::take()` on a temporary value", "try", format!( "{}", From 2903b56f177b2b23db62d6bf2d500837d704fb4e Mon Sep 17 00:00:00 2001 From: infrandomness Date: Tue, 12 Apr 2022 19:34:55 +0200 Subject: [PATCH 049/129] Add tests and docs This adds test to make sure correct behavior of lint - The first test's option variable is not a temporary variable - The second test does not make usage of `take()` - The third test makes usage of `take()` and uses a temporary variable --- clippy_lints/src/methods/mod.rs | 6 ++++-- tests/ui/needless_option_take.rs | 10 +++++++++- tests/ui/option_take_on_temporary.fixed | 10 +++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 36844b3609b92..48fa30ba9295a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2169,11 +2169,13 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // example code where clippy issues a warning + /// let x = Some(3); + /// x.as_ref().take(); /// ``` /// Use instead: /// ```rust - /// // example code which does not raise clippy warning + /// let x = Some(3); + /// x.as_ref(); /// ``` #[clippy::version = "1.61.0"] pub NEEDLESS_OPTION_TAKE, diff --git a/tests/ui/needless_option_take.rs b/tests/ui/needless_option_take.rs index 27cc9a2e61e74..9f4109eb4635a 100644 --- a/tests/ui/needless_option_take.rs +++ b/tests/ui/needless_option_take.rs @@ -1,7 +1,15 @@ // run-rustfix fn main() { - println!("Testing option_take_on_temporary"); + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + println!("Testing erroneous option_take_on_temporary"); let x = Some(3); x.as_ref().take(); } diff --git a/tests/ui/option_take_on_temporary.fixed b/tests/ui/option_take_on_temporary.fixed index 7e2930766824b..29691e81666f7 100644 --- a/tests/ui/option_take_on_temporary.fixed +++ b/tests/ui/option_take_on_temporary.fixed @@ -1,7 +1,15 @@ // run-rustfix fn main() { - println!("Testing option_take_on_temporary"); + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + println!("Testing erroneous option_take_on_temporary"); let x = Some(3); x.as_ref(); } From 0de314b3b3d7ac30edfa2bdd6627683d6a19e694 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Tue, 1 Mar 2022 00:50:56 +0000 Subject: [PATCH 050/129] Reimplement lowering of sym operands for asm! so that it also works with global_asm! --- clippy_lints/src/loops/never_loop.rs | 7 ++++--- clippy_lints/src/utils/inspector.rs | 23 ++++++++++++++++++++--- clippy_utils/src/hir_utils.rs | 3 ++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index a0b2302662e64..9ba9642fcc833 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -169,13 +169,14 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { .iter() .map(|(o, _)| match o { InlineAsmOperand::In { expr, .. } - | InlineAsmOperand::InOut { expr, .. } - | InlineAsmOperand::Sym { expr } => never_loop_expr(expr, main_loop_id), + | InlineAsmOperand::InOut { expr, .. } => never_loop_expr(expr, main_loop_id), InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id), InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id) }, - InlineAsmOperand::Const { .. } => NeverLoopResult::Otherwise, + InlineAsmOperand::Const { .. } + | InlineAsmOperand::SymFn { .. } + | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise, }) .fold(NeverLoopResult::Otherwise, combine_both), ExprKind::Struct(_, _, None) diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index dc48ea3f4f99d..a04288e0a413e 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -281,8 +281,9 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { for (op, _op_sp) in asm.operands { match op { hir::InlineAsmOperand::In { expr, .. } - | hir::InlineAsmOperand::InOut { expr, .. } - | hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1), + | hir::InlineAsmOperand::InOut { expr, .. } => { + print_expr(cx, expr, indent + 1); + } hir::InlineAsmOperand::Out { expr, .. } => { if let Some(expr) = expr { print_expr(cx, expr, indent + 1); @@ -294,10 +295,26 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, out_expr, indent + 1); } }, - hir::InlineAsmOperand::Const { anon_const } => { + hir::InlineAsmOperand::Const { anon_const } + | hir::InlineAsmOperand::SymFn { anon_const } => { println!("{}anon_const:", ind); print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); }, + hir::InlineAsmOperand::SymStatic { path, .. } => { + match path { + hir::QPath::Resolved(ref ty, path) => { + println!("{}Resolved Path, {:?}", ind, ty); + println!("{}path: {:?}", ind, path); + }, + hir::QPath::TypeRelative(ty, seg) => { + println!("{}Relative Path, {:?}", ind, ty); + println!("{}seg: {:?}", ind, seg); + }, + hir::QPath::LangItem(lang_item, ..) => { + println!("{}Lang Item Path, {:?}", ind, lang_item.name()); + }, + } + } } } }, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 00594f4d42add..c05317f59b716 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -675,7 +675,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, InlineAsmOperand::Const { anon_const } => self.hash_body(anon_const.body), - InlineAsmOperand::Sym { expr } => self.hash_expr(expr), + InlineAsmOperand::SymFn { anon_const } => self.hash_body(anon_const.body), + InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path), } } }, From dc970804541bcfdefbcfd45e46fa65c4757d086e Mon Sep 17 00:00:00 2001 From: ouz-a Date: Thu, 14 Apr 2022 23:42:15 +0300 Subject: [PATCH 051/129] Update issue-92893.stderr --- clippy_lints/src/loops/needless_range_loop.rs | 4 ++-- clippy_lints/src/shadow.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 72e86804ed2c3..6ed141fa4a5a6 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -59,7 +59,7 @@ pub(super) fn check<'tcx>( if let Some(indexed_extent) = indexed_extent { let parent_def_id = cx.tcx.hir().get_parent_item(expr.hir_id); let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id); - let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id); + let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { return; } @@ -262,7 +262,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { match res { Res::Local(hir_id) => { let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); - let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id); + let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id).unwrap(); if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 1188258504461..1ab7f52110ce7 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -160,8 +160,8 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool { let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id()); - let first_scope = scope_tree.var_scope(first); - let second_scope = scope_tree.var_scope(second); + let first_scope = scope_tree.var_scope(first).unwrap(); + let second_scope = scope_tree.var_scope(second).unwrap(); scope_tree.is_subscope_of(second_scope, first_scope) } From d68ac9ccce85b88de5f9f7eed169ef301bdaae3f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 17 Mar 2022 19:44:12 -0400 Subject: [PATCH 052/129] Don't lint `let_unit_value` when needed for type inferenece --- clippy_lints/src/unit_types/let_unit_value.rs | 72 +++++++++++++++++-- tests/ui/let_unit.fixed | 27 +++++++ tests/ui/let_unit.rs | 27 +++++++ tests/ui/let_unit.stderr | 42 ++++++++++- 4 files changed, 162 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index b25a6e3375bb4..39352b3ee4769 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,18 +1,38 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_macro_callsite; +use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::{Stmt, StmtKind}; +use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, Ty, TypeFoldable, TypeVisitor}; use super::LET_UNIT_VALUE; pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { - if let StmtKind::Local(local) = stmt.kind { - if cx.typeck_results().pat_ty(local.pat).is_unit() { - if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { - return; + if let StmtKind::Local(local) = stmt.kind + && !local.pat.span.from_expansion() + && !in_external_macro(cx.sess(), stmt.span) + && cx.typeck_results().pat_ty(local.pat).is_unit() + { + if local.init.map_or(false, |e| needs_inferred_result_ty(cx, e)) { + if !matches!(local.pat.kind, PatKind::Wild) { + span_lint_and_then( + cx, + LET_UNIT_VALUE, + stmt.span, + "this let-binding has unit value", + |diag| { + diag.span_suggestion( + local.pat.span, + "use a wild (`_`) binding", + "_".into(), + Applicability::MaybeIncorrect, // snippet + ); + }, + ); } + } else { span_lint_and_then( cx, LET_UNIT_VALUE, @@ -33,3 +53,45 @@ pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } } } + +fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + let id = match e.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(ref path), + hir_id, + .. + }, + _, + ) => cx.qpath_res(path, *hir_id).opt_def_id(), + ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(e.hir_id), + _ => return false, + }; + if let Some(id) = id + && let sig = cx.tcx.fn_sig(id).skip_binder() + && let ty::Param(output_ty) = *sig.output().kind() + { + sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index)) + } else { + false + } +} + +fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool { + struct Visitor(u32); + impl<'tcx> TypeVisitor<'tcx> for Visitor { + type BreakTy = (); + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + if let ty::Param(ty) = *ty.kind() { + if ty.index == self.0 { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } + } else { + ty.super_visit_with(self) + } + } + } + ty.visit_with(&mut Visitor(index)).is_break() +} diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index f398edc23cb5e..5fee742b19248 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -61,3 +61,30 @@ fn multiline_sugg() { #[derive(Copy, Clone)] pub struct ContainsUnit(()); // should be fine + +fn _returns_generic() { + fn f() -> T { + unimplemented!() + } + fn f2(_: T) -> U { + unimplemented!() + } + fn f3(x: T) -> T { + x + } + fn f4(mut x: Vec) -> T { + x.pop().unwrap() + } + + let _: () = f(); // Ok + let _: () = f(); // Lint. + + let _: () = f2(0i32); // Ok + let _: () = f2(0i32); // Lint. + + f3(()); // Lint + f3(()); // Lint + + f4(vec![()]); // Lint + f4(vec![()]); // Lint +} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index af5b1fb2ac7e4..505e4a7d8fdd5 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -61,3 +61,30 @@ fn multiline_sugg() { #[derive(Copy, Clone)] pub struct ContainsUnit(()); // should be fine + +fn _returns_generic() { + fn f() -> T { + unimplemented!() + } + fn f2(_: T) -> U { + unimplemented!() + } + fn f3(x: T) -> T { + x + } + fn f4(mut x: Vec) -> T { + x.pop().unwrap() + } + + let _: () = f(); // Ok + let x: () = f(); // Lint. + + let _: () = f2(0i32); // Ok + let x: () = f2(0i32); // Lint. + + let _: () = f3(()); // Lint + let x: () = f3(()); // Lint + + let _: () = f4(vec![()]); // Lint + let x: () = f4(vec![()]); // Lint +} diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index f2600c6c22883..c4a766bb97440 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -34,5 +34,45 @@ LL + .map(|_| ()) LL + .next() ... -error: aborting due to 3 previous errors +error: this let-binding has unit value + --> $DIR/let_unit.rs:80:5 + | +LL | let x: () = f(); // Lint. + | ^^^^-^^^^^^^^^^^ + | | + | help: use a wild (`_`) binding: `_` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:83:5 + | +LL | let x: () = f2(0i32); // Lint. + | ^^^^-^^^^^^^^^^^^^^^^ + | | + | help: use a wild (`_`) binding: `_` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:85:5 + | +LL | let _: () = f3(()); // Lint + | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:86:5 + | +LL | let x: () = f3(()); // Lint + | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:88:5 + | +LL | let _: () = f4(vec![()]); // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:89:5 + | +LL | let x: () = f4(vec![()]); // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);` + +error: aborting due to 9 previous errors From 48bcc1d95f07eaf03f72f98011e78cb8630798a4 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 17 Mar 2022 19:53:28 -0400 Subject: [PATCH 053/129] Move `let_unit_value` back into `style` --- clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 1 - clippy_lints/src/lib.register_style.rs | 1 + clippy_lints/src/unit_types/mod.rs | 2 +- .../interning_defined_symbol.fixed | 2 +- tests/ui-internal/interning_defined_symbol.rs | 2 +- tests/ui-toml/functions_maxlines/test.rs | 1 + tests/ui-toml/functions_maxlines/test.stderr | 8 +- tests/ui/cast_slice_different_sizes.rs | 2 + tests/ui/cast_slice_different_sizes.stderr | 12 +-- tests/ui/crashes/ice-5944.rs | 1 + tests/ui/default_numeric_fallback_f64.fixed | 15 ++-- tests/ui/default_numeric_fallback_f64.rs | 15 ++-- tests/ui/default_numeric_fallback_f64.stderr | 46 +++++----- tests/ui/default_numeric_fallback_i32.fixed | 13 +-- tests/ui/default_numeric_fallback_i32.rs | 13 +-- tests/ui/default_numeric_fallback_i32.stderr | 50 +++++------ tests/ui/doc_unsafe.rs | 2 + tests/ui/doc_unsafe.stderr | 12 +-- tests/ui/iter_overeager_cloned.fixed | 2 +- tests/ui/iter_overeager_cloned.rs | 2 +- tests/ui/let_underscore_drop.rs | 1 + tests/ui/let_underscore_drop.stderr | 6 +- tests/ui/needless_for_each_fixable.fixed | 7 +- tests/ui/needless_for_each_fixable.rs | 7 +- tests/ui/needless_for_each_fixable.stderr | 16 ++-- tests/ui/needless_late_init.rs | 1 + tests/ui/needless_late_init.stderr | 18 ++-- tests/ui/non_expressive_names.rs | 2 +- tests/ui/option_if_let_else.fixed | 7 +- tests/ui/option_if_let_else.rs | 7 +- tests/ui/option_if_let_else.stderr | 30 +++---- tests/ui/shadow.rs | 1 + tests/ui/shadow.stderr | 88 +++++++++---------- tests/ui/similar_names.rs | 3 +- tests/ui/similar_names.stderr | 28 +++--- tests/ui/single_char_lifetime_names.rs | 1 + tests/ui/single_char_lifetime_names.stderr | 10 +-- tests/ui/suspicious_else_formatting.rs | 2 +- tests/ui/undocumented_unsafe_blocks.rs | 1 + tests/ui/undocumented_unsafe_blocks.stderr | 36 ++++---- tests/ui/uninit.rs | 1 + tests/ui/uninit.stderr | 6 +- tests/ui/unit_arg.rs | 3 +- tests/ui/unit_arg.stderr | 20 ++--- tests/ui/unit_hash.rs | 1 + tests/ui/unit_hash.stderr | 6 +- tests/ui/wildcard_imports.fixed | 3 +- tests/ui/wildcard_imports.rs | 3 +- tests/ui/wildcard_imports.stderr | 42 ++++----- 50 files changed, 303 insertions(+), 257 deletions(-) diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 2fa5973336596..097064ae26f89 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -308,6 +308,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_hash::UNIT_HASH), LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), + LintId::of(unit_types::LET_UNIT_VALUE), LintId::of(unit_types::UNIT_ARG), LintId::of(unit_types::UNIT_CMP), LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index eb6534cb8cae7..96cdc4577d53d 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -89,7 +89,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(types::LINKEDLIST), LintId::of(types::OPTION_OPTION), LintId::of(unicode::UNICODE_NOT_NFC), - LintId::of(unit_types::LET_UNIT_VALUE), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(unused_async::UNUSED_ASYNC), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index d6e4390fde1b6..5a85a9e209ebe 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -107,6 +107,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(unit_types::LET_UNIT_VALUE), LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_unit::UNUSED_UNIT), diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs index d9f5b53b413a0..a9e2073dec251 100644 --- a/clippy_lints/src/unit_types/mod.rs +++ b/clippy_lints/src/unit_types/mod.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub LET_UNIT_VALUE, - pedantic, + style, "creating a `let` binding to a value of unit type, which usually can't be used afterwards" } diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed index 6b7fd6efe394c..eaea218e12888 100644 --- a/tests/ui-internal/interning_defined_symbol.fixed +++ b/tests/ui-internal/interning_defined_symbol.fixed @@ -1,6 +1,6 @@ // run-rustfix #![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] #![feature(rustc_private)] extern crate rustc_span; diff --git a/tests/ui-internal/interning_defined_symbol.rs b/tests/ui-internal/interning_defined_symbol.rs index 98d7d7adad170..7efebb8fae486 100644 --- a/tests/ui-internal/interning_defined_symbol.rs +++ b/tests/ui-internal/interning_defined_symbol.rs @@ -1,6 +1,6 @@ // run-rustfix #![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] #![feature(rustc_private)] extern crate rustc_span; diff --git a/tests/ui-toml/functions_maxlines/test.rs b/tests/ui-toml/functions_maxlines/test.rs index e678c896fd3e3..4ac0378544c7d 100644 --- a/tests/ui-toml/functions_maxlines/test.rs +++ b/tests/ui-toml/functions_maxlines/test.rs @@ -1,4 +1,5 @@ #![warn(clippy::too_many_lines)] +#![allow(clippy::let_unit_value)] // This function should be considered one line. fn many_comments_but_one_line_of_code() { diff --git a/tests/ui-toml/functions_maxlines/test.stderr b/tests/ui-toml/functions_maxlines/test.stderr index d736bf899735a..dc255bdcabafe 100644 --- a/tests/ui-toml/functions_maxlines/test.stderr +++ b/tests/ui-toml/functions_maxlines/test.stderr @@ -1,5 +1,5 @@ error: this function has too many lines (2/1) - --> $DIR/test.rs:18:1 + --> $DIR/test.rs:19:1 | LL | / fn too_many_lines() { LL | | println!("This is bad."); @@ -10,7 +10,7 @@ LL | | } = note: `-D clippy::too-many-lines` implied by `-D warnings` error: this function has too many lines (4/1) - --> $DIR/test.rs:24:1 + --> $DIR/test.rs:25:1 | LL | / async fn async_too_many_lines() { LL | | println!("This is bad."); @@ -19,7 +19,7 @@ LL | | } | |_^ error: this function has too many lines (4/1) - --> $DIR/test.rs:30:1 + --> $DIR/test.rs:31:1 | LL | / fn closure_too_many_lines() { LL | | let _ = { @@ -30,7 +30,7 @@ LL | | } | |_^ error: this function has too many lines (2/1) - --> $DIR/test.rs:52:1 + --> $DIR/test.rs:53:1 | LL | / fn comment_before_code() { LL | | let _ = "test"; diff --git a/tests/ui/cast_slice_different_sizes.rs b/tests/ui/cast_slice_different_sizes.rs index cfe1cca2eba94..57270fcf52b52 100644 --- a/tests/ui/cast_slice_different_sizes.rs +++ b/tests/ui/cast_slice_different_sizes.rs @@ -1,3 +1,5 @@ +#![allow(clippy::let_unit_value)] + fn main() { let x: [i32; 3] = [1_i32, 2, 3]; let r_x = &x; diff --git a/tests/ui/cast_slice_different_sizes.stderr b/tests/ui/cast_slice_different_sizes.stderr index a37cec7cb3be0..993e93c2bf366 100644 --- a/tests/ui/cast_slice_different_sizes.stderr +++ b/tests/ui/cast_slice_different_sizes.stderr @@ -1,5 +1,5 @@ error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:7:13 + --> $DIR/cast_slice_different_sizes.rs:9:13 | LL | let b = a as *const [u8]; | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(a as *const u8, ..)` @@ -7,25 +7,25 @@ LL | let b = a as *const [u8]; = note: `#[deny(clippy::cast_slice_different_sizes)]` on by default error: casting between raw pointers to `[u8]` (element size 1) and `[u32]` (element size 4) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:8:13 + --> $DIR/cast_slice_different_sizes.rs:10:13 | LL | let c = b as *const [u32]; | ^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(b as *const u32, ..)` error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:11:16 + --> $DIR/cast_slice_different_sizes.rs:13:16 | LL | let loss = r_x as *const [i32] as *const [u8]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)` error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:18:24 + --> $DIR/cast_slice_different_sizes.rs:20:24 | LL | let loss_block_1 = { r_x as *const [i32] } as *const [u8]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts({ r_x as *const [i32] } as *const u8, ..)` error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:19:24 + --> $DIR/cast_slice_different_sizes.rs:21:24 | LL | let loss_block_2 = { | ________________________^ @@ -43,7 +43,7 @@ LL ~ } as *const u8, ..); | error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:36:27 + --> $DIR/cast_slice_different_sizes.rs:38:27 | LL | let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const u8, ..)` diff --git a/tests/ui/crashes/ice-5944.rs b/tests/ui/crashes/ice-5944.rs index 5caf29c619735..ce46bc1acc1b7 100644 --- a/tests/ui/crashes/ice-5944.rs +++ b/tests/ui/crashes/ice-5944.rs @@ -1,4 +1,5 @@ #![warn(clippy::repeat_once)] +#![allow(clippy::let_unit_value)] trait Repeat { fn repeat(&self) {} diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed index e0b4a2f694239..a28bff76755b6 100644 --- a/tests/ui/default_numeric_fallback_f64.fixed +++ b/tests/ui/default_numeric_fallback_f64.fixed @@ -2,12 +2,15 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] -#![allow(clippy::match_single_binding)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::match_single_binding, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs index 50bbb6eec6c70..b48435cc7b282 100644 --- a/tests/ui/default_numeric_fallback_f64.rs +++ b/tests/ui/default_numeric_fallback_f64.rs @@ -2,12 +2,15 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] -#![allow(clippy::match_single_binding)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::match_single_binding, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/default_numeric_fallback_f64.stderr b/tests/ui/default_numeric_fallback_f64.stderr index f8a2407b6933d..f8b6c7746edbb 100644 --- a/tests/ui/default_numeric_fallback_f64.stderr +++ b/tests/ui/default_numeric_fallback_f64.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:18:17 + --> $DIR/default_numeric_fallback_f64.rs:21:17 | LL | let x = 0.12; | ^^^^ help: consider adding suffix: `0.12_f64` @@ -7,133 +7,133 @@ LL | let x = 0.12; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:19:18 + --> $DIR/default_numeric_fallback_f64.rs:22:18 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:19:22 + --> $DIR/default_numeric_fallback_f64.rs:22:22 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `2.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:19:26 + --> $DIR/default_numeric_fallback_f64.rs:22:26 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `3.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:28 + --> $DIR/default_numeric_fallback_f64.rs:23:28 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:32 + --> $DIR/default_numeric_fallback_f64.rs:23:32 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `2.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:46 + --> $DIR/default_numeric_fallback_f64.rs:23:46 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `3.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:50 + --> $DIR/default_numeric_fallback_f64.rs:23:50 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `4.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:21:23 + --> $DIR/default_numeric_fallback_f64.rs:24:23 | LL | let x = match 1. { | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:22:18 + --> $DIR/default_numeric_fallback_f64.rs:25:18 | LL | _ => 1., | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:40:21 + --> $DIR/default_numeric_fallback_f64.rs:43:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:48:21 + --> $DIR/default_numeric_fallback_f64.rs:51:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:54:21 + --> $DIR/default_numeric_fallback_f64.rs:57:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:66:9 + --> $DIR/default_numeric_fallback_f64.rs:69:9 | LL | 1. | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:72:27 + --> $DIR/default_numeric_fallback_f64.rs:75:27 | LL | let f = || -> _ { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:76:29 + --> $DIR/default_numeric_fallback_f64.rs:79:29 | LL | let f = || -> f64 { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:90:21 + --> $DIR/default_numeric_fallback_f64.rs:93:21 | LL | generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:93:32 + --> $DIR/default_numeric_fallback_f64.rs:96:32 | LL | let x: _ = generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:111:28 + --> $DIR/default_numeric_fallback_f64.rs:114:28 | LL | GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:114:36 + --> $DIR/default_numeric_fallback_f64.rs:117:36 | LL | let _ = GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:132:24 + --> $DIR/default_numeric_fallback_f64.rs:135:24 | LL | GenericEnum::X(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:152:23 + --> $DIR/default_numeric_fallback_f64.rs:155:23 | LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:159:21 + --> $DIR/default_numeric_fallback_f64.rs:162:21 | LL | let x = 22.; | ^^^ help: consider adding suffix: `22.0_f64` diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index bded9e2c0e801..fa85d278c8fca 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -2,11 +2,14 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index 3fceefa551c78..71acccd702b06 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -2,11 +2,14 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr index 6f9e124704b2c..3cc84ff113232 100644 --- a/tests/ui/default_numeric_fallback_i32.stderr +++ b/tests/ui/default_numeric_fallback_i32.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:17:17 + --> $DIR/default_numeric_fallback_i32.rs:20:17 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -7,145 +7,145 @@ LL | let x = 22; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:18:18 + --> $DIR/default_numeric_fallback_i32.rs:21:18 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:18:21 + --> $DIR/default_numeric_fallback_i32.rs:21:21 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:18:24 + --> $DIR/default_numeric_fallback_i32.rs:21:24 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:28 + --> $DIR/default_numeric_fallback_i32.rs:22:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:31 + --> $DIR/default_numeric_fallback_i32.rs:22:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:44 + --> $DIR/default_numeric_fallback_i32.rs:22:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:47 + --> $DIR/default_numeric_fallback_i32.rs:22:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:20:23 + --> $DIR/default_numeric_fallback_i32.rs:23:23 | LL | let x = match 1 { | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:13 + --> $DIR/default_numeric_fallback_i32.rs:24:13 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:18 + --> $DIR/default_numeric_fallback_i32.rs:24:18 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:18 + --> $DIR/default_numeric_fallback_i32.rs:25:18 | LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:39:21 + --> $DIR/default_numeric_fallback_i32.rs:42:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:47:21 + --> $DIR/default_numeric_fallback_i32.rs:50:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:53:21 + --> $DIR/default_numeric_fallback_i32.rs:56:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:65:9 + --> $DIR/default_numeric_fallback_i32.rs:68:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:71:27 + --> $DIR/default_numeric_fallback_i32.rs:74:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:75:29 + --> $DIR/default_numeric_fallback_i32.rs:78:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:89:21 + --> $DIR/default_numeric_fallback_i32.rs:92:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:92:32 + --> $DIR/default_numeric_fallback_i32.rs:95:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:110:28 + --> $DIR/default_numeric_fallback_i32.rs:113:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:113:36 + --> $DIR/default_numeric_fallback_i32.rs:116:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:131:24 + --> $DIR/default_numeric_fallback_i32.rs:134:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:151:23 + --> $DIR/default_numeric_fallback_i32.rs:154:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:158:21 + --> $DIR/default_numeric_fallback_i32.rs:161:21 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` diff --git a/tests/ui/doc_unsafe.rs b/tests/ui/doc_unsafe.rs index 4464a21b3b654..b91f7aa0dd8d2 100644 --- a/tests/ui/doc_unsafe.rs +++ b/tests/ui/doc_unsafe.rs @@ -1,5 +1,7 @@ // aux-build:doc_unsafe_macros.rs +#![allow(clippy::let_unit_value)] + #[macro_use] extern crate doc_unsafe_macros; diff --git a/tests/ui/doc_unsafe.stderr b/tests/ui/doc_unsafe.stderr index d68b8a0c67be6..904b88eaef622 100644 --- a/tests/ui/doc_unsafe.stderr +++ b/tests/ui/doc_unsafe.stderr @@ -1,5 +1,5 @@ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:7:1 + --> $DIR/doc_unsafe.rs:9:1 | LL | / pub unsafe fn destroy_the_planet() { LL | | unimplemented!(); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-safety-doc` implied by `-D warnings` error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:30:5 + --> $DIR/doc_unsafe.rs:32:5 | LL | / pub unsafe fn republished() { LL | | unimplemented!(); @@ -17,13 +17,13 @@ LL | | } | |_____^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:38:5 + --> $DIR/doc_unsafe.rs:40:5 | LL | unsafe fn woefully_underdocumented(self); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for unsafe trait missing `# Safety` section - --> $DIR/doc_unsafe.rs:44:1 + --> $DIR/doc_unsafe.rs:46:1 | LL | / pub unsafe trait UnsafeTrait { LL | | fn method(); @@ -31,7 +31,7 @@ LL | | } | |_^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:74:5 + --> $DIR/doc_unsafe.rs:76:5 | LL | / pub unsafe fn more_undocumented_unsafe() -> Self { LL | | unimplemented!(); @@ -39,7 +39,7 @@ LL | | } | |_____^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:90:9 + --> $DIR/doc_unsafe.rs:92:9 | LL | / pub unsafe fn whee() { LL | | unimplemented!() diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index 56761ebbcb80b..7c2b05d837ba8 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code)] +#![allow(dead_code, clippy::let_unit_value)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index 98321d889b582..f2d0b155d2c2a 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code)] +#![allow(dead_code, clippy::let_unit_value)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/tests/ui/let_underscore_drop.rs b/tests/ui/let_underscore_drop.rs index 50744f81c3cf8..11b50492ab290 100644 --- a/tests/ui/let_underscore_drop.rs +++ b/tests/ui/let_underscore_drop.rs @@ -1,4 +1,5 @@ #![warn(clippy::let_underscore_drop)] +#![allow(clippy::let_unit_value)] struct Droppable; diff --git a/tests/ui/let_underscore_drop.stderr b/tests/ui/let_underscore_drop.stderr index 66069e0c5e13f..ee7bbe995f168 100644 --- a/tests/ui/let_underscore_drop.stderr +++ b/tests/ui/let_underscore_drop.stderr @@ -1,5 +1,5 @@ error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:16:5 + --> $DIR/let_underscore_drop.rs:17:5 | LL | let _ = Box::new(()); | ^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _ = Box::new(()); = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:17:5 + --> $DIR/let_underscore_drop.rs:18:5 | LL | let _ = Droppable; | ^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let _ = Droppable; = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:18:5 + --> $DIR/let_underscore_drop.rs:19:5 | LL | let _ = Some(Droppable); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed index f00f9ee4c331b..c1685f7b6d7ad 100644 --- a/tests/ui/needless_for_each_fixable.fixed +++ b/tests/ui/needless_for_each_fixable.fixed @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] -#![allow(unused, clippy::needless_return, clippy::match_single_binding)] +#![allow( + unused, + clippy::needless_return, + clippy::match_single_binding, + clippy::let_unit_value +)] use std::collections::HashMap; diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs index 1bd400d348ba9..ad17b0956fa93 100644 --- a/tests/ui/needless_for_each_fixable.rs +++ b/tests/ui/needless_for_each_fixable.rs @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] -#![allow(unused, clippy::needless_return, clippy::match_single_binding)] +#![allow( + unused, + clippy::needless_return, + clippy::match_single_binding, + clippy::let_unit_value +)] use std::collections::HashMap; diff --git a/tests/ui/needless_for_each_fixable.stderr b/tests/ui/needless_for_each_fixable.stderr index 6487e57266c71..08e995851d7a5 100644 --- a/tests/ui/needless_for_each_fixable.stderr +++ b/tests/ui/needless_for_each_fixable.stderr @@ -1,5 +1,5 @@ error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:10:5 + --> $DIR/needless_for_each_fixable.rs:15:5 | LL | / v.iter().for_each(|elem| { LL | | acc += elem; @@ -15,7 +15,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:13:5 + --> $DIR/needless_for_each_fixable.rs:18:5 | LL | / v.into_iter().for_each(|elem| { LL | | acc += elem; @@ -30,7 +30,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:17:5 + --> $DIR/needless_for_each_fixable.rs:22:5 | LL | / [1, 2, 3].iter().for_each(|elem| { LL | | acc += elem; @@ -45,7 +45,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:22:5 + --> $DIR/needless_for_each_fixable.rs:27:5 | LL | / hash_map.iter().for_each(|(k, v)| { LL | | acc += k + v; @@ -60,7 +60,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:25:5 + --> $DIR/needless_for_each_fixable.rs:30:5 | LL | / hash_map.iter_mut().for_each(|(k, v)| { LL | | acc += *k + *v; @@ -75,7 +75,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:28:5 + --> $DIR/needless_for_each_fixable.rs:33:5 | LL | / hash_map.keys().for_each(|k| { LL | | acc += k; @@ -90,7 +90,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:31:5 + --> $DIR/needless_for_each_fixable.rs:36:5 | LL | / hash_map.values().for_each(|v| { LL | | acc += v; @@ -105,7 +105,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:38:5 + --> $DIR/needless_for_each_fixable.rs:43:5 | LL | / my_vec().iter().for_each(|elem| { LL | | acc += elem; diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 89e012c066fe4..3cfbfb6033bd8 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,4 +1,5 @@ #![allow(unused)] +#![allow(clippy::let_unit_value)] fn main() { let a; diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index ef79e635d2ae6..9ef8bb23df6a6 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ error: unneeded late initalization - --> $DIR/needless_late_init.rs:4:5 + --> $DIR/needless_late_init.rs:5:5 | LL | let a; | ^^^^^^ @@ -21,7 +21,7 @@ LL | }; | + error: unneeded late initalization - --> $DIR/needless_late_init.rs:13:5 + --> $DIR/needless_late_init.rs:14:5 | LL | let b; | ^^^^^^ @@ -42,7 +42,7 @@ LL | }; | + error: unneeded late initalization - --> $DIR/needless_late_init.rs:20:5 + --> $DIR/needless_late_init.rs:21:5 | LL | let c; | ^^^^^^ @@ -63,7 +63,7 @@ LL | }; | + error: unneeded late initalization - --> $DIR/needless_late_init.rs:27:5 + --> $DIR/needless_late_init.rs:28:5 | LL | let d; | ^^^^^^ @@ -84,7 +84,7 @@ LL | }; | + error: unneeded late initalization - --> $DIR/needless_late_init.rs:35:5 + --> $DIR/needless_late_init.rs:36:5 | LL | let e; | ^^^^^^ @@ -105,7 +105,7 @@ LL | }; | + error: unneeded late initalization - --> $DIR/needless_late_init.rs:42:5 + --> $DIR/needless_late_init.rs:43:5 | LL | let f; | ^^^^^^ @@ -121,7 +121,7 @@ LL + 1 => "three", | error: unneeded late initalization - --> $DIR/needless_late_init.rs:48:5 + --> $DIR/needless_late_init.rs:49:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -141,7 +141,7 @@ LL | }; | + error: unneeded late initalization - --> $DIR/needless_late_init.rs:63:5 + --> $DIR/needless_late_init.rs:64:5 | LL | let a; | ^^^^^^ @@ -162,7 +162,7 @@ LL | }; | + error: unneeded late initalization - --> $DIR/needless_late_init.rs:80:5 + --> $DIR/needless_late_init.rs:81:5 | LL | let a; | ^^^^^^ diff --git a/tests/ui/non_expressive_names.rs b/tests/ui/non_expressive_names.rs index 9937005d68d8a..583096ac054a1 100644 --- a/tests/ui/non_expressive_names.rs +++ b/tests/ui/non_expressive_names.rs @@ -1,5 +1,5 @@ #![warn(clippy::all)] -#![allow(unused, clippy::println_empty_string, non_snake_case)] +#![allow(unused, clippy::println_empty_string, non_snake_case, clippy::let_unit_value)] #[derive(Clone, Debug)] enum MaybeInst { diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 7790c816481d1..e12e13a57f1f0 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::option_if_let_else)] -#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)] +#![allow( + clippy::redundant_closure, + clippy::ref_option_ref, + clippy::equatable_if_let, + clippy::let_unit_value +)] fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 3d9f76ee4a6b5..b5206fc26a9e1 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::option_if_let_else)] -#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)] +#![allow( + clippy::redundant_closure, + clippy::ref_option_ref, + clippy::equatable_if_let, + clippy::let_unit_value +)] fn bad1(string: Option<&str>) -> (bool, &str) { if let Some(x) = string { diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 546131ceb5b63..40aef977b989b 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:6:5 + --> $DIR/option_if_let_else.rs:11:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -11,19 +11,19 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:24:13 + --> $DIR/option_if_let_else.rs:29:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:25:13 + --> $DIR/option_if_let_else.rs:30:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:26:13 + --> $DIR/option_if_let_else.rs:31:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -43,13 +43,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:32:13 + --> $DIR/option_if_let_else.rs:37:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:33:13 + --> $DIR/option_if_let_else.rs:38:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -69,7 +69,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:39:13 + --> $DIR/option_if_let_else.rs:44:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -89,7 +89,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:48:5 + --> $DIR/option_if_let_else.rs:53:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -108,7 +108,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:61:13 + --> $DIR/option_if_let_else.rs:66:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -120,7 +120,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:70:13 + --> $DIR/option_if_let_else.rs:75:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -143,7 +143,7 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:103:13 + --> $DIR/option_if_let_else.rs:108:13 | LL | / if let Some(idx) = s.find('.') { LL | | vec![s[..idx].to_string(), s[idx..].to_string()] @@ -153,13 +153,13 @@ LL | | } | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:127:13 + --> $DIR/option_if_let_else.rs:132:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:136:13 + --> $DIR/option_if_let_else.rs:141:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -181,13 +181,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:164:13 + --> $DIR/option_if_let_else.rs:169:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:168:13 + --> $DIR/option_if_let_else.rs:173:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index 0321f8c4cdf84..a394ef8f25c67 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -1,4 +1,5 @@ #![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)] +#![allow(clippy::let_unit_value)] fn shadow_same() { let x = 1; diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index f8b9221d55587..3bd41d0626049 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -1,266 +1,266 @@ error: `x` is shadowed by itself in `x` - --> $DIR/shadow.rs:5:9 + --> $DIR/shadow.rs:6:9 | LL | let x = x; | ^ | = note: `-D clippy::shadow-same` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:4:9 + --> $DIR/shadow.rs:5:9 | LL | let x = 1; | ^ error: `mut x` is shadowed by itself in `&x` - --> $DIR/shadow.rs:6:13 + --> $DIR/shadow.rs:7:13 | LL | let mut x = &x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:5:9 + --> $DIR/shadow.rs:6:9 | LL | let x = x; | ^ error: `x` is shadowed by itself in `&mut x` - --> $DIR/shadow.rs:7:9 + --> $DIR/shadow.rs:8:9 | LL | let x = &mut x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:6:9 + --> $DIR/shadow.rs:7:9 | LL | let mut x = &x; | ^^^^^ error: `x` is shadowed by itself in `*x` - --> $DIR/shadow.rs:8:9 + --> $DIR/shadow.rs:9:9 | LL | let x = *x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:7:9 + --> $DIR/shadow.rs:8:9 | LL | let x = &mut x; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:13:9 + --> $DIR/shadow.rs:14:9 | LL | let x = x.0; | ^ | = note: `-D clippy::shadow-reuse` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:12:9 + --> $DIR/shadow.rs:13:9 | LL | let x = ([[0]], ()); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:14:9 + --> $DIR/shadow.rs:15:9 | LL | let x = x[0]; | ^ | note: previous binding is here - --> $DIR/shadow.rs:13:9 + --> $DIR/shadow.rs:14:9 | LL | let x = x.0; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:15:10 + --> $DIR/shadow.rs:16:10 | LL | let [x] = x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:14:9 + --> $DIR/shadow.rs:15:9 | LL | let x = x[0]; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:16:9 + --> $DIR/shadow.rs:17:9 | LL | let x = Some(x); | ^ | note: previous binding is here - --> $DIR/shadow.rs:15:10 + --> $DIR/shadow.rs:16:10 | LL | let [x] = x; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:17:9 + --> $DIR/shadow.rs:18:9 | LL | let x = foo(x); | ^ | note: previous binding is here - --> $DIR/shadow.rs:16:9 + --> $DIR/shadow.rs:17:9 | LL | let x = Some(x); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:18:9 + --> $DIR/shadow.rs:19:9 | LL | let x = || x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:17:9 + --> $DIR/shadow.rs:18:9 | LL | let x = foo(x); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:19:9 + --> $DIR/shadow.rs:20:9 | LL | let x = Some(1).map(|_| x)?; | ^ | note: previous binding is here - --> $DIR/shadow.rs:18:9 + --> $DIR/shadow.rs:19:9 | LL | let x = || x; | ^ error: `y` is shadowed - --> $DIR/shadow.rs:21:9 + --> $DIR/shadow.rs:22:9 | LL | let y = match y { | ^ | note: previous binding is here - --> $DIR/shadow.rs:20:9 + --> $DIR/shadow.rs:21:9 | LL | let y = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:30:9 + --> $DIR/shadow.rs:31:9 | LL | let x = 2; | ^ | = note: `-D clippy::shadow-unrelated` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:29:9 + --> $DIR/shadow.rs:30:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:35:13 + --> $DIR/shadow.rs:36:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:34:10 + --> $DIR/shadow.rs:35:10 | LL | fn f(x: u32) { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:40:14 + --> $DIR/shadow.rs:41:14 | LL | Some(x) => { | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:41:17 + --> $DIR/shadow.rs:42:17 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:40:14 + --> $DIR/shadow.rs:41:14 | LL | Some(x) => { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:45:17 + --> $DIR/shadow.rs:46:17 | LL | if let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:46:20 + --> $DIR/shadow.rs:47:20 | LL | while let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:47:15 + --> $DIR/shadow.rs:48:15 | LL | let _ = |[x]: [u32; 1]| { | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:48:13 + --> $DIR/shadow.rs:49:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:47:15 + --> $DIR/shadow.rs:48:15 | LL | let _ = |[x]: [u32; 1]| { | ^ error: `y` is shadowed - --> $DIR/shadow.rs:51:17 + --> $DIR/shadow.rs:52:17 | LL | if let Some(y) = y {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:50:9 + --> $DIR/shadow.rs:51:9 | LL | let y = Some(1); | ^ error: `_b` shadows a previous, unrelated binding - --> $DIR/shadow.rs:87:9 + --> $DIR/shadow.rs:88:9 | LL | let _b = _a; | ^^ | note: previous binding is here - --> $DIR/shadow.rs:86:28 + --> $DIR/shadow.rs:87:28 | LL | pub async fn foo2(_a: i32, _b: i64) { | ^^ diff --git a/tests/ui/similar_names.rs b/tests/ui/similar_names.rs index 76f6ce9ee6b47..c21225d153bd6 100644 --- a/tests/ui/similar_names.rs +++ b/tests/ui/similar_names.rs @@ -3,7 +3,8 @@ unused, clippy::println_empty_string, clippy::empty_loop, - clippy::diverging_sub_expression + clippy::diverging_sub_expression, + clippy::let_unit_value )] struct Foo { diff --git a/tests/ui/similar_names.stderr b/tests/ui/similar_names.stderr index faf572b0c6bc2..6e77269389737 100644 --- a/tests/ui/similar_names.stderr +++ b/tests/ui/similar_names.stderr @@ -1,84 +1,84 @@ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:20:9 + --> $DIR/similar_names.rs:21:9 | LL | let bpple: i32; | ^^^^^ | = note: `-D clippy::similar-names` implied by `-D warnings` note: existing binding defined here - --> $DIR/similar_names.rs:18:9 + --> $DIR/similar_names.rs:19:9 | LL | let apple: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:22:9 + --> $DIR/similar_names.rs:23:9 | LL | let cpple: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:18:9 + --> $DIR/similar_names.rs:19:9 | LL | let apple: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:46:9 + --> $DIR/similar_names.rs:47:9 | LL | let bluby: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:45:9 + --> $DIR/similar_names.rs:46:9 | LL | let blubx: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:50:9 + --> $DIR/similar_names.rs:51:9 | LL | let coke: i32; | ^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:48:9 + --> $DIR/similar_names.rs:49:9 | LL | let cake: i32; | ^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:68:9 + --> $DIR/similar_names.rs:69:9 | LL | let xyzeabc: i32; | ^^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:66:9 + --> $DIR/similar_names.rs:67:9 | LL | let xyz1abc: i32; | ^^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:72:9 + --> $DIR/similar_names.rs:73:9 | LL | let parsee: i32; | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:70:9 + --> $DIR/similar_names.rs:71:9 | LL | let parser: i32; | ^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:93:16 + --> $DIR/similar_names.rs:94:16 | LL | bpple: sprang, | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:92:16 + --> $DIR/similar_names.rs:93:16 | LL | apple: spring, | ^^^^^^ diff --git a/tests/ui/single_char_lifetime_names.rs b/tests/ui/single_char_lifetime_names.rs index 261d8bc7260ca..69c5b236f7cf8 100644 --- a/tests/ui/single_char_lifetime_names.rs +++ b/tests/ui/single_char_lifetime_names.rs @@ -1,4 +1,5 @@ #![warn(clippy::single_char_lifetime_names)] +#![allow(clippy::let_unit_value)] // Lifetimes should only be linted when they're introduced struct DiagnosticCtx<'a, 'b> diff --git a/tests/ui/single_char_lifetime_names.stderr b/tests/ui/single_char_lifetime_names.stderr index 013b64f46a8ce..1438b3999dba8 100644 --- a/tests/ui/single_char_lifetime_names.stderr +++ b/tests/ui/single_char_lifetime_names.stderr @@ -1,5 +1,5 @@ error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:4:22 + --> $DIR/single_char_lifetime_names.rs:5:22 | LL | struct DiagnosticCtx<'a, 'b> | ^^ @@ -8,7 +8,7 @@ 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:4:26 + --> $DIR/single_char_lifetime_names.rs:5:26 | LL | struct DiagnosticCtx<'a, 'b> | ^^ @@ -16,7 +16,7 @@ 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 + --> $DIR/single_char_lifetime_names.rs:14:6 | LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { | ^^ @@ -24,7 +24,7 @@ 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 + --> $DIR/single_char_lifetime_names.rs:14:10 | LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { | ^^ @@ -32,7 +32,7 @@ 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 + --> $DIR/single_char_lifetime_names.rs:34:15 | LL | fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) { | ^^ diff --git a/tests/ui/suspicious_else_formatting.rs b/tests/ui/suspicious_else_formatting.rs index fcd827a91c7f6..21753e5dc6a47 100644 --- a/tests/ui/suspicious_else_formatting.rs +++ b/tests/ui/suspicious_else_formatting.rs @@ -1,7 +1,7 @@ // aux-build:proc_macro_suspicious_else_formatting.rs #![warn(clippy::suspicious_else_formatting)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::let_unit_value)] extern crate proc_macro_suspicious_else_formatting; use proc_macro_suspicious_else_formatting::DeriveBadSpan; diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index afa337c45f417..7be15b0b2dd3d 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -1,6 +1,7 @@ // aux-build:proc_macro_unsafe.rs #![warn(clippy::undocumented_unsafe_blocks)] +#![allow(clippy::let_unit_value)] extern crate proc_macro_unsafe; diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 856a07fd31685..87d445bd7b86b 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:256:19 + --> $DIR/undocumented_unsafe_blocks.rs:257:19 | LL | /* Safety: */ unsafe {} | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | /* Safety: */ unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:260:5 + --> $DIR/undocumented_unsafe_blocks.rs:261:5 | LL | unsafe {} | ^^^^^^^^^ @@ -16,7 +16,7 @@ LL | unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:14 + --> $DIR/undocumented_unsafe_blocks.rs:265:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:29 + --> $DIR/undocumented_unsafe_blocks.rs:265:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:48 + --> $DIR/undocumented_unsafe_blocks.rs:265:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:268:18 + --> $DIR/undocumented_unsafe_blocks.rs:269:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:268:37 + --> $DIR/undocumented_unsafe_blocks.rs:269:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:272:14 + --> $DIR/undocumented_unsafe_blocks.rs:273:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = *unsafe { &42 }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:277:19 + --> $DIR/undocumented_unsafe_blocks.rs:278:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = match unsafe {} { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:283:14 + --> $DIR/undocumented_unsafe_blocks.rs:284:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:287:14 + --> $DIR/undocumented_unsafe_blocks.rs:288:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = [unsafe {}; 5]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:291:13 + --> $DIR/undocumented_unsafe_blocks.rs:292:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:301:8 + --> $DIR/undocumented_unsafe_blocks.rs:302:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -104,7 +104,7 @@ LL | t!(unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:307:13 + --> $DIR/undocumented_unsafe_blocks.rs:308:13 | LL | unsafe {} | ^^^^^^^^^ @@ -116,7 +116,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:315:5 + --> $DIR/undocumented_unsafe_blocks.rs:316:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -124,7 +124,7 @@ LL | unsafe {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:319:5 + --> $DIR/undocumented_unsafe_blocks.rs:320:5 | LL | unsafe { | ^^^^^^^^ @@ -132,7 +132,7 @@ LL | unsafe { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:329:5 + --> $DIR/undocumented_unsafe_blocks.rs:330:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -140,7 +140,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:333:20 + --> $DIR/undocumented_unsafe_blocks.rs:334:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/uninit.rs b/tests/ui/uninit.rs index 1ed3883c1f060..dac5ce272c026 100644 --- a/tests/ui/uninit.rs +++ b/tests/ui/uninit.rs @@ -1,4 +1,5 @@ #![feature(stmt_expr_attributes)] +#![allow(clippy::let_unit_value)] use std::mem::{self, MaybeUninit}; diff --git a/tests/ui/uninit.stderr b/tests/ui/uninit.stderr index 85b64a8419ab0..15ef2349489fa 100644 --- a/tests/ui/uninit.stderr +++ b/tests/ui/uninit.stderr @@ -1,5 +1,5 @@ error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:6:29 + --> $DIR/uninit.rs:7:29 | LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; = note: `#[deny(clippy::uninit_assumed_init)]` on by default error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:9:31 + --> $DIR/uninit.rs:10:31 | LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:24:29 + --> $DIR/uninit.rs:25:29 | LL | let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 535683729f686..38be87bddf198 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -7,7 +7,8 @@ clippy::unnecessary_wraps, clippy::or_fun_call, clippy::needless_question_mark, - clippy::self_named_constructors + clippy::self_named_constructors, + clippy::let_unit_value )] use std::fmt::Debug; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 5cfb367a7be80..394dee29dc96e 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:56:5 + --> $DIR/unit_arg.rs:57:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:59:5 + --> $DIR/unit_arg.rs:60:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:60:5 + --> $DIR/unit_arg.rs:61:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:65:5 + --> $DIR/unit_arg.rs:66:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL ~ b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:68:5 + --> $DIR/unit_arg.rs:69:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:69:5 + --> $DIR/unit_arg.rs:70:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:73:5 + --> $DIR/unit_arg.rs:74:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL + foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:84:13 + --> $DIR/unit_arg.rs:85:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL ~ }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:87:5 + --> $DIR/unit_arg.rs:88:5 | LL | foo(foo(())); | ^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:124:5 + --> $DIR/unit_arg.rs:125:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/tests/ui/unit_hash.rs b/tests/ui/unit_hash.rs index 989916c239bad..43eb54eff477b 100644 --- a/tests/ui/unit_hash.rs +++ b/tests/ui/unit_hash.rs @@ -1,4 +1,5 @@ #![warn(clippy::unit_hash)] +#![allow(clippy::let_unit_value)] use std::collections::hash_map::DefaultHasher; use std::hash::Hash; diff --git a/tests/ui/unit_hash.stderr b/tests/ui/unit_hash.stderr index da276296e0282..050fa55a12bea 100644 --- a/tests/ui/unit_hash.stderr +++ b/tests/ui/unit_hash.stderr @@ -1,5 +1,5 @@ error: this call to `hash` on the unit type will do nothing - --> $DIR/unit_hash.rs:18:23 + --> $DIR/unit_hash.rs:19:23 | LL | Foo::Empty => ().hash(&mut state), | ^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)` @@ -8,7 +8,7 @@ LL | Foo::Empty => ().hash(&mut state), = note: the implementation of `Hash` for `()` is a no-op error: this call to `hash` on the unit type will do nothing - --> $DIR/unit_hash.rs:23:5 + --> $DIR/unit_hash.rs:24:5 | LL | res.hash(&mut state); | ^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)` @@ -16,7 +16,7 @@ LL | res.hash(&mut state); = note: the implementation of `Hash` for `()` is a no-op error: this call to `hash` on the unit type will do nothing - --> $DIR/unit_hash.rs:26:5 + --> $DIR/unit_hash.rs:27:5 | LL | do_nothing().hash(&mut state); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)` diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 8402c33a4cd5f..b6f47ae906b73 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -8,8 +8,7 @@ // FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] -#![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index faaeaade9b02b..eb404b7a3de5f 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -8,8 +8,7 @@ // FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] -#![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 7534a65ec9bd5..626c1754fc82c 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> $DIR/wildcard_imports.rs:17:5 + --> $DIR/wildcard_imports.rs:16:5 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` @@ -7,85 +7,85 @@ LL | use crate::fn_mod::*; = note: `-D clippy::wildcard-imports` implied by `-D warnings` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:18:5 + --> $DIR/wildcard_imports.rs:17:5 | LL | use crate::mod_mod::*; | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:19:5 + --> $DIR/wildcard_imports.rs:18:5 | LL | use crate::multi_fn_mod::*; | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:21:5 + --> $DIR/wildcard_imports.rs:20:5 | LL | use crate::struct_mod::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:25:5 + --> $DIR/wildcard_imports.rs:24:5 | LL | use wildcard_imports_helper::inner::inner_for_self_import::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:26:5 + --> $DIR/wildcard_imports.rs:25:5 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:97:13 + --> $DIR/wildcard_imports.rs:96:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:103:75 + --> $DIR/wildcard_imports.rs:102:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:104:13 + --> $DIR/wildcard_imports.rs:103:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:115:20 + --> $DIR/wildcard_imports.rs:114:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:115:30 + --> $DIR/wildcard_imports.rs:114:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:122:13 + --> $DIR/wildcard_imports.rs:121:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:151:9 + --> $DIR/wildcard_imports.rs:150:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:160:9 + --> $DIR/wildcard_imports.rs:159:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:161:9 + --> $DIR/wildcard_imports.rs:160:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:172:13 + --> $DIR/wildcard_imports.rs:171:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:207:17 + --> $DIR/wildcard_imports.rs:206:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:215:13 + --> $DIR/wildcard_imports.rs:214:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:224:17 + --> $DIR/wildcard_imports.rs:223:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:233:13 + --> $DIR/wildcard_imports.rs:232:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:241:13 + --> $DIR/wildcard_imports.rs:240:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` From 70f7c624e442c3049d440cd20f8d77b66580105c Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 4 Apr 2022 21:54:34 -0400 Subject: [PATCH 054/129] Allow more complex expressions in `let_unit_value` --- clippy_lints/src/unit_types/let_unit_value.rs | 10 +++++- clippy_utils/src/visitors.rs | 34 +++++++++++++++++++ tests/ui/cast_alignment.rs | 6 ++-- tests/ui/let_unit.fixed | 25 ++++++++++++++ tests/ui/let_unit.rs | 25 ++++++++++++++ tests/ui/let_unit.stderr | 31 ++++++++++++++++- tests/ui/or_then_unwrap.fixed | 2 +- tests/ui/or_then_unwrap.rs | 2 +- tests/ui/panicking_macros.rs | 2 +- 9 files changed, 129 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 39352b3ee4769..96858fc7f2749 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::visitors::for_each_value_source; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind}; @@ -11,11 +12,18 @@ use super::LET_UNIT_VALUE; pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init && !local.pat.span.from_expansion() && !in_external_macro(cx.sess(), stmt.span) && cx.typeck_results().pat_ty(local.pat).is_unit() { - if local.init.map_or(false, |e| needs_inferred_result_ty(cx, e)) { + let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) { + ControlFlow::Continue(()) + } else { + ControlFlow::Break(()) + }).is_continue(); + + if needs_inferred { if !matches!(local.pat.kind, PatKind::Wild) { span_lint_and_then( cx, diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 3db64b2535398..c00bc2bd213f9 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,4 +1,5 @@ use crate::path_to_local_id; +use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor}; @@ -402,3 +403,36 @@ pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) v.visit_expr(e); v.found_unsafe } + +/// Runs the given function for each sub-expression producing the final value consumed by the parent +/// of the give expression. +/// +/// e.g. for the following expression +/// ```rust,ignore +/// if foo { +/// f(0) +/// } else { +/// 1 + 1 +/// } +/// ``` +/// this will pass both `f(0)` and `1+1` to the given function. +pub fn for_each_value_source<'tcx, B>( + e: &'tcx Expr<'tcx>, + f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, +) -> ControlFlow { + match e.kind { + ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f), + ExprKind::Match(_, arms, _) => { + for arm in arms { + for_each_value_source(arm.body, f)?; + } + ControlFlow::Continue(()) + }, + ExprKind::If(_, if_expr, Some(else_expr)) => { + for_each_value_source(if_expr, f)?; + for_each_value_source(else_expr, f) + }, + ExprKind::DropTemps(e) => for_each_value_source(e, f), + _ => f(e), + } +} diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_alignment.rs index e4e7290a30e9e..95bb883df1bf1 100644 --- a/tests/ui/cast_alignment.rs +++ b/tests/ui/cast_alignment.rs @@ -44,8 +44,8 @@ fn main() { let _ = core::ptr::read_unaligned(ptr as *const u16); let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16); let ptr = &mut data as *mut [u8; 2] as *mut u8; - let _ = (ptr as *mut u16).write_unaligned(0); - let _ = core::ptr::write_unaligned(ptr as *mut u16, 0); - let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0); + (ptr as *mut u16).write_unaligned(0); + core::ptr::write_unaligned(ptr as *mut u16, 0); + core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0); } } diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index 5fee742b19248..e72b746232551 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -87,4 +87,29 @@ fn _returns_generic() { f4(vec![()]); // Lint f4(vec![()]); // Lint + + // Ok + let _: () = { + let x = 5; + f2(x) + }; + + let _: () = if true { f() } else { f2(0) }; // Ok + let _: () = if true { f() } else { f2(0) }; // Lint + + // Ok + let _: () = match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => f2('x'), + }; + + // Lint + match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => (), + }; } diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index 505e4a7d8fdd5..47ee0a7672479 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -87,4 +87,29 @@ fn _returns_generic() { let _: () = f4(vec![()]); // Lint let x: () = f4(vec![()]); // Lint + + // Ok + let _: () = { + let x = 5; + f2(x) + }; + + let _: () = if true { f() } else { f2(0) }; // Ok + let x: () = if true { f() } else { f2(0) }; // Lint + + // Ok + let _: () = match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => f2('x'), + }; + + // Lint + let _: () = match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => (), + }; } diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index c4a766bb97440..13ec11a6d33e9 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -74,5 +74,34 @@ error: this let-binding has unit value LL | let x: () = f4(vec![()]); // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);` -error: aborting due to 9 previous errors +error: this let-binding has unit value + --> $DIR/let_unit.rs:98:5 + | +LL | let x: () = if true { f() } else { f2(0) }; // Lint + | ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: use a wild (`_`) binding: `_` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:109:5 + | +LL | / let _: () = match Some(0) { +LL | | None => f2(1), +LL | | Some(0) => f(), +LL | | Some(1) => f2(3), +LL | | Some(_) => (), +LL | | }; + | |______^ + | +help: omit the `let` binding + | +LL ~ match Some(0) { +LL + None => f2(1), +LL + Some(0) => f(), +LL + Some(1) => f2(3), +LL + Some(_) => (), +LL + }; + | + +error: aborting due to 11 previous errors diff --git a/tests/ui/or_then_unwrap.fixed b/tests/ui/or_then_unwrap.fixed index 6e0d5a87f6807..844cc4b7a0928 100644 --- a/tests/ui/or_then_unwrap.fixed +++ b/tests/ui/or_then_unwrap.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::or_then_unwrap)] -#![allow(clippy::map_identity)] +#![allow(clippy::map_identity, clippy::let_unit_value)] struct SomeStruct; impl SomeStruct { diff --git a/tests/ui/or_then_unwrap.rs b/tests/ui/or_then_unwrap.rs index e406a71d46d00..1528ef9be964d 100644 --- a/tests/ui/or_then_unwrap.rs +++ b/tests/ui/or_then_unwrap.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::or_then_unwrap)] -#![allow(clippy::map_identity)] +#![allow(clippy::map_identity, clippy::let_unit_value)] struct SomeStruct; impl SomeStruct { diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index 12a0c776ae2fd..041ef17fa6834 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -1,4 +1,4 @@ -#![allow(clippy::assertions_on_constants, clippy::eq_op)] +#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::let_unit_value)] #![feature(inline_const)] #![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] From b592cc659b5891606846ae9333d0d373c4a25dea Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 15 Apr 2022 19:33:16 +0900 Subject: [PATCH 055/129] Fix typo in comment --- clippy_lints/src/matches/needless_match.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 2105a03e03a30..d6c8e472552db 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -83,7 +83,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { return false; } - // Recurrsively check for each `else if let` phrase, + // Recursively check for each `else if let` phrase, if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) { return check_if_let(cx, nested_if_let); } From 6f825246f8b8d8e3afffdf3b9c61c5c9d1e13339 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 15 Apr 2022 16:52:58 +0300 Subject: [PATCH 056/129] clippy: Update full path to `CString` --- clippy_utils/src/paths.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 79e6e92dc0aaf..e5fa6deefc5de 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -23,7 +23,7 @@ pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", " pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; -pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"]; +pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"]; pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; /// Preferably use the diagnostic item `sym::deref_method` where possible From d1a78f3ea5c17ab5fd7239942242dfcd12c4545f Mon Sep 17 00:00:00 2001 From: dlup <93457935+lupd@users.noreply.github.com> Date: Fri, 15 Apr 2022 16:34:44 -0400 Subject: [PATCH 057/129] Fix formatting of `cast_abs_to_unsigned` docs --- clippy_lints/src/casts/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 55c1f085657bb..ee6be2306173c 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -487,6 +487,7 @@ declare_clippy_lint! { /// let y: u32 = x.abs() as u32; /// ``` /// Use instead: + /// ```rust /// let x: i32 = -42; /// let y: u32 = x.unsigned_abs(); /// ``` From 29ef80c78a0f9c58607944e50d3240eee2b7cdc7 Mon Sep 17 00:00:00 2001 From: whodi Date: Fri, 15 Apr 2022 14:25:55 -0400 Subject: [PATCH 058/129] adding spell checking --- .github/ISSUE_TEMPLATE/blank_issue.yml | 2 +- .github/ISSUE_TEMPLATE/false_negative.yml | 2 +- .github/ISSUE_TEMPLATE/false_positive.yml | 2 +- .github/workflows/clippy.yml | 4 ++-- clippy_dev/src/setup/git_hook.rs | 2 +- clippy_dev/src/setup/mod.rs | 4 ++-- clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/empty_structs_with_brackets.rs | 2 +- clippy_lints/src/index_refutable_slice.rs | 2 +- clippy_lints/src/match_result_ok.rs | 4 ++-- clippy_lints/src/matches/match_same_arms.rs | 4 ++-- clippy_lints/src/matches/mod.rs | 4 ++-- clippy_lints/src/matches/needless_match.rs | 2 +- clippy_lints/src/methods/mod.rs | 4 ++-- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/needless_bitwise_bool.rs | 4 ++-- clippy_lints/src/needless_late_init.rs | 6 +++--- clippy_lints/src/octal_escapes.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 8 ++++---- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/transmute/utils.rs | 2 +- clippy_lints/src/types/mod.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 2 +- clippy_lints/src/use_self.rs | 2 +- clippy_lints/src/utils/author.rs | 2 +- clippy_lints/src/utils/internal_lints.rs | 4 ++-- .../src/utils/internal_lints/metadata_collector.rs | 6 +++--- clippy_utils/src/macros.rs | 2 +- clippy_utils/src/source.rs | 2 +- tests/lint_message_convention.rs | 2 +- tests/ui/auxiliary/proc_macro_derive.rs | 2 +- 31 files changed, 46 insertions(+), 46 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/blank_issue.yml b/.github/ISSUE_TEMPLATE/blank_issue.yml index d610e8c7bc478..89884bfc85902 100644 --- a/.github/ISSUE_TEMPLATE/blank_issue.yml +++ b/.github/ISSUE_TEMPLATE/blank_issue.yml @@ -9,7 +9,7 @@ body: attributes: label: Description description: > - Please provide a discription of the issue, along with any information + Please provide a description of the issue, along with any information you feel relevant to replicate it. validations: required: true diff --git a/.github/ISSUE_TEMPLATE/false_negative.yml b/.github/ISSUE_TEMPLATE/false_negative.yml index 9357ccc4f4e76..25e436d30b97d 100644 --- a/.github/ISSUE_TEMPLATE/false_negative.yml +++ b/.github/ISSUE_TEMPLATE/false_negative.yml @@ -23,7 +23,7 @@ body: id: reproducer attributes: label: Reproducer - description: Please provide the code and steps to repoduce the bug + description: Please provide the code and steps to reproduce the bug value: | I tried this code: diff --git a/.github/ISSUE_TEMPLATE/false_positive.yml b/.github/ISSUE_TEMPLATE/false_positive.yml index b7dd400ee7333..561b65c93a7f9 100644 --- a/.github/ISSUE_TEMPLATE/false_positive.yml +++ b/.github/ISSUE_TEMPLATE/false_positive.yml @@ -24,7 +24,7 @@ body: attributes: label: Reproducer description: > - Please provide the code and steps to repoduce the bug together with the + Please provide the code and steps to reproduce the bug together with the output from Clippy. value: | I tried this code: diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index cd83bc9642b60..4292949a02dff 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -6,14 +6,14 @@ on: branches-ignore: - auto - try - # Don't run Clippy tests, when only textfiles were modified + # Don't run Clippy tests, when only text files were modified paths-ignore: - 'COPYRIGHT' - 'LICENSE-*' - '**.md' - '**.txt' pull_request: - # Don't run Clippy tests, when only textfiles were modified + # Don't run Clippy tests, when only text files were modified paths-ignore: - 'COPYRIGHT' - 'LICENSE-*' diff --git a/clippy_dev/src/setup/git_hook.rs b/clippy_dev/src/setup/git_hook.rs index 3fbb77d59235c..ad4b96f7ff347 100644 --- a/clippy_dev/src/setup/git_hook.rs +++ b/clippy_dev/src/setup/git_hook.rs @@ -18,7 +18,7 @@ pub fn install_hook(force_override: bool) { // So a little bit of a funny story. Git on unix requires the pre-commit file // to have the `execute` permission to be set. The Rust functions for modifying - // these flags doesn't seem to work when executed with normal user permissions. + // these flags doesn't seem to work when executed with normal user permissions. // // However, there is a little hack that is also being used by Rust itself in their // setup script. Git saves the `execute` flag when syncing files. This means diff --git a/clippy_dev/src/setup/mod.rs b/clippy_dev/src/setup/mod.rs index a1e4dd103b88b..f691ae4fa45da 100644 --- a/clippy_dev/src/setup/mod.rs +++ b/clippy_dev/src/setup/mod.rs @@ -7,7 +7,7 @@ use std::path::Path; const CLIPPY_DEV_DIR: &str = "clippy_dev"; /// This function verifies that the tool is being executed in the clippy directory. -/// This is useful to ensure that setups only modify Clippys resources. The verification +/// This is useful to ensure that setups only modify Clippy's resources. The verification /// is done by checking that `clippy_dev` is a sub directory of the current directory. /// /// It will print an error message and return `false` if the directory could not be @@ -17,7 +17,7 @@ fn verify_inside_clippy_dir() -> bool { if path.exists() && path.is_dir() { true } else { - eprintln!("error: unable to verify that the working directory is clippys directory"); + eprintln!("error: unable to verify that the working directory is clippy's directory"); false } } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 55c1f085657bb..f05315a7d0cdb 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -270,7 +270,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Casting a function pointer to an integer can have surprising results and can occur - /// accidentally if parantheses are omitted from a function call. If you aren't doing anything + /// accidentally if parentheses are omitted from a function call. If you aren't doing anything /// low-level with function pointers then you can opt-out of casting functions to integers in /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function /// pointer casts in your code. diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs index fdeac8d82557f..8430e7b4c8271 100644 --- a/clippy_lints/src/empty_structs_with_brackets.rs +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -66,7 +66,7 @@ fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Spa } // there might still be field declarations hidden from the AST - // (conditionaly compiled code using #[cfg(..)]) + // (conditionally compiled code using #[cfg(..)]) let Some(braces_span_str) = snippet_opt(cx, braces_span) else { return false; diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 6b62748ffef2e..8a84513b7792f 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -116,7 +116,7 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap LateLintPass<'tcx> for MatchResultOk { if_chain! { if let ExprKind::MethodCall(ok_path, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation - if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; + if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized; if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index b8591fe0db0ad..9b7344fb8b0b2 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .map(|a| NormalizedPat::from_pat(cx, &arena, a.pat)) .collect(); - // The furthast forwards a pattern can move without semantic changes + // The furthest forwards a pattern can move without semantic changes let forwards_blocking_idxs: Vec<_> = normalized_pats .iter() .enumerate() @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { }) .collect(); - // The furthast backwards a pattern can move without semantic changes + // The furthest backwards a pattern can move without semantic changes let backwards_blocking_idxs: Vec<_> = normalized_pats .iter() .enumerate() diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 854cf06ed8198..401ecef460c35 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -7,7 +7,7 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, SpanData, SyntaxContext}; -mod infalliable_detructuring_match; +mod infallible_destructuring_match; mod match_as_ref; mod match_bool; mod match_like_matches; @@ -694,7 +694,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { - self.infallible_destructuring_match_linted |= infalliable_detructuring_match::check(cx, local); + self.infallible_destructuring_match_linted |= infallible_destructuring_match::check(cx, local); } fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index d6c8e472552db..0abe6ddda65a2 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -118,7 +118,7 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { } /// Manually check for coercion casting by checking if the type of the match operand or let expr -/// differs with the assigned local variable or the funtion return type. +/// differs with the assigned local variable or the function return type. fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) { match p_node { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f1a74220e6826..70a2aaf78a077 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1266,7 +1266,7 @@ declare_clippy_lint! { #[clippy::version = "1.55.0"] pub EXTEND_WITH_DRAIN, perf, - "using vec.append(&mut vec) to move the full range of a vecor to another" + "using vec.append(&mut vec) to move the full range of a vector to another" } declare_clippy_lint! { @@ -2100,7 +2100,7 @@ declare_clippy_lint! { /// using `.collect::()` over `.collect::>().join("")` /// will prevent loop unrolling and will result in a negative performance impact. /// - /// Additionlly, differences have been observed between aarch64 and x86_64 assembly output, + /// Additionally, differences have been observed between aarch64 and x86_64 assembly output, /// with aarch64 tending to producing faster assembly in more cases when using `.collect::()` #[clippy::version = "1.61.0"] pub UNNECESSARY_JOIN, diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index ac2f16b49e3f1..6e6ad1ebbceda 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -44,7 +44,7 @@ declare_clippy_lint! { /// pub struct PubBaz; /// impl PubBaz { /// fn private() {} // ok - /// pub fn not_ptrivate() {} // missing #[inline] + /// pub fn not_private() {} // missing #[inline] /// } /// /// impl Bar for PubBaz { diff --git a/clippy_lints/src/needless_bitwise_bool.rs b/clippy_lints/src/needless_bitwise_bool.rs index a8a8d174a823e..95395e2e136d9 100644 --- a/clippy_lints/src/needless_bitwise_bool.rs +++ b/clippy_lints/src/needless_bitwise_bool.rs @@ -53,7 +53,7 @@ fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } -fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +fn suggesstion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let ExprKind::Binary(ref op, left, right) = expr.kind { if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) { let op_snippet = match op.node { @@ -75,7 +75,7 @@ impl LateLintPass<'_> for NeedlessBitwiseBool { expr.span, "use of bitwise operator instead of lazy operator between booleans", |diag| { - if let Some(sugg) = suggession_snippet(cx, expr) { + if let Some(sugg) = suggesstion_snippet(cx, expr) { diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 9957afcbf04aa..bbcf7e9e3789c 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -240,7 +240,7 @@ fn check<'tcx>( cx, NEEDLESS_LATE_INIT, local_stmt.span, - "unneeded late initalization", + "unneeded late initialization", |diag| { diag.tool_only_span_suggestion( local_stmt.span, @@ -265,7 +265,7 @@ fn check<'tcx>( cx, NEEDLESS_LATE_INIT, local_stmt.span, - "unneeded late initalization", + "unneeded late initialization", |diag| { diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); @@ -296,7 +296,7 @@ fn check<'tcx>( cx, NEEDLESS_LATE_INIT, local_stmt.span, - "unneeded late initalization", + "unneeded late initialization", |diag| { diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index c19cea661042d..e8532db4f711d 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// /// ### Known problems /// The actual meaning can be the intended one. `\x00` can be used in these - /// cases to be unambigious. + /// cases to be unambiguous. /// /// The lint does not trigger for format strings in `print!()`, `write!()` /// and friends since the string is already preprocessed when Clippy lints diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index c9f807f2aa3aa..ea5a8f0858b66 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -78,7 +78,7 @@ fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { /// A struct containing information about occurrences of the /// `if let Some(..) = .. else` construct that this lint detects. -struct OptionIfLetElseOccurence { +struct OptionIfLetElseOccurrence { option: String, method_sugg: String, some_expr: String, @@ -100,9 +100,9 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo } /// If this expression is the option if let/else construct we're detecting, then -/// this function returns an `OptionIfLetElseOccurence` struct with details if +/// this function returns an `OptionIfLetElseOccurrence` struct with details if /// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { +fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if_chain! { if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly if !in_constant(cx, expr.hir_id); @@ -154,7 +154,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> } } } - Some(OptionIfLetElseOccurence { + Some(OptionIfLetElseOccurrence { option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), method_sugg: method_sugg.to_string(), some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")), diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index b5dd27ff80de4..c4c1aa11004ac 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -550,7 +550,7 @@ fn ident_difference_expr_with_base_location( // IdentIter, then the output of this function will be almost always be correct // in practice. // - // If it turns out that problematic cases are more prelavent than we assume, + // If it turns out that problematic cases are more prevalent than we assume, // then we should be able to change this function to do the correct traversal, // without needing to change the rest of the code. diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 0e07a0b0392eb..0cbf5ccefa6d8 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -87,7 +87,7 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx> let res = check.do_check(&fn_ctxt); // do_check's documentation says that it might return Ok and create - // errors in the fcx instead of returing Err in some cases. Those cases + // errors in the fcx instead of returning Err in some cases. Those cases // should be filtered out before getting here. assert!( !fn_ctxt.errors_reported_since_creation(), diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 67cc891331896..b1b2addb9a160 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -432,7 +432,7 @@ impl Types { fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) { // Ignore functions in trait implementations as they are usually forced by the trait definition. // - // FIXME: idially we would like to warn *if the compicated type can be simplified*, but it's hard to + // FIXME: ideally we would like to warn *if the complicated type can be simplified*, but it's hard to // check. if context.is_in_trait_impl { return; diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 790ffe618d71d..ae431aac83b82 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*. /// /// ### Why is this bad? - /// In the example above, `Some` is repeated, which unncessarily complicates the pattern. + /// In the example above, `Some` is repeated, which unnecessarily complicates the pattern. /// /// ### Example /// ```rust diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index f8e1021af0ea1..138f8bccb3f57 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// /// ### Known problems /// - Unaddressed false negative in fn bodies of trait implementations - /// - False positive with assotiated types in traits (#4140) + /// - False positive with associated types in traits (#4140) /// /// ### Example /// ```rust diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index d23c85c033b2f..ff5be825b7817 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -70,7 +70,7 @@ macro_rules! bind { }; } -/// Transforms the given `Option` varibles into `OptionPat>`. +/// Transforms the given `Option` variables into `OptionPat>`. /// This displays as `Some($name)` or `None` when printed. The name of the inner binding /// is set to the name of the variable passed to the macro. macro_rules! opt_bind { diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 25d74b8c49939..0e8f40e92101a 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -292,7 +292,7 @@ declare_clippy_lint! { /// Checks for unnecessary conversion from Symbol to a string. /// /// ### Why is this bad? - /// It's faster use symbols directly intead of strings. + /// It's faster use symbols directly instead of strings. /// /// ### Example /// Bad: @@ -823,7 +823,7 @@ fn suggest_note( cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.span, - "this call is collspible", + "this call is collapsible", "collapse into", format!( "span_lint_and_note({}, {}, {}, {}, {}, {})", diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index ca03b8010dd82..526bb2f7e064b 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -117,7 +117,7 @@ const APPLICABILITY_NAME_INDEX: usize = 2; /// This applicability will be set for unresolved applicability values. const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved"; /// The version that will be displayed if none has been defined -const VERION_DEFAULT_STR: &str = "Unknown"; +const VERSION_DEFAULT_STR: &str = "Unknown"; declare_clippy_lint! { /// ### What it does @@ -571,7 +571,7 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option { fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String { extract_clippy_version_value(cx, item).map_or_else( - || VERION_DEFAULT_STR.to_string(), + || VERSION_DEFAULT_STR.to_string(), |version| version.as_str().to_string(), ) } @@ -872,7 +872,7 @@ impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> { self.suggestion_count += 2; } - /// Checks if the suggestions include multiple spanns + /// Checks if the suggestions include multiple spans fn is_multi_part(&self) -> bool { self.suggestion_count > 1 } diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index e7d4c5a49521d..a268e339bb130 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -367,7 +367,7 @@ impl<'tcx> FormatArgsExpn<'tcx> { expr_visitor_no_bodies(|e| { // if we're still inside of the macro definition... if e.span.ctxt() == expr.span.ctxt() { - // ArgumnetV1::new_() + // ArgumentV1::new_() if_chain! { if let ExprKind::Call(callee, [val]) = e.kind; if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index beed73268037e..c69a3d8d2a15e 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -108,7 +108,7 @@ pub fn is_present_in_source(cx: &T, span: Span) -> bool { true } -/// Returns the positon just before rarrow +/// Returns the position just before rarrow /// /// ```rust,ignore /// fn into(self) -> () {} diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index dc82ba891fb1f..dd1d441203600 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -66,7 +66,7 @@ fn lint_message_convention() { // make sure that lint messages: // * are not capitalized - // * don't have puncuation at the end of the last sentence + // * don't have punctuation at the end of the last sentence // these directories have interesting tests let test_dirs = ["ui", "ui-cargo", "ui-internal", "ui-toml"] diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 4b7b7fec78fe8..ed7b17651e675 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -13,7 +13,7 @@ use proc_macro::{quote, TokenStream}; #[proc_macro_derive(DeriveSomething)] pub fn derive(_: TokenStream) -> TokenStream { - // Shound not trigger `used_underscore_binding` + // Should not trigger `used_underscore_binding` let _inside_derive = 1; assert_eq!(_inside_derive, _inside_derive); From fb1934b63ff692870e9793ea60624d3ab2736d05 Mon Sep 17 00:00:00 2001 From: whodi Date: Fri, 15 Apr 2022 14:33:53 -0400 Subject: [PATCH 059/129] infallible <> infalliable in filename --- ...ble_detructuring_match.rs => infallible_detructuring_match.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clippy_lints/src/matches/{infalliable_detructuring_match.rs => infallible_detructuring_match.rs} (100%) diff --git a/clippy_lints/src/matches/infalliable_detructuring_match.rs b/clippy_lints/src/matches/infallible_detructuring_match.rs similarity index 100% rename from clippy_lints/src/matches/infalliable_detructuring_match.rs rename to clippy_lints/src/matches/infallible_detructuring_match.rs From 2be7ad5b39b182e7e2ce6cbe2c49a259288cd464 Mon Sep 17 00:00:00 2001 From: whodi Date: Fri, 15 Apr 2022 15:00:44 -0400 Subject: [PATCH 060/129] initialization misspell --- clippy_dev/src/setup/git_hook.rs | 2 +- ...ch.rs => infallible_destructuring_match.rs} | 0 clippy_lints/src/types/mod.rs | 4 ++-- tests/ui/needless_late_init.stderr | 18 +++++++++--------- tests/ui/needless_late_init_fixable.stderr | 12 ++++++------ 5 files changed, 18 insertions(+), 18 deletions(-) rename clippy_lints/src/matches/{infallible_detructuring_match.rs => infallible_destructuring_match.rs} (100%) diff --git a/clippy_dev/src/setup/git_hook.rs b/clippy_dev/src/setup/git_hook.rs index ad4b96f7ff347..3fbb77d59235c 100644 --- a/clippy_dev/src/setup/git_hook.rs +++ b/clippy_dev/src/setup/git_hook.rs @@ -18,7 +18,7 @@ pub fn install_hook(force_override: bool) { // So a little bit of a funny story. Git on unix requires the pre-commit file // to have the `execute` permission to be set. The Rust functions for modifying - // these flags doesn't seem to work when executed with normal user permissions. + // these flags doesn't seem to work when executed with normal user permissions. // // However, there is a little hack that is also being used by Rust itself in their // setup script. Git saves the `execute` flag when syncing files. This means diff --git a/clippy_lints/src/matches/infallible_detructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs similarity index 100% rename from clippy_lints/src/matches/infallible_detructuring_match.rs rename to clippy_lints/src/matches/infallible_destructuring_match.rs diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index b1b2addb9a160..353a6f6b899ea 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -432,8 +432,8 @@ impl Types { fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) { // Ignore functions in trait implementations as they are usually forced by the trait definition. // - // FIXME: ideally we would like to warn *if the complicated type can be simplified*, but it's hard to - // check. + // FIXME: ideally we would like to warn *if the complicated type can be simplified*, but it's hard + // to check. if context.is_in_trait_impl { return; } diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index 9ef8bb23df6a6..124fe5592c31c 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,4 +1,4 @@ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init.rs:5:5 | LL | let a; @@ -20,7 +20,7 @@ help: add a semicolon after the `match` expression LL | }; | + -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init.rs:14:5 | LL | let b; @@ -41,7 +41,7 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init.rs:21:5 | LL | let c; @@ -62,7 +62,7 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init.rs:28:5 | LL | let d; @@ -83,7 +83,7 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init.rs:36:5 | LL | let e; @@ -104,7 +104,7 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init.rs:43:5 | LL | let f; @@ -120,7 +120,7 @@ LL - 1 => f = "three", LL + 1 => "three", | -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init.rs:49:5 | LL | let g: usize; @@ -140,7 +140,7 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init.rs:64:5 | LL | let a; @@ -161,7 +161,7 @@ help: add a semicolon after the `match` expression LL | }; | + -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init.rs:81:5 | LL | let a; diff --git a/tests/ui/needless_late_init_fixable.stderr b/tests/ui/needless_late_init_fixable.stderr index 3f3d4f5286b2b..34dccc2cec8ba 100644 --- a/tests/ui/needless_late_init_fixable.stderr +++ b/tests/ui/needless_late_init_fixable.stderr @@ -1,4 +1,4 @@ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:6:5 | LL | let a; @@ -10,7 +10,7 @@ help: declare `a` here LL | let a = "zero"; | ~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:9:5 | LL | let b; @@ -21,7 +21,7 @@ help: declare `b` here LL | let b = 1; | ~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:10:5 | LL | let c; @@ -32,7 +32,7 @@ help: declare `c` here LL | let c = 2; | ~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:14:5 | LL | let d: usize; @@ -43,7 +43,7 @@ help: declare `d` here LL | let d: usize = 1; | ~~~~~~~~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:17:5 | LL | let mut e; @@ -54,7 +54,7 @@ help: declare `e` here LL | let mut e = 1; | ~~~~~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:21:5 | LL | let h; From 73b7cb9c49cfa505da6e809453a1556df0b0d67b Mon Sep 17 00:00:00 2001 From: whodi Date: Fri, 15 Apr 2022 15:20:54 -0400 Subject: [PATCH 061/129] shound -> should --- lintcheck/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 16cf728e3cb0f..491527c95f809 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -393,7 +393,7 @@ struct LintcheckConfig { lintcheck_results_path: PathBuf, /// whether to just run --fix and not collect all the warnings fix: bool, - /// A list of lint that this lintcheck run shound focus on + /// A list of lints that this lintcheck run should focus on lint_filter: Vec, /// Indicate if the output should support markdown syntax markdown: bool, From e475dde09772e06a76b52155285ed2b7d001875b Mon Sep 17 00:00:00 2001 From: whodi Date: Fri, 15 Apr 2022 15:28:48 -0400 Subject: [PATCH 062/129] collapsible <> collspible --- tests/ui-internal/collapsible_span_lint_calls.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui-internal/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr index 558d129916078..0852fe65aafd0 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.stderr +++ b/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -29,7 +29,7 @@ LL | | db.help(help_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` -error: this call is collspible +error: this call is collapsible --> $DIR/collapsible_span_lint_calls.rs:45:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { @@ -37,7 +37,7 @@ LL | | db.span_note(expr.span, note_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` -error: this call is collspible +error: this call is collapsible --> $DIR/collapsible_span_lint_calls.rs:48:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { From 4844325faf392beb47c6e6d3231dfbba55f926c2 Mon Sep 17 00:00:00 2001 From: Lily Mara Date: Thu, 14 Apr 2022 16:52:10 -0700 Subject: [PATCH 063/129] Add `await_holding_invalid_type` lint changelog: [`await_holding_invalid_type`] This lint allows users to create a denylist of types which are not allowed to be held across await points. This is essentially a re-implementation of the language-level [`must_not_suspend` lint](https://github.com/rust-lang/rust/issues/83310). That lint has a lot of work still to be done before it will reach Rust stable, and in the meantime there are a lot of types which can trip up developers if they are used improperly. --- CHANGELOG.md | 1 + Cargo.toml | 1 + clippy_lints/src/await_holding_invalid.rs | 165 ++++++++++++++---- clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/lib.rs | 7 +- clippy_lints/src/utils/conf.rs | 2 + tests/compile-test.rs | 4 + .../await_holding_invalid_type.rs | 41 +++++ .../await_holding_invalid_type.stderr | 25 +++ .../await_holding_invalid_type/clippy.toml | 4 + .../toml_unknown_key/conf_unknown_key.stderr | 2 +- 13 files changed, 215 insertions(+), 40 deletions(-) create mode 100644 tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs create mode 100644 tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr create mode 100644 tests/ui-toml/await_holding_invalid_type/clippy.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index 39786c08156dc..7aa4884d1039b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3182,6 +3182,7 @@ Released 2018-09-13 [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops [`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async +[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask diff --git a/Cargo.toml b/Cargo.toml index dd6518d5241b5..1fb532deb9bef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ futures = "0.3" parking_lot = "0.11.2" tokio = { version = "1", features = ["io-util"] } rustc-semver = "1.1" +tracing = "0.1" [build-dependencies] rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 4592ca7274888..36b4f7faa19fc 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,12 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths}; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; -use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::GeneratorInteriorTypeCause; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; +use crate::utils::conf::DisallowedType; + declare_clippy_lint! { /// ### What it does /// Checks for calls to await while holding a non-async-aware MutexGuard. @@ -127,9 +130,73 @@ declare_clippy_lint! { "inside an async function, holding a `RefCell` ref while calling `await`" } -declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]); +declare_clippy_lint! { + /// ### What it does + /// Allows users to configure types which should not be held across `await` + /// suspension points. + /// + /// ### Why is this bad? + /// There are some types which are perfectly "safe" to be used concurrently + /// from a memory access perspective but will cause bugs at runtime if they + /// are held in such a way. + /// + /// ### Known problems + /// + /// ### Example + /// + /// The `tracing` library has types which should not be held across `await` + /// points. + /// + /// ```toml + /// await-holding-invalid-types = [ + /// "tracing::span::Entered", + /// "tracing::span::EnteredSpan", + /// ] + /// ``` + /// + /// ```rust + /// # async fn baz() {} + /// async fn foo() { + /// let _entered = tracing::info_span!("baz").entered(); + /// baz().await; + /// } + /// ``` + #[clippy::version = "1.49.0"] + pub AWAIT_HOLDING_INVALID_TYPE, + suspicious, + "inside an async function, holding a type across an await point which is not safe to be held across an await point" +} + +impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); + +#[derive(Debug)] +pub struct AwaitHolding { + conf_invalid_types: Vec, + def_ids: FxHashMap, +} + +impl AwaitHolding { + pub(crate) fn new(conf_invalid_types: Vec) -> Self { + Self { + conf_invalid_types, + def_ids: FxHashMap::default(), + } + } +} impl LateLintPass<'_> for AwaitHolding { + fn check_crate(&mut self, cx: &LateContext<'_>) { + for conf in &self.conf_invalid_types { + let path = match conf { + DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path, + }; + let segs: Vec<_> = path.split("::").collect(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) { + self.def_ids.insert(id, conf.clone()); + } + } + } + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { use AsyncGeneratorKind::{Block, Closure, Fn}; if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { @@ -137,7 +204,7 @@ impl LateLintPass<'_> for AwaitHolding { hir_id: body.value.hir_id, }; let typeck_results = cx.tcx.typeck_body(body_id); - check_interior_types( + self.check_interior_types( cx, typeck_results.generator_interior_types.as_ref().skip_binder(), body.value.span, @@ -146,46 +213,68 @@ impl LateLintPass<'_> for AwaitHolding { } } -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { - if is_mutex_guard(cx, adt.did()) { - span_lint_and_then( - cx, - AWAIT_HOLDING_LOCK, - ty_cause.span, - "this `MutexGuard` is held across an `await` point", - |diag| { - diag.help( - "consider using an async-aware `Mutex` type or ensuring the \ +impl AwaitHolding { + fn check_interior_types(&self, cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_mutex_guard(cx, adt.did()) { + span_lint_and_then( + cx, + AWAIT_HOLDING_LOCK, + ty_cause.span, + "this `MutexGuard` is held across an `await` point", + |diag| { + diag.help( + "consider using an async-aware `Mutex` type or ensuring the \ `MutexGuard` is dropped before calling await", - ); - diag.span_note( - ty_cause.scope_span.unwrap_or(span), - "these are all the `await` points this lock is held through", - ); - }, - ); - } - if is_refcell_ref(cx, adt.did()) { - span_lint_and_then( - cx, - AWAIT_HOLDING_REFCELL_REF, - ty_cause.span, - "this `RefCell` reference is held across an `await` point", - |diag| { - diag.help("ensure the reference is dropped before calling `await`"); - diag.span_note( - ty_cause.scope_span.unwrap_or(span), - "these are all the `await` points this reference is held through", - ); - }, - ); + ); + diag.span_note( + ty_cause.scope_span.unwrap_or(span), + "these are all the `await` points this lock is held through", + ); + }, + ); + } else if is_refcell_ref(cx, adt.did()) { + span_lint_and_then( + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this `RefCell` reference is held across an `await` point", + |diag| { + diag.help("ensure the reference is dropped before calling `await`"); + diag.span_note( + ty_cause.scope_span.unwrap_or(span), + "these are all the `await` points this reference is held through", + ); + }, + ); + } else if let Some(disallowed) = self.def_ids.get(&adt.did()) { + emit_invalid_type(cx, ty_cause.span, disallowed); + } } } } } +fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) { + let (type_name, reason) = match disallowed { + DisallowedType::Simple(path) => (path, &None), + DisallowedType::WithReason { path, reason } => (path, reason), + }; + + span_lint_and_then( + cx, + AWAIT_HOLDING_INVALID_TYPE, + span, + &format!("`{type_name}` may not be held across an `await` point according to config",), + |diag| { + if let Some(reason) = reason { + diag.note(format!("{reason} (according to clippy.toml)")); + } + }, + ); +} + fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { match_def_path(cx, def_id, &paths::MUTEX_GUARD) || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 2fa5973336596..be57e1038fdfd 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -14,6 +14,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(attrs::DEPRECATED_SEMVER), LintId::of(attrs::MISMATCHED_TARGET_OS), LintId::of(attrs::USELESS_ATTRIBUTE), + LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(bit_mask::BAD_BIT_MASK), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index c608f634291f8..8a97233dc5d5e 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -52,6 +52,7 @@ store.register_lints(&[ attrs::INLINE_ALWAYS, attrs::MISMATCHED_TARGET_OS, attrs::USELESS_ATTRIBUTE, + await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE, await_holding_invalid::AWAIT_HOLDING_LOCK, await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, bit_mask::BAD_BIT_MASK, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 82f45b5fd58b9..7a881bfe39912 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -5,6 +5,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![ LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), + LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(casts::CAST_ABS_TO_UNSIGNED), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 88e8a0cc2af04..e31f71fe5da97 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -515,7 +515,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: } store.register_late_pass(|| Box::new(utils::author::Author)); - store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding)); + let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); + store.register_late_pass(move || { + Box::new(await_holding_invalid::AwaitHolding::new( + await_holding_invalid_types.clone(), + )) + }); store.register_late_pass(|| Box::new(serde_api::SerdeApi)); let vec_box_size_threshold = conf.vec_box_size_threshold; let type_complexity_threshold = conf.type_complexity_threshold; diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 271c3a3dd181c..e6abe499b430d 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -310,6 +310,8 @@ define_Conf! { /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed. /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. (max_suggested_slice_pattern_length: u64 = 3), + /// Lint: AWAIT_HOLDING_INVALID_TYPE + (await_holding_invalid_types: Vec = Vec::new()), } /// Search for the configuration file. diff --git a/tests/compile-test.rs b/tests/compile-test.rs index c9710e3db8e8d..f437553bca18c 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -35,6 +35,7 @@ static TEST_DEPENDENCIES: &[&str] = &[ "tokio", "parking_lot", "rustc_semver", + "tracing", ]; // Test dependencies may need an `extern crate` here to ensure that they show up @@ -59,6 +60,9 @@ extern crate rustc_semver; extern crate syn; #[allow(unused_extern_crates)] extern crate tokio; +#[allow(unused_extern_crates, unused_imports)] +#[macro_use] +extern crate tracing; /// Produces a string with an `--extern` flag for all UI test crate /// dependencies. diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs new file mode 100644 index 0000000000000..fbef5c4564b1b --- /dev/null +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs @@ -0,0 +1,41 @@ +#![warn(clippy::await_holding_invalid_type)] +use std::net::Ipv4Addr; + +async fn bad() -> u32 { + let _x = String::from("hello"); + baz().await +} + +async fn bad_reason() -> u32 { + let _x = Ipv4Addr::new(127, 0, 0, 1); + baz().await +} + +async fn good() -> u32 { + { + let _x = String::from("hi!"); + let _y = Ipv4Addr::new(127, 0, 0, 1); + } + baz().await; + let _x = String::from("hi!"); + 47 +} + +async fn baz() -> u32 { + 42 +} + +#[allow(clippy::manual_async_fn)] +fn block_bad() -> impl std::future::Future { + async move { + let _x = String::from("hi!"); + baz().await + } +} + +fn main() { + good(); + bad(); + bad_reason(); + block_bad(); +} diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr new file mode 100644 index 0000000000000..eebdd624d2431 --- /dev/null +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -0,0 +1,25 @@ +error: `std::string::String` may not be held across an `await` point according to config + --> $DIR/await_holding_invalid_type.rs:5:9 + | +LL | let _x = String::from("hello"); + | ^^ + | + = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings` + = note: strings are bad (according to clippy.toml) + +error: `std::net::Ipv4Addr` may not be held across an `await` point according to config + --> $DIR/await_holding_invalid_type.rs:10:9 + | +LL | let _x = Ipv4Addr::new(127, 0, 0, 1); + | ^^ + +error: `std::string::String` may not be held across an `await` point according to config + --> $DIR/await_holding_invalid_type.rs:31:13 + | +LL | let _x = String::from("hi!"); + | ^^ + | + = note: strings are bad (according to clippy.toml) + +error: aborting due to 3 previous errors + diff --git a/tests/ui-toml/await_holding_invalid_type/clippy.toml b/tests/ui-toml/await_holding_invalid_type/clippy.toml new file mode 100644 index 0000000000000..79990096b84e0 --- /dev/null +++ b/tests/ui-toml/await_holding_invalid_type/clippy.toml @@ -0,0 +1,4 @@ +await-holding-invalid-types = [ + { path = "std::string::String", reason = "strings are bad" }, + "std::net::Ipv4Addr", +] diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 00ddbd608a7c7..f60afb8d4e382 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `third-party` at line 5 column 1 error: aborting due to previous error From 3cd8b5a5ef81b43434f1fd0e3574263a286d152a Mon Sep 17 00:00:00 2001 From: Lily Mara Date: Fri, 15 Apr 2022 14:45:58 -0700 Subject: [PATCH 064/129] fixup! Add `await_holding_invalid_type` lint --- Cargo.toml | 1 - tests/compile-test.rs | 4 ---- 2 files changed, 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1fb532deb9bef..dd6518d5241b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,6 @@ futures = "0.3" parking_lot = "0.11.2" tokio = { version = "1", features = ["io-util"] } rustc-semver = "1.1" -tracing = "0.1" [build-dependencies] rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } diff --git a/tests/compile-test.rs b/tests/compile-test.rs index f437553bca18c..c9710e3db8e8d 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -35,7 +35,6 @@ static TEST_DEPENDENCIES: &[&str] = &[ "tokio", "parking_lot", "rustc_semver", - "tracing", ]; // Test dependencies may need an `extern crate` here to ensure that they show up @@ -60,9 +59,6 @@ extern crate rustc_semver; extern crate syn; #[allow(unused_extern_crates)] extern crate tokio; -#[allow(unused_extern_crates, unused_imports)] -#[macro_use] -extern crate tracing; /// Produces a string with an `--extern` flag for all UI test crate /// dependencies. From 534419282e94e618054815e643932f6f39e48000 Mon Sep 17 00:00:00 2001 From: Lily Mara Date: Fri, 15 Apr 2022 14:54:19 -0700 Subject: [PATCH 065/129] fixup! Add `await_holding_invalid_type` lint --- clippy_lints/src/await_holding_invalid.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 36b4f7faa19fc..51dd5f4937a4f 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -144,21 +144,21 @@ declare_clippy_lint! { /// /// ### Example /// - /// The `tracing` library has types which should not be held across `await` - /// points. + /// Strings are, of course, safe to hold across await points. This example + /// exists only to show how this lint might be configured for third-party + /// crates. /// /// ```toml /// await-holding-invalid-types = [ - /// "tracing::span::Entered", - /// "tracing::span::EnteredSpan", + /// "std::string::String", /// ] /// ``` /// /// ```rust /// # async fn baz() {} /// async fn foo() { - /// let _entered = tracing::info_span!("baz").entered(); - /// baz().await; + /// let _x = String::from("hello!"); + /// baz().await; // Lint violation /// } /// ``` #[clippy::version = "1.49.0"] From 5b35bd9c3e2c4c05304ad54a1ba4164e2740a7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 16 Apr 2022 00:56:28 +0200 Subject: [PATCH 066/129] lintcheck: fix --fix looks like --allow-no-vcs does no longer exist(?) --- lintcheck/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 491527c95f809..816efbdaedf36 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -306,7 +306,7 @@ impl Crate { let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); let mut args = if fix { - vec!["--fix", "--allow-no-vcs", "--"] + vec!["--fix", "--"] } else { vec!["--", "--message-format=json", "--"] }; From af721036af4e0a0cadd58962a0826dac0880cf45 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 13 Apr 2022 23:14:35 +0200 Subject: [PATCH 067/129] Changelog for Rust 1.61 :octopus: --- CHANGELOG.md | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef21dbac820e7..fe696ef4f3656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,108 @@ document. ## Unreleased / In Rust Nightly -[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master) +[d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master) + +## Rust 1.61 (beta) + +Current beta, released 2022-05-19 + +[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481) + +### New Lints + +* [`only_used_in_recursion`] + [#8422](https://github.com/rust-lang/rust-clippy/pull/8422) +* [`cast_enum_truncation`] + [#8381](https://github.com/rust-lang/rust-clippy/pull/8381) +* [`missing_spin_loop`] + [#8174](https://github.com/rust-lang/rust-clippy/pull/8174) +* [`deref_by_slicing`] + [#8218](https://github.com/rust-lang/rust-clippy/pull/8218) +* [`needless_match`] + [#8471](https://github.com/rust-lang/rust-clippy/pull/8471) +* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`) + [#8504](https://github.com/rust-lang/rust-clippy/pull/8504) +* [`print_in_format_impl`] + [#8253](https://github.com/rust-lang/rust-clippy/pull/8253) +* [`unnecessary_find_map`] + [#8489](https://github.com/rust-lang/rust-clippy/pull/8489) +* [`or_then_unwrap`] + [#8561](https://github.com/rust-lang/rust-clippy/pull/8561) +* [`unnecessary_join`] + [#8579](https://github.com/rust-lang/rust-clippy/pull/8579) +* [`iter_with_drain`] + [#8483](https://github.com/rust-lang/rust-clippy/pull/8483) +* [`cast_enum_constructor`] + [#8562](https://github.com/rust-lang/rust-clippy/pull/8562) +* [`cast_slice_different_sizes`] + [#8445](https://github.com/rust-lang/rust-clippy/pull/8445) + +### Moves and Deprecations + +* Moved [`transmute_undefined_repr`] to `nursery` (now allow-by-default) + [#8432](https://github.com/rust-lang/rust-clippy/pull/8432) +* Moved [`try_err`] to `restriction` + [#8544](https://github.com/rust-lang/rust-clippy/pull/8544) +* Move [`iter_with_drain`] to `nursery` + [#8541](https://github.com/rust-lang/rust-clippy/pull/8541) +* Renamed `to_string_in_display` to [`recursive_format_impl`] + [#8188](https://github.com/rust-lang/rust-clippy/pull/8188) + +### Enhancements + +* [`dbg_macro`]: The lint level can now be set with crate attributes and works inside macros + [#8411](https://github.com/rust-lang/rust-clippy/pull/8411) +* [`ptr_as_ptr`]: Now works inside macros + [#8442](https://github.com/rust-lang/rust-clippy/pull/8442) +* [`use_self`]: Now works for variants in match expressions + [#8456](https://github.com/rust-lang/rust-clippy/pull/8456) +* [`await_holding_lock`]: Now lints for `parking_lot::{Mutex, RwLock}` + [#8419](https://github.com/rust-lang/rust-clippy/pull/8419) +* [`recursive_format_impl`]: Now checks for format calls on `self` + [#8188](https://github.com/rust-lang/rust-clippy/pull/8188) + +### False Positive Fixes + +* [`new_without_default`]: No longer lints for `new()` methods with `#[doc(hidden)]` + [#8472](https://github.com/rust-lang/rust-clippy/pull/8472) +* [`transmute_undefined_repr`]: No longer lints for single field structs with `#[repr(C)]`, + generic parameters, wide pointers, unions, tuples and allow several forms of type erasure + [#8425](https://github.com/rust-lang/rust-clippy/pull/8425) + [#8553](https://github.com/rust-lang/rust-clippy/pull/8553) + [#8440](https://github.com/rust-lang/rust-clippy/pull/8440) + [#8547](https://github.com/rust-lang/rust-clippy/pull/8547) +* [`match_single_binding`], [`match_same_arms`], [`match_as_ref`], [`match_bool`]: No longer + lint `match` expressions with `cfg`ed arms + [#8443](https://github.com/rust-lang/rust-clippy/pull/8443) +* [`single_component_path_imports`]: No longer lint on macros + [#8537](https://github.com/rust-lang/rust-clippy/pull/8537) +* [`ptr_arg`]: Allow `&mut` arguments for `Cow<_>` + [#8552](https://github.com/rust-lang/rust-clippy/pull/8552) +* [`needless_borrow`]: No longer lints for method calls + [#8441](https://github.com/rust-lang/rust-clippy/pull/8441) +* [`match_same_arms`]: Now ensures that interposing arm patterns don't overlap + [#8232](https://github.com/rust-lang/rust-clippy/pull/8232) +* [`default_trait_access`]: Now allows `Default::default` in update expressions + [#8433](https://github.com/rust-lang/rust-clippy/pull/8433) + +### Suggestion Fixes/Improvements + +* [`redundant_slicing`]: Fixed suggestion for a method calls + [#8218](https://github.com/rust-lang/rust-clippy/pull/8218) +* [`map_flatten`]: Long suggestions will now be split up into two help messages + [#8520](https://github.com/rust-lang/rust-clippy/pull/8520) +* [`unnecessary_lazy_evaluations`]: Now shows suggestions for longer code snippets + [#8543](https://github.com/rust-lang/rust-clippy/pull/8543) +* [`unnecessary_sort_by`]: Now suggests `Reverse` including the path + [#8462](https://github.com/rust-lang/rust-clippy/pull/8462) +* [`search_is_some`]: More suggestions are now `MachineApplicable` + [#8536](https://github.com/rust-lang/rust-clippy/pull/8536) + +### Documentation Improvements + +* [`new_without_default`]: Document `pub` requirement for the struct and fields + [#8429](https://github.com/rust-lang/rust-clippy/pull/8429) ## Rust 1.60 From e4110cf6333bcf6174b9277669a9b71aede2e67e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 1 Apr 2022 19:18:10 +0200 Subject: [PATCH 068/129] Bless clippy. --- tests/ui/unused_unit.stderr | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index 02038b5fb6b5a..0d2cb77855be1 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,8 +1,8 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:28 + --> $DIR/unused_unit.rs:19:58 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () - | ^^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` | note: the lint level is defined here --> $DIR/unused_unit.rs:12:9 @@ -11,16 +11,16 @@ LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:20:18 + --> $DIR/unused_unit.rs:19:28 | -LL | where G: Fn() -> () { - | ^^^^^^ help: remove the `-> ()` +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:19:58 + --> $DIR/unused_unit.rs:20:18 | -LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () - | ^^^^^^ help: remove the `-> ()` +LL | where G: Fn() -> () { + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type --> $DIR/unused_unit.rs:21:26 From 18a44116b866ebf5171b16d917c09dfd44a8fd36 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 15 Apr 2022 19:27:53 +0200 Subject: [PATCH 069/129] Stop using CRATE_DEF_INDEX. `CRATE_DEF_ID` and `CrateNum::as_def_id` are almost always what we want. --- clippy_lints/src/missing_doc.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index fc0483a929a7a..5816a95dcebff 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -10,7 +10,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::ast; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty; +use rustc_middle::ty::{self, DefIdTree}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::source_map::Span; @@ -114,8 +114,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { hir::ItemKind::Fn(..) => { // ignore main() if it.ident.name == sym::main { - let def_key = cx.tcx.hir().def_key(it.def_id); - if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) { + let at_root = cx.tcx.local_parent(it.def_id) == Some(CRATE_DEF_ID); + if at_root { return; } } From ccedc64e3abc53795106d2fba027f59c06905eb4 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 15 Apr 2022 11:37:58 +0200 Subject: [PATCH 070/129] Add `#[clippy::print_hir]` attribute for debugging --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/utils/dump_hir.rs | 55 ++ clippy_lints/src/utils/inspector.rs | 555 ------------------ .../internal_lints/metadata_collector.rs | 2 +- clippy_lints/src/utils/mod.rs | 2 +- doc/adding_lints.md | 14 + 6 files changed, 72 insertions(+), 558 deletions(-) create mode 100644 clippy_lints/src/utils/dump_hir.rs delete mode 100644 clippy_lints/src/utils/inspector.rs diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 604bd44c6c59a..c5d02ca278e94 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -501,7 +501,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: { store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); - store.register_late_pass(|| Box::new(utils::inspector::DeepCodeInspector)); store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls)); store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new())); store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle)); @@ -513,6 +512,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl)); } + store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|| Box::new(utils::author::Author)); store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding)); store.register_late_pass(|| Box::new(serde_api::SerdeApi)); diff --git a/clippy_lints/src/utils/dump_hir.rs b/clippy_lints/src/utils/dump_hir.rs new file mode 100644 index 0000000000000..01efc527a8c3a --- /dev/null +++ b/clippy_lints/src/utils/dump_hir.rs @@ -0,0 +1,55 @@ +use clippy_utils::get_attr; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// It formats the attached node with `{:#?}` and writes the result to the + /// standard output. This is intended for debugging. + /// + /// ### Examples + /// ```rs + /// #[clippy::dump] + /// use std::mem; + /// + /// #[clippy::dump] + /// fn foo(input: u32) -> u64 { + /// input as u64 + /// } + /// ``` + pub DUMP_HIR, + internal_warn, + "helper to dump info about code" +} + +declare_lint_pass!(DumpHir => [DUMP_HIR]); + +impl<'tcx> LateLintPass<'tcx> for DumpHir { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if has_attr(cx, item.hir_id()) { + println!("{item:#?}"); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if has_attr(cx, expr.hir_id) { + println!("{expr:#?}"); + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { + match stmt.kind { + hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return, + _ => {}, + } + if has_attr(cx, stmt.hir_id) { + println!("{stmt:#?}"); + } + } +} + +fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { + let attrs = cx.tcx.hir().attrs(hir_id); + get_attr(cx.sess(), attrs, "dump").count() > 0 +} diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs deleted file mode 100644 index dc48ea3f4f99d..0000000000000 --- a/clippy_lints/src/utils/inspector.rs +++ /dev/null @@ -1,555 +0,0 @@ -//! checks for attributes - -use clippy_utils::get_attr; -use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece}; -use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_session::Session; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// Dumps every ast/hir node which has the `#[clippy::dump]` - /// attribute - /// - /// ### Example - /// ```rust,ignore - /// #[clippy::dump] - /// extern crate foo; - /// ``` - /// - /// prints - /// - /// ```text - /// item `foo` - /// visibility inherited from outer item - /// extern crate dylib source: "/path/to/foo.so" - /// ``` - pub DEEP_CODE_INSPECTION, - internal_warn, - "helper to dump info about code" -} - -declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]); - -impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) { - return; - } - print_item(cx, item); - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) { - return; - } - println!("impl item `{}`", item.ident.name); - match item.vis.node { - hir::VisibilityKind::Public => println!("public"), - hir::VisibilityKind::Crate(_) => println!("visible crate wide"), - hir::VisibilityKind::Restricted { path, .. } => println!( - "visible in module `{}`", - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) - ), - hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"), - } - match item.kind { - hir::ImplItemKind::Const(_, body_id) => { - println!("associated constant"); - print_expr(cx, &cx.tcx.hir().body(body_id).value, 1); - }, - hir::ImplItemKind::Fn(..) => println!("method"), - hir::ImplItemKind::TyAlias(_) => println!("associated type"), - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) { - return; - } - print_expr(cx, expr, 0); - } - - fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(arm.hir_id)) { - return; - } - print_pat(cx, arm.pat, 1); - if let Some(ref guard) = arm.guard { - println!("guard:"); - print_guard(cx, guard, 1); - } - println!("body:"); - print_expr(cx, arm.body, 1); - } - - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(stmt.hir_id)) { - return; - } - match stmt.kind { - hir::StmtKind::Local(local) => { - println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id)); - println!("pattern:"); - print_pat(cx, local.pat, 0); - if let Some(e) = local.init { - println!("init expression:"); - print_expr(cx, e, 0); - } - }, - hir::StmtKind::Item(_) => println!("item decl"), - hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0), - } - } -} - -fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { - get_attr(sess, attrs, "dump").count() > 0 -} - -#[allow(clippy::similar_names)] -#[allow(clippy::too_many_lines)] -fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - println!("{}ty: {}", ind, cx.typeck_results().expr_ty(expr)); - println!( - "{}adjustments: {:?}", - ind, - cx.typeck_results().adjustments().get(expr.hir_id) - ); - match expr.kind { - hir::ExprKind::Box(e) => { - println!("{}Box", ind); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Array(v) => { - println!("{}Array", ind); - for e in v { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Call(func, args) => { - println!("{}Call", ind); - println!("{}function:", ind); - print_expr(cx, func, indent + 1); - println!("{}arguments:", ind); - for arg in args { - print_expr(cx, arg, indent + 1); - } - }, - hir::ExprKind::Let(hir::Let { pat, init, ty, .. }) => { - print_pat(cx, pat, indent + 1); - if let Some(ty) = ty { - println!("{} type annotation: {:?}", ind, ty); - } - print_expr(cx, init, indent + 1); - }, - hir::ExprKind::MethodCall(path, args, _) => { - println!("{}MethodCall", ind); - println!("{}method name: {}", ind, path.ident.name); - for arg in args { - print_expr(cx, arg, indent + 1); - } - }, - hir::ExprKind::Tup(v) => { - println!("{}Tup", ind); - for e in v { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Binary(op, lhs, rhs) => { - println!("{}Binary", ind); - println!("{}op: {:?}", ind, op.node); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::Unary(op, inner) => { - println!("{}Unary", ind); - println!("{}op: {:?}", ind, op); - print_expr(cx, inner, indent + 1); - }, - hir::ExprKind::Lit(ref lit) => { - println!("{}Lit", ind); - println!("{}{:?}", ind, lit); - }, - hir::ExprKind::Cast(e, target) => { - println!("{}Cast", ind); - print_expr(cx, e, indent + 1); - println!("{}target type: {:?}", ind, target); - }, - hir::ExprKind::Type(e, target) => { - println!("{}Type", ind); - print_expr(cx, e, indent + 1); - println!("{}target type: {:?}", ind, target); - }, - hir::ExprKind::Loop(..) => { - println!("{}Loop", ind); - }, - hir::ExprKind::If(cond, _, ref else_opt) => { - println!("{}If", ind); - println!("{}condition:", ind); - print_expr(cx, cond, indent + 1); - if let Some(els) = *else_opt { - println!("{}else:", ind); - print_expr(cx, els, indent + 1); - } - }, - hir::ExprKind::Match(cond, _, ref source) => { - println!("{}Match", ind); - println!("{}condition:", ind); - print_expr(cx, cond, indent + 1); - println!("{}source: {:?}", ind, source); - }, - hir::ExprKind::Closure(ref clause, _, _, _, _) => { - println!("{}Closure", ind); - println!("{}clause: {:?}", ind, clause); - }, - hir::ExprKind::Yield(sub, _) => { - println!("{}Yield", ind); - print_expr(cx, sub, indent + 1); - }, - hir::ExprKind::Block(_, _) => { - println!("{}Block", ind); - }, - hir::ExprKind::Assign(lhs, rhs, _) => { - println!("{}Assign", ind); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::AssignOp(ref binop, lhs, rhs) => { - println!("{}AssignOp", ind); - println!("{}op: {:?}", ind, binop.node); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::Field(e, ident) => { - println!("{}Field", ind); - println!("{}field name: {}", ind, ident.name); - println!("{}struct expr:", ind); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Index(arr, idx) => { - println!("{}Index", ind); - println!("{}array expr:", ind); - print_expr(cx, arr, indent + 1); - println!("{}index expr:", ind); - print_expr(cx, idx, indent + 1); - }, - hir::ExprKind::Path(hir::QPath::Resolved(ref ty, path)) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::ExprKind::Path(hir::QPath::TypeRelative(ty, seg)) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - hir::ExprKind::AddrOf(kind, ref muta, e) => { - println!("{}AddrOf", ind); - println!("kind: {:?}", kind); - println!("mutability: {:?}", muta); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Break(_, ref e) => { - println!("{}Break", ind); - if let Some(e) = *e { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Continue(_) => println!("{}Again", ind), - hir::ExprKind::Ret(ref e) => { - println!("{}Ret", ind); - if let Some(e) = *e { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::InlineAsm(asm) => { - println!("{}InlineAsm", ind); - println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template)); - println!("{}options: {:?}", ind, asm.options); - println!("{}operands:", ind); - for (op, _op_sp) in asm.operands { - match op { - hir::InlineAsmOperand::In { expr, .. } - | hir::InlineAsmOperand::InOut { expr, .. } - | hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1), - hir::InlineAsmOperand::Out { expr, .. } => { - if let Some(expr) = expr { - print_expr(cx, expr, indent + 1); - } - }, - hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - print_expr(cx, in_expr, indent + 1); - if let Some(out_expr) = out_expr { - print_expr(cx, out_expr, indent + 1); - } - }, - hir::InlineAsmOperand::Const { anon_const } => { - println!("{}anon_const:", ind); - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - } - } - }, - hir::ExprKind::Struct(path, fields, ref base) => { - println!("{}Struct", ind); - println!("{}path: {:?}", ind, path); - for field in fields { - println!("{}field \"{}\":", ind, field.ident.name); - print_expr(cx, field.expr, indent + 1); - } - if let Some(base) = *base { - println!("{}base:", ind); - print_expr(cx, base, indent + 1); - } - }, - hir::ExprKind::ConstBlock(ref anon_const) => { - println!("{}ConstBlock", ind); - println!("{}anon_const:", ind); - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - hir::ExprKind::Repeat(val, length) => { - println!("{}Repeat", ind); - println!("{}value:", ind); - print_expr(cx, val, indent + 1); - println!("{}repeat count:", ind); - 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); - }, - hir::ExprKind::DropTemps(e) => { - println!("{}DropTemps", ind); - print_expr(cx, e, indent + 1); - }, - } -} - -fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { - let did = item.def_id; - println!("item `{}`", item.ident.name); - match item.vis.node { - hir::VisibilityKind::Public => println!("public"), - hir::VisibilityKind::Crate(_) => println!("visible crate wide"), - hir::VisibilityKind::Restricted { path, .. } => println!( - "visible in module `{}`", - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) - ), - hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"), - } - match item.kind { - hir::ItemKind::ExternCrate(ref _renamed_from) => { - if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(did) { - let source = cx.tcx.used_crate_source(crate_id); - if let Some(ref src) = source.dylib { - println!("extern crate dylib source: {:?}", src.0); - } - if let Some(ref src) = source.rlib { - println!("extern crate rlib source: {:?}", src.0); - } - } else { - println!("weird extern crate without a crate id"); - } - }, - hir::ItemKind::Use(path, ref kind) => println!("{:?}, {:?}", path, kind), - hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)), - hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)), - hir::ItemKind::Fn(..) => { - let item_ty = cx.tcx.type_of(did); - println!("function of type {:#?}", item_ty); - }, - hir::ItemKind::Macro(ref macro_def, _) => { - if macro_def.macro_rules { - println!("macro introduced by `macro_rules!`"); - } else { - println!("macro introduced by `macro`"); - } - }, - hir::ItemKind::Mod(..) => println!("module"), - hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi), - hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm), - hir::ItemKind::TyAlias(..) => { - println!("type alias for {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::OpaqueTy(..) => { - println!("existential type with real type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Enum(..) => { - println!("enum definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Struct(..) => { - println!("struct definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Union(..) => { - println!("union definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Trait(..) => { - println!("trait decl"); - if cx.tcx.trait_is_auto(did.to_def_id()) { - println!("trait is auto"); - } else { - println!("trait is not auto"); - } - }, - hir::ItemKind::TraitAlias(..) => { - println!("trait alias"); - }, - hir::ItemKind::Impl(hir::Impl { - of_trait: Some(ref _trait_ref), - .. - }) => { - println!("trait impl"); - }, - hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) => { - println!("impl"); - }, - } -} - -#[allow(clippy::similar_names)] -#[allow(clippy::too_many_lines)] -fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - match pat.kind { - hir::PatKind::Wild => println!("{}Wild", ind), - hir::PatKind::Binding(ref mode, .., ident, ref inner) => { - println!("{}Binding", ind); - println!("{}mode: {:?}", ind, mode); - println!("{}name: {}", ind, ident.name); - if let Some(inner) = *inner { - println!("{}inner:", ind); - print_pat(cx, inner, indent + 1); - } - }, - hir::PatKind::Or(fields) => { - println!("{}Or", ind); - for field in fields { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Struct(ref path, fields, ignore) => { - println!("{}Struct", ind); - println!( - "{}name: {}", - ind, - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) - ); - println!("{}ignore leftover fields: {}", ind, ignore); - println!("{}fields:", ind); - for field in fields { - println!("{} field name: {}", ind, field.ident.name); - if field.is_shorthand { - println!("{} in shorthand notation", ind); - } - print_pat(cx, field.pat, indent + 1); - } - }, - hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => { - println!("{}TupleStruct", ind); - println!( - "{}path: {}", - ind, - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) - ); - if let Some(dot_position) = opt_dots_position { - println!("{}dot position: {}", ind, dot_position); - } - for field in fields { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Path(hir::QPath::Resolved(ref ty, path)) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::PatKind::Path(hir::QPath::TypeRelative(ty, seg)) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - hir::PatKind::Tuple(pats, opt_dots_position) => { - println!("{}Tuple", ind); - if let Some(dot_position) = opt_dots_position { - println!("{}dot position: {}", ind, dot_position); - } - for field in pats { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Box(inner) => { - println!("{}Box", ind); - print_pat(cx, inner, indent + 1); - }, - hir::PatKind::Ref(inner, ref muta) => { - println!("{}Ref", ind); - println!("{}mutability: {:?}", ind, muta); - print_pat(cx, inner, indent + 1); - }, - hir::PatKind::Lit(e) => { - println!("{}Lit", ind); - print_expr(cx, e, indent + 1); - }, - hir::PatKind::Range(ref l, ref r, ref range_end) => { - println!("{}Range", ind); - if let Some(expr) = l { - print_expr(cx, expr, indent + 1); - } - if let Some(expr) = r { - print_expr(cx, expr, indent + 1); - } - match *range_end { - hir::RangeEnd::Included => println!("{} end included", ind), - hir::RangeEnd::Excluded => println!("{} end excluded", ind), - } - }, - hir::PatKind::Slice(first_pats, ref range, last_pats) => { - println!("{}Slice [a, b, ..i, y, z]", ind); - println!("[a, b]:"); - for pat in first_pats { - print_pat(cx, pat, indent + 1); - } - println!("i:"); - if let Some(pat) = *range { - print_pat(cx, pat, indent + 1); - } - println!("[y, z]:"); - for pat in last_pats { - print_pat(cx, pat, indent + 1); - } - }, - } -} - -fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - match guard { - hir::Guard::If(expr) => { - println!("{}If", ind); - print_expr(cx, expr, indent + 1); - }, - hir::Guard::IfLet(pat, expr) => { - println!("{}IfLet", ind); - print_pat(cx, pat, indent + 1); - print_expr(cx, expr, indent + 1); - }, - } -} diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index ca03b8010dd82..9c75f7864795c 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -33,7 +33,7 @@ use std::path::Path; /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; /// These lints are excluded from the export. -const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"]; +const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"]; /// These groups will be ignored by the lint group matcher. This is useful for collections like /// `clippy::all` const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"]; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index dc385ebacba66..787e9fd982c89 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; +pub mod dump_hir; #[cfg(feature = "internal")] pub mod internal_lints; diff --git a/doc/adding_lints.md b/doc/adding_lints.md index cf16a1d5d3dcf..307cf2f3a9047 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -22,6 +22,7 @@ because that's clearly a non-descriptive name. - [Adding the lint logic](#adding-the-lint-logic) - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv) - [Author lint](#author-lint) + - [Print HIR lint](#print-hir-lint) - [Documentation](#documentation) - [Running rustfmt](#running-rustfmt) - [Debugging](#debugging) @@ -484,6 +485,19 @@ you are implementing your lint. [author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 +## Print HIR lint + +To implement a lint, it's helpful to first understand the internal representation +that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the +[_High-Level Intermediate Representation (HIR)_] of the item, statement, or +expression that the attribute is attached to. To attach the attribute to expressions +you often need to enable `#![feature(stmt_expr_attributes)]`. + +[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_. + +[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html +[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb + ## Documentation The final thing before submitting our PR is to add some documentation to our From b94e24e95feaab72c622da3af48eff60ef85ec25 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 15 Apr 2022 19:33:24 +0900 Subject: [PATCH 071/129] Fix needless_match false positive for if-let when the else block doesn't match to given expr --- clippy_lints/src/matches/needless_match.rs | 2 +- tests/ui/needless_match.fixed | 12 ++++++++++++ tests/ui/needless_match.rs | 12 ++++++++++++ tests/ui/needless_match.stderr | 8 ++++---- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 0abe6ddda65a2..f920ad4651f9d 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -99,7 +99,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if let ExprKind::Path(ref qpath) = ret.kind { return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); } - return true; + return false; } return eq_expr_value(cx, if_let.let_expr, ret); } diff --git a/tests/ui/needless_match.fixed b/tests/ui/needless_match.fixed index 9ccccaa1725a6..b997e5316cf3f 100644 --- a/tests/ui/needless_match.fixed +++ b/tests/ui/needless_match.fixed @@ -80,6 +80,18 @@ fn if_let_option() { } else { None }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) }; +} + +fn if_let_option_result() -> Result<(), ()> { + fn f(x: i32) -> Result, ()> { + Ok(Some(x)) + } + // Don't trigger + let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? }; + Ok(()) } fn if_let_result() { diff --git a/tests/ui/needless_match.rs b/tests/ui/needless_match.rs index d210ecff7f163..90482775a1eeb 100644 --- a/tests/ui/needless_match.rs +++ b/tests/ui/needless_match.rs @@ -103,6 +103,18 @@ fn if_let_option() { } else { None }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) }; +} + +fn if_let_option_result() -> Result<(), ()> { + fn f(x: i32) -> Result, ()> { + Ok(Some(x)) + } + // Don't trigger + let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? }; + Ok(()) } fn if_let_result() { diff --git a/tests/ui/needless_match.stderr b/tests/ui/needless_match.stderr index 34c5226f06057..2d679631c6f0d 100644 --- a/tests/ui/needless_match.stderr +++ b/tests/ui/needless_match.stderr @@ -72,19 +72,19 @@ LL | let _ = if let Some(a) = Some(1) { Some(a) } else { None }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:110:31 + --> $DIR/needless_match.rs:122:31 | LL | let _: Result = if let Err(e) = x { Err(e) } else { x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:111:31 + --> $DIR/needless_match.rs:123:31 | LL | let _: Result = if let Ok(val) = x { Ok(val) } else { x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:117:21 + --> $DIR/needless_match.rs:129:21 | LL | let _: Simple = if let Simple::A = x { | _____________________^ @@ -97,7 +97,7 @@ LL | | }; | |_____^ help: replace it with: `x` error: this match expression is unnecessary - --> $DIR/needless_match.rs:156:26 + --> $DIR/needless_match.rs:168:26 | LL | let _: Complex = match ce { | __________________________^ From 7a4d07105f58fdee2ee1d72826bfaa177b07fdb2 Mon Sep 17 00:00:00 2001 From: bing Date: Mon, 18 Apr 2022 14:42:24 +0800 Subject: [PATCH 072/129] Less authoritative stable_sort_primitive message --- clippy_lints/src/stable_sort_primitive.rs | 4 ++-- tests/ui/stable_sort_primitive.stderr | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index bcd28b429784a..a285323dc6e0b 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.47.0"] pub STABLE_SORT_PRIMITIVE, - perf, + pedantic, "use of sort() when sort_unstable() is equivalent" } @@ -126,7 +126,7 @@ impl LateLintPass<'_> for StableSortPrimitive { Applicability::MachineApplicable, ); diag.note( - "an unstable sort would perform faster without any observable difference for this data type", + "an unstable sort typically performs faster without any observable difference for this data type", ); }, ); diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr index b8d22ed250467..c35e0c22ae899 100644 --- a/tests/ui/stable_sort_primitive.stderr +++ b/tests/ui/stable_sort_primitive.stderr @@ -5,7 +5,7 @@ LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `bool` --> $DIR/stable_sort_primitive.rs:9:5 @@ -13,7 +13,7 @@ error: used `sort` on primitive type `bool` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `char` --> $DIR/stable_sort_primitive.rs:11:5 @@ -21,7 +21,7 @@ error: used `sort` on primitive type `char` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `str` --> $DIR/stable_sort_primitive.rs:13:5 @@ -29,7 +29,7 @@ error: used `sort` on primitive type `str` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `tuple` --> $DIR/stable_sort_primitive.rs:15:5 @@ -37,7 +37,7 @@ error: used `sort` on primitive type `tuple` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `array` --> $DIR/stable_sort_primitive.rs:17:5 @@ -45,7 +45,7 @@ error: used `sort` on primitive type `array` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `i32` --> $DIR/stable_sort_primitive.rs:19:5 @@ -53,7 +53,7 @@ error: used `sort` on primitive type `i32` LL | arr.sort(); | ^^^^^^^^^^ help: try: `arr.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: aborting due to 7 previous errors From 7a73e09e35900703d2d58353f30358cfeb1cb4ea Mon Sep 17 00:00:00 2001 From: bing Date: Mon, 18 Apr 2022 14:48:34 +0800 Subject: [PATCH 073/129] Update lints --- clippy_lints/src/lib.register_all.rs | 1 - clippy_lints/src/lib.register_pedantic.rs | 1 + clippy_lints/src/lib.register_perf.rs | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index d091503c4dd83..8608b8a9372d5 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -278,7 +278,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 96cdc4577d53d..63232fd411305 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -82,6 +82,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(ref_option_ref::REF_OPTION_REF), LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE), LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), + LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_ADD_ASSIGN), LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs index 8f361fdad4ac4..82431863e6cfd 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/clippy_lints/src/lib.register_perf.rs @@ -24,7 +24,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(misc::CMP_OWNED), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(types::BOX_COLLECTION), LintId::of(types::REDUNDANT_ALLOCATION), LintId::of(vec::USELESS_VEC), From 6d0baf2b16c709284868c3e3b0809cc336bc6243 Mon Sep 17 00:00:00 2001 From: Lily Mara <54288692+lilymara-onesignal@users.noreply.github.com> Date: Mon, 18 Apr 2022 10:31:57 -0700 Subject: [PATCH 074/129] Update clippy_lints/src/await_holding_invalid.rs Co-authored-by: llogiq --- clippy_lints/src/await_holding_invalid.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 51dd5f4937a4f..8f2b67176ec84 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -164,7 +164,7 @@ declare_clippy_lint! { #[clippy::version = "1.49.0"] pub AWAIT_HOLDING_INVALID_TYPE, suspicious, - "inside an async function, holding a type across an await point which is not safe to be held across an await point" + "holding a type across an await point which is not allowed to be held as per the configuration" } impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); From 228ce783bc75b778272958c3a4d3b8b990a74401 Mon Sep 17 00:00:00 2001 From: Lily Mara <54288692+lilymara-onesignal@users.noreply.github.com> Date: Mon, 18 Apr 2022 10:32:03 -0700 Subject: [PATCH 075/129] Update clippy_lints/src/await_holding_invalid.rs Co-authored-by: llogiq --- clippy_lints/src/await_holding_invalid.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 8f2b67176ec84..24c9f49d9f8be 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -266,7 +266,7 @@ fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedTy cx, AWAIT_HOLDING_INVALID_TYPE, span, - &format!("`{type_name}` may not be held across an `await` point according to config",), + &format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",), |diag| { if let Some(reason) = reason { diag.note(format!("{reason} (according to clippy.toml)")); From ee3ebb35f4f077176657b11d2b05b37f23742baf Mon Sep 17 00:00:00 2001 From: Lily Mara <54288692+lilymara-onesignal@users.noreply.github.com> Date: Mon, 18 Apr 2022 10:32:11 -0700 Subject: [PATCH 076/129] Update clippy_lints/src/await_holding_invalid.rs Co-authored-by: llogiq --- clippy_lints/src/await_holding_invalid.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 24c9f49d9f8be..be905f67ec8c4 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -269,7 +269,7 @@ fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedTy &format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",), |diag| { if let Some(reason) = reason { - diag.note(format!("{reason} (according to clippy.toml)")); + diag.note(reason.clone()); } }, ); From a511072c2cb29f4a30fbd357c7e4b6771b9f6a29 Mon Sep 17 00:00:00 2001 From: Lily Mara Date: Mon, 18 Apr 2022 10:34:17 -0700 Subject: [PATCH 077/129] fixup! Add `await_holding_invalid_type` lint --- clippy_lints/src/await_holding_invalid.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index be905f67ec8c4..5659651c8426a 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -144,20 +144,17 @@ declare_clippy_lint! { /// /// ### Example /// - /// Strings are, of course, safe to hold across await points. This example - /// exists only to show how this lint might be configured for third-party - /// crates. - /// /// ```toml /// await-holding-invalid-types = [ - /// "std::string::String", + /// "CustomLockType", /// ] /// ``` /// /// ```rust /// # async fn baz() {} + /// struct CustomLockType; /// async fn foo() { - /// let _x = String::from("hello!"); + /// let _x = CustomLockType; /// baz().await; // Lint violation /// } /// ``` From 7e26edce65df4e89be23aa22bcb3d3a2db723bf7 Mon Sep 17 00:00:00 2001 From: Lily Mara Date: Mon, 18 Apr 2022 11:16:35 -0700 Subject: [PATCH 078/129] fixup! Add `await_holding_invalid_type` lint --- clippy_lints/src/await_holding_invalid.rs | 5 +++++ .../await_holding_invalid_type.stderr | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 5659651c8426a..022f82ebf51bf 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -146,15 +146,20 @@ declare_clippy_lint! { /// /// ```toml /// await-holding-invalid-types = [ + /// # You can specify a type name /// "CustomLockType", + /// # You can (optionally) specify a reason + /// { type = "OtherCustomLockType", reason = "Relies on a thread local" } /// ] /// ``` /// /// ```rust /// # async fn baz() {} /// struct CustomLockType; + /// struct OtherCustomLockType; /// async fn foo() { /// let _x = CustomLockType; + /// let _y = CustomLockType; /// baz().await; // Lint violation /// } /// ``` diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr index eebdd624d2431..62c45b54634f4 100644 --- a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -1,25 +1,25 @@ -error: `std::string::String` may not be held across an `await` point according to config +error: `std::string::String` may not be held across an `await` point per `clippy.toml` --> $DIR/await_holding_invalid_type.rs:5:9 | LL | let _x = String::from("hello"); | ^^ | = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings` - = note: strings are bad (according to clippy.toml) + = note: strings are bad -error: `std::net::Ipv4Addr` may not be held across an `await` point according to config +error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml` --> $DIR/await_holding_invalid_type.rs:10:9 | LL | let _x = Ipv4Addr::new(127, 0, 0, 1); | ^^ -error: `std::string::String` may not be held across an `await` point according to config +error: `std::string::String` may not be held across an `await` point per `clippy.toml` --> $DIR/await_holding_invalid_type.rs:31:13 | LL | let _x = String::from("hi!"); | ^^ | - = note: strings are bad (according to clippy.toml) + = note: strings are bad error: aborting due to 3 previous errors From 8eccbbed68bbc4ad8c5f0ab17d905359e88e30fa Mon Sep 17 00:00:00 2001 From: Lily Mara Date: Mon, 18 Apr 2022 11:30:59 -0700 Subject: [PATCH 079/129] fixup! Add `await_holding_invalid_type` lint --- clippy_lints/src/await_holding_invalid.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 022f82ebf51bf..5c29afb947b59 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -159,7 +159,7 @@ declare_clippy_lint! { /// struct OtherCustomLockType; /// async fn foo() { /// let _x = CustomLockType; - /// let _y = CustomLockType; + /// let _y = OtherCustomLockType; /// baz().await; // Lint violation /// } /// ``` From 05086858c4058400053e67d53d647ffa43945530 Mon Sep 17 00:00:00 2001 From: Lily Mara Date: Mon, 18 Apr 2022 11:32:28 -0700 Subject: [PATCH 080/129] fixup! Add `await_holding_invalid_type` lint --- clippy_lints/src/await_holding_invalid.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 5c29afb947b59..5b7c4591504e1 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -149,7 +149,7 @@ declare_clippy_lint! { /// # You can specify a type name /// "CustomLockType", /// # You can (optionally) specify a reason - /// { type = "OtherCustomLockType", reason = "Relies on a thread local" } + /// { path = "OtherCustomLockType", reason = "Relies on a thread local" } /// ] /// ``` /// From df1ec91d952aa39646f38775a51834205fb75bef Mon Sep 17 00:00:00 2001 From: Chase Ruskin Date: Sun, 30 Jan 2022 19:16:10 -0500 Subject: [PATCH 081/129] adds lint logic and test for bytes_count_to_len formats code with fixes single match clippy error to replace with if let swaps ident.name.as_str to ident.name == sym for count fn --- CHANGELOG.md | 1 + clippy_lints/src/bytes_count_to_len.rs | 54 +++++++++++++++++++++ clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_complexity.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + tests/ui/bytes_count_to_len.rs | 15 ++++++ tests/ui/bytes_count_to_len.stderr | 19 ++++++++ 8 files changed, 94 insertions(+) create mode 100644 clippy_lints/src/bytes_count_to_len.rs create mode 100644 tests/ui/bytes_count_to_len.rs create mode 100644 tests/ui/bytes_count_to_len.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 811dce481d570..5de598a3204d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3299,6 +3299,7 @@ Released 2018-09-13 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons diff --git a/clippy_lints/src/bytes_count_to_len.rs b/clippy_lints/src/bytes_count_to_len.rs new file mode 100644 index 0000000000000..799d3783f74fd --- /dev/null +++ b/clippy_lints/src/bytes_count_to_len.rs @@ -0,0 +1,54 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// It checks for `str::bytes().count()` and suggests replacing it with + /// `str::len()`. + /// + /// ### Why is this bad? + /// `str::bytes().count()` is longer and may not be as performant as using + /// `str::len()`. + /// + /// ### Example + /// ```rust + /// "hello".bytes().count(); + /// ``` + /// Use instead: + /// ```rust + /// "hello".len(); + /// ``` + #[clippy::version = "1.60.0"] + pub BYTES_COUNT_TO_LEN, + complexity, + "Using bytest().count() when len() performs the same functionality" +} + +declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]); + +impl<'tcx> LateLintPass<'tcx> for BytesCountToLen { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + //check for method call called "count" + if let hir::ExprKind::MethodCall(count_path, count_args, _) = &expr.kind; + if count_path.ident.name == rustc_span::sym::count; + if let [bytes_expr] = &**count_args; + //check for method call called "bytes" that was linked to "count" + if let hir::ExprKind::MethodCall(bytes_path, _, _) = &bytes_expr.kind; + if bytes_path.ident.name.as_str() == "bytes"; + then { + span_lint_and_note( + cx, + BYTES_COUNT_TO_LEN, + expr.span, + "using long and hard to read `.bytes().count()`", + None, + "`.len()` achieves same functionality" + ); + } + }; + } +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 097064ae26f89..0294b9866ac14 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -23,6 +23,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 10369a855ae6e..cd637b199fe27 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -5,6 +5,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), LintId::of(derivable_impls::DERIVABLE_IMPLS), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index c608f634291f8..590fc502738f1 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -64,6 +64,7 @@ store.register_lints(&[ booleans::NONMINIMAL_BOOL, borrow_as_ptr::BORROW_AS_PTR, bytecount::NAIVE_BYTECOUNT, + bytes_count_to_len::BYTES_COUNT_TO_LEN, cargo::CARGO_COMMON_METADATA, cargo::MULTIPLE_CRATE_VERSIONS, cargo::NEGATIVE_FEATURE_NAMES, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 88e8a0cc2af04..9a9ba4f177908 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -181,6 +181,7 @@ mod bool_assert_comparison; mod booleans; mod borrow_as_ptr; mod bytecount; +mod bytes_count_to_len; mod cargo; mod case_sensitive_file_extension_comparisons; mod casts; @@ -875,6 +876,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_late_pass(|| Box::new(format_push_string::FormatPushString)); + store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/bytes_count_to_len.rs b/tests/ui/bytes_count_to_len.rs new file mode 100644 index 0000000000000..b2dd2bfeeed77 --- /dev/null +++ b/tests/ui/bytes_count_to_len.rs @@ -0,0 +1,15 @@ +#![warn(clippy::bytes_count_to_len)] + +fn main() { + let s1 = String::from("world"); + + //test warning against a string literal + "hello".bytes().count(); + + //test warning against a string variable + s1.bytes().count(); + + //make sure using count() normally doesn't trigger warning + let vector = [0, 1, 2]; + let size = vector.iter().count(); +} diff --git a/tests/ui/bytes_count_to_len.stderr b/tests/ui/bytes_count_to_len.stderr new file mode 100644 index 0000000000000..ddd08ee824c11 --- /dev/null +++ b/tests/ui/bytes_count_to_len.stderr @@ -0,0 +1,19 @@ +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:7:5 + | +LL | "hello".bytes().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::bytes-count-to-len` implied by `-D warnings` + = note: `.len()` achieves same functionality + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:10:5 + | +LL | s1.bytes().count(); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `.len()` achieves same functionality + +error: aborting due to 2 previous errors + From f19387d2371de497552fbf465acf9438084fe77b Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Sat, 16 Apr 2022 17:53:22 +0900 Subject: [PATCH 082/129] add checking type adding test patterns cargo dev bless fix comment add ; delete : fix suggestion code and update stderr in tests. use match_def_path when checking method name --- clippy_lints/src/bytes_count_to_len.rs | 42 ++++++++++++++++++-------- clippy_utils/src/paths.rs | 2 ++ tests/ui/bytes_count_to_len.fixed | 34 +++++++++++++++++++++ tests/ui/bytes_count_to_len.rs | 33 +++++++++++++++----- tests/ui/bytes_count_to_len.stderr | 27 +++++++++++------ 5 files changed, 109 insertions(+), 29 deletions(-) create mode 100644 tests/ui/bytes_count_to_len.fixed diff --git a/clippy_lints/src/bytes_count_to_len.rs b/clippy_lints/src/bytes_count_to_len.rs index 799d3783f74fd..d70dbf5b23904 100644 --- a/clippy_lints/src/bytes_count_to_len.rs +++ b/clippy_lints/src/bytes_count_to_len.rs @@ -1,8 +1,14 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -16,15 +22,17 @@ declare_clippy_lint! { /// ### Example /// ```rust /// "hello".bytes().count(); + /// String::from("hello").bytes().count(); /// ``` /// Use instead: /// ```rust /// "hello".len(); + /// String::from("hello").len(); /// ``` - #[clippy::version = "1.60.0"] + #[clippy::version = "1.62.0"] pub BYTES_COUNT_TO_LEN, complexity, - "Using bytest().count() when len() performs the same functionality" + "Using `bytes().count()` when `len()` performs the same functionality" } declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]); @@ -32,21 +40,29 @@ declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]); impl<'tcx> LateLintPass<'tcx> for BytesCountToLen { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { - //check for method call called "count" - if let hir::ExprKind::MethodCall(count_path, count_args, _) = &expr.kind; - if count_path.ident.name == rustc_span::sym::count; - if let [bytes_expr] = &**count_args; - //check for method call called "bytes" that was linked to "count" - if let hir::ExprKind::MethodCall(bytes_path, _, _) = &bytes_expr.kind; - if bytes_path.ident.name.as_str() == "bytes"; + if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind; + if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, expr_def_id, &paths::ITER_COUNT); + + if let [bytes_expr] = &**expr_args; + if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind; + if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id); + if match_def_path(cx, bytes_def_id, &paths::STR_BYTES); + + if let [str_expr] = &**bytes_args; + let ty = cx.typeck_results().expr_ty(str_expr).peel_refs(); + + if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str; then { - span_lint_and_note( + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( cx, BYTES_COUNT_TO_LEN, expr.span, "using long and hard to read `.bytes().count()`", - None, - "`.len()` achieves same functionality" + "consider calling `.len()` instead", + format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)), + applicability ); } }; diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index deb548daf2d0c..4291a5e2299c7 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -61,6 +61,7 @@ pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; +pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; 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"]; @@ -149,6 +150,7 @@ pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; +pub const STR_BYTES: [&str; 4] = ["core", "str", "", "bytes"]; 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"]; diff --git a/tests/ui/bytes_count_to_len.fixed b/tests/ui/bytes_count_to_len.fixed new file mode 100644 index 0000000000000..860642363b5f0 --- /dev/null +++ b/tests/ui/bytes_count_to_len.fixed @@ -0,0 +1,34 @@ +// run-rustfix +#![warn(clippy::bytes_count_to_len)] +use std::fs::File; +use std::io::Read; + +fn main() { + // should fix, because type is String + let _ = String::from("foo").len(); + + let s1 = String::from("foo"); + let _ = s1.len(); + + // should fix, because type is &str + let _ = "foo".len(); + + let s2 = "foo"; + let _ = s2.len(); + + // make sure using count() normally doesn't trigger warning + let vector = [0, 1, 2]; + let _ = vector.iter().count(); + + // The type is slice, so should not fix + let _ = &[1, 2, 3].bytes().count(); + + let bytes: &[u8] = &[1, 2, 3]; + bytes.bytes().count(); + + // The type is File, so should not fix + let _ = File::open("foobar").unwrap().bytes().count(); + + let f = File::open("foobar").unwrap(); + let _ = f.bytes().count(); +} diff --git a/tests/ui/bytes_count_to_len.rs b/tests/ui/bytes_count_to_len.rs index b2dd2bfeeed77..162730c2842a1 100644 --- a/tests/ui/bytes_count_to_len.rs +++ b/tests/ui/bytes_count_to_len.rs @@ -1,15 +1,34 @@ +// run-rustfix #![warn(clippy::bytes_count_to_len)] +use std::fs::File; +use std::io::Read; fn main() { - let s1 = String::from("world"); + // should fix, because type is String + let _ = String::from("foo").bytes().count(); - //test warning against a string literal - "hello".bytes().count(); + let s1 = String::from("foo"); + let _ = s1.bytes().count(); - //test warning against a string variable - s1.bytes().count(); + // should fix, because type is &str + let _ = "foo".bytes().count(); - //make sure using count() normally doesn't trigger warning + let s2 = "foo"; + let _ = s2.bytes().count(); + + // make sure using count() normally doesn't trigger warning let vector = [0, 1, 2]; - let size = vector.iter().count(); + let _ = vector.iter().count(); + + // The type is slice, so should not fix + let _ = &[1, 2, 3].bytes().count(); + + let bytes: &[u8] = &[1, 2, 3]; + bytes.bytes().count(); + + // The type is File, so should not fix + let _ = File::open("foobar").unwrap().bytes().count(); + + let f = File::open("foobar").unwrap(); + let _ = f.bytes().count(); } diff --git a/tests/ui/bytes_count_to_len.stderr b/tests/ui/bytes_count_to_len.stderr index ddd08ee824c11..224deb779871c 100644 --- a/tests/ui/bytes_count_to_len.stderr +++ b/tests/ui/bytes_count_to_len.stderr @@ -1,19 +1,28 @@ error: using long and hard to read `.bytes().count()` - --> $DIR/bytes_count_to_len.rs:7:5 + --> $DIR/bytes_count_to_len.rs:8:13 | -LL | "hello".bytes().count(); - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = String::from("foo").bytes().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `String::from("foo").len()` | = note: `-D clippy::bytes-count-to-len` implied by `-D warnings` - = note: `.len()` achieves same functionality error: using long and hard to read `.bytes().count()` - --> $DIR/bytes_count_to_len.rs:10:5 + --> $DIR/bytes_count_to_len.rs:11:13 | -LL | s1.bytes().count(); - | ^^^^^^^^^^^^^^^^^^ +LL | let _ = s1.bytes().count(); + | ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s1.len()` + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:14:13 + | +LL | let _ = "foo".bytes().count(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `"foo".len()` + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:17:13 | - = note: `.len()` achieves same functionality +LL | let _ = s2.bytes().count(); + | ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s2.len()` -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors From c922bb9443993df9f32ebf25bed76195cfab11f2 Mon Sep 17 00:00:00 2001 From: asquared31415 <34665709+asquared31415@users.noreply.github.com> Date: Mon, 18 Apr 2022 22:19:34 -0400 Subject: [PATCH 083/129] fix ICE --- .../src/casts/cast_slice_different_sizes.rs | 2 +- tests/ui/cast_slice_different_sizes.rs | 31 +++++++++++ tests/ui/cast_slice_different_sizes.stderr | 53 ++++++++++++++++++- 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 3608c1654d5c7..ff03f416d0bdd 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -59,7 +59,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option cast_expr, _ => unreachable!("expr should be a cast as checked by expr_cast_chain_tys"), }; diff --git a/tests/ui/cast_slice_different_sizes.rs b/tests/ui/cast_slice_different_sizes.rs index 57270fcf52b52..7ec137cc5b293 100644 --- a/tests/ui/cast_slice_different_sizes.rs +++ b/tests/ui/cast_slice_different_sizes.rs @@ -39,3 +39,34 @@ fn main() { let long_chain_restore = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8] as *const [u32]; } + +// foo and foo2 should not fire, they're the same size +fn foo(x: *mut [u8]) -> *mut [u8] { + x as *mut [u8] +} + +fn foo2(x: *mut [u8]) -> *mut [u8] { + x as *mut _ +} + +// Test that casts as part of function returns work +fn bar(x: *mut [u16]) -> *mut [u8] { + x as *mut [u8] +} + +fn uwu(x: *mut [u16]) -> *mut [u8] { + x as *mut _ +} + +fn bar2(x: *mut [u16]) -> *mut [u8] { + x as _ +} + +// function returns plus blocks +fn blocks(x: *mut [u16]) -> *mut [u8] { + ({ x }) as _ +} + +fn more_blocks(x: *mut [u16]) -> *mut [u8] { + { ({ x }) as _ } +} diff --git a/tests/ui/cast_slice_different_sizes.stderr b/tests/ui/cast_slice_different_sizes.stderr index 993e93c2bf366..0e018b62e60ae 100644 --- a/tests/ui/cast_slice_different_sizes.stderr +++ b/tests/ui/cast_slice_different_sizes.stderr @@ -48,5 +48,56 @@ error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (elem LL | let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const u8, ..)` -error: aborting due to 6 previous errors +error: casting between raw pointers to `[mut u16]` (element size 2) and `[mut u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:53:36 + | +LL | fn bar(x: *mut [u16]) -> *mut [u8] { + | ____________________________________^ +LL | | x as *mut [u8] +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut mut u8, ..)` + +error: casting between raw pointers to `[mut u16]` (element size 2) and `[mut u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:57:36 + | +LL | fn uwu(x: *mut [u16]) -> *mut [u8] { + | ____________________________________^ +LL | | x as *mut _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut mut u8, ..)` + +error: casting between raw pointers to `[mut u16]` (element size 2) and `[mut u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:61:37 + | +LL | fn bar2(x: *mut [u16]) -> *mut [u8] { + | _____________________________________^ +LL | | x as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut mut u8, ..)` + +error: casting between raw pointers to `[mut u16]` (element size 2) and `[mut u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:66:39 + | +LL | fn blocks(x: *mut [u16]) -> *mut [u8] { + | _______________________________________^ +LL | | ({ x }) as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut mut u8, ..)` + +error: casting between raw pointers to `[mut u16]` (element size 2) and `[mut u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:70:44 + | +LL | fn more_blocks(x: *mut [u16]) -> *mut [u8] { + | ____________________________________________^ +LL | | { ({ x }) as _ } +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut mut u8, ..)` + +error: casting between raw pointers to `[mut u16]` (element size 2) and `[mut u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:71:5 + | +LL | { ({ x }) as _ } + | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut mut u8, ..)` + +error: aborting due to 12 previous errors From 1b6b802b99a1c0ce575bb80eca6d8f53db20af0a Mon Sep 17 00:00:00 2001 From: bing Date: Wed, 20 Apr 2022 23:11:54 +0800 Subject: [PATCH 084/129] Better documentation wording and add known problems section --- clippy_lints/src/stable_sort_primitive.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index a285323dc6e0b..a6c685df721d6 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -9,15 +9,25 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does /// When sorting primitive values (integers, bools, chars, as well - /// as arrays, slices, and tuples of such items), it is better to + /// as arrays, slices, and tuples of such items), it is typically better to /// use an unstable sort than a stable sort. /// /// ### Why is this bad? - /// Using a stable sort consumes more memory and cpu cycles. Because - /// values which compare equal are identical, preserving their + /// Typically, using a stable sort consumes more memory and cpu cycles. + /// Because values which compare equal are identical, preserving their /// relative order (the guarantee that a stable sort provides) means /// nothing, while the extra costs still apply. /// + /// ### Known problems + /// + /// As pointed out in + /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241), + /// a stable sort can instead be significantly faster for certain scenarios + /// (eg. when a sorted vector is extended with new data and resorted). + /// + /// For more information and benchmarking results, please refer to the + /// issue linked above. + /// /// ### Example /// ```rust /// let mut vec = vec![2, 1, 3]; From 8de3fb159dcf412d4692bd862b7b3d348810ecd0 Mon Sep 17 00:00:00 2001 From: Gryffon Bellish Date: Fri, 25 Mar 2022 15:31:52 -0400 Subject: [PATCH 085/129] Add empty_drop lint --- CHANGELOG.md | 1 + clippy_lints/src/empty_drop.rs | 65 ++++++++++++++++++++ clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_restriction.rs | 1 + clippy_lints/src/lib.rs | 2 + tests/ui/empty_drop.fixed | 24 ++++++++ tests/ui/empty_drop.rs | 30 +++++++++ tests/ui/empty_drop.stderr | 22 +++++++ 8 files changed, 146 insertions(+) create mode 100644 clippy_lints/src/empty_drop.rs create mode 100644 tests/ui/empty_drop.fixed create mode 100644 tests/ui/empty_drop.rs create mode 100644 tests/ui/empty_drop.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index dba4ab50cf2fd..2948c24fb3b67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3365,6 +3365,7 @@ Released 2018-09-13 [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else +[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop diff --git a/clippy_lints/src/empty_drop.rs b/clippy_lints/src/empty_drop.rs new file mode 100644 index 0000000000000..325ae2356c14c --- /dev/null +++ b/clippy_lints/src/empty_drop.rs @@ -0,0 +1,65 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, peel_blocks}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for empty `Drop` implementations. + /// + /// ### Why is this bad? + /// Empty `Drop` implementations have no effect when dropping an instance of the type. They are + /// most likely useless. However, an empty `Drop` implementation prevents a type from being + /// destructured, which might be the intention behind adding the implementation as a marker. + /// + /// ### Example + /// ```rust + /// struct S; + /// + /// impl Drop for S { + /// fn drop(&mut self) {} + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct S; + /// ``` + #[clippy::version = "1.61.0"] + pub EMPTY_DROP, + restriction, + "empty `Drop` implementations" +} +declare_lint_pass!(EmptyDrop => [EMPTY_DROP]); + +impl LateLintPass<'_> for EmptyDrop { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if_chain! { + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + items: [child], + .. + }) = item.kind; + if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait(); + if let impl_item_hir = child.id.hir_id(); + if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); + if let ImplItemKind::Fn(_, b) = &impl_item.kind; + if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); + let func_expr = peel_blocks(func_expr); + if let ExprKind::Block(block, _) = func_expr.kind; + if block.stmts.is_empty() && block.expr.is_none(); + then { + span_lint_and_sugg( + cx, + EMPTY_DROP, + item.span, + "empty drop implementation", + "try removing this impl", + String::new(), + Applicability::MaybeIncorrect + ); + } + } + } +} diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 3541c9328c068..8507531c8d307 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -134,6 +134,7 @@ store.register_lints(&[ drop_forget_ref::UNDROPPED_MANUALLY_DROPS, duration_subsec::DURATION_SUBSEC, else_if_without_else::ELSE_IF_WITHOUT_ELSE, + empty_drop::EMPTY_DROP, empty_enum::EMPTY_ENUM, empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS, entry::MAP_ENTRY, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 555a8ad841fec..46c79c56a47db 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -16,6 +16,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION), LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS), LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(empty_drop::EMPTY_DROP), LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c1783a3f764bc..335467bf9d2ae 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -210,6 +210,7 @@ mod double_parens; mod drop_forget_ref; mod duration_subsec; mod else_if_without_else; +mod empty_drop; mod empty_enum; mod empty_structs_with_brackets; mod entry; @@ -822,6 +823,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); + store.register_late_pass(|| Box::new(empty_drop::EmptyDrop)); store.register_late_pass(|| Box::new(strings::StrToString)); store.register_late_pass(|| Box::new(strings::StringToString)); store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues)); diff --git a/tests/ui/empty_drop.fixed b/tests/ui/empty_drop.fixed new file mode 100644 index 0000000000000..2e1b768461ab2 --- /dev/null +++ b/tests/ui/empty_drop.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::empty_drop)] +#![allow(unused)] + +// should cause an error +struct Foo; + + + +// shouldn't cause an error +struct Bar; + +impl Drop for Bar { + fn drop(&mut self) { + println!("dropping bar!"); + } +} + +// should error +struct Baz; + + + +fn main() {} diff --git a/tests/ui/empty_drop.rs b/tests/ui/empty_drop.rs new file mode 100644 index 0000000000000..75232b0334df6 --- /dev/null +++ b/tests/ui/empty_drop.rs @@ -0,0 +1,30 @@ +// run-rustfix +#![warn(clippy::empty_drop)] +#![allow(unused)] + +// should cause an error +struct Foo; + +impl Drop for Foo { + fn drop(&mut self) {} +} + +// shouldn't cause an error +struct Bar; + +impl Drop for Bar { + fn drop(&mut self) { + println!("dropping bar!"); + } +} + +// should error +struct Baz; + +impl Drop for Baz { + fn drop(&mut self) { + {} + } +} + +fn main() {} diff --git a/tests/ui/empty_drop.stderr b/tests/ui/empty_drop.stderr new file mode 100644 index 0000000000000..70f7880d03601 --- /dev/null +++ b/tests/ui/empty_drop.stderr @@ -0,0 +1,22 @@ +error: empty drop implementation + --> $DIR/empty_drop.rs:8:1 + | +LL | / impl Drop for Foo { +LL | | fn drop(&mut self) {} +LL | | } + | |_^ help: try removing this impl + | + = note: `-D clippy::empty-drop` implied by `-D warnings` + +error: empty drop implementation + --> $DIR/empty_drop.rs:24:1 + | +LL | / impl Drop for Baz { +LL | | fn drop(&mut self) { +LL | | {} +LL | | } +LL | | } + | |_^ help: try removing this impl + +error: aborting due to 2 previous errors + From 4424aa444c2b24c3728e3f09d85bd9b69f57a48d Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 18 Apr 2022 13:28:23 +0100 Subject: [PATCH 086/129] `manual_split_once`: lint manual iteration of `SplitN` --- clippy_lints/src/methods/mod.rs | 22 ++- clippy_lints/src/methods/str_splitn.rs | 223 +++++++++++++++++++++---- tests/ui/manual_split_once.fixed | 96 ++++++++++- tests/ui/manual_split_once.rs | 96 ++++++++++- tests/ui/manual_split_once.stderr | 129 +++++++++++++- 5 files changed, 527 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 23266739645da..7a1bf36489a18 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2009,13 +2009,27 @@ declare_clippy_lint! { /// ### Example /// ```rust,ignore /// // Bad - /// let (key, value) = _.splitn(2, '=').next_tuple()?; - /// let value = _.splitn(2, '=').nth(1)?; + /// let s = "key=value=add"; + /// let (key, value) = s.splitn(2, '=').next_tuple()?; + /// let value = s.splitn(2, '=').nth(1)?; /// + /// let mut parts = s.splitn(2, '='); + /// let key = parts.next()?; + /// let value = parts.next()?; + /// ``` + /// Use instead: + /// ```rust,ignore /// // Good - /// let (key, value) = _.split_once('=')?; - /// let value = _.split_once('=')?.1; + /// let s = "key=value=add"; + /// let (key, value) = s.split_once('=')?; + /// let value = s.split_once('=')?.1; + /// + /// let (key, value) = s.split_once('=')?; /// ``` + /// + /// ### Limitations + /// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()` + /// in two separate `let` statements that immediately follow the `splitn()` #[clippy::version = "1.57.0"] pub MANUAL_SPLIT_ONCE, complexity, diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 6eaa9eb2ef0ec..52891eeed0696 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,14 +1,19 @@ use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; -use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, paths}; +use clippy_utils::usage::local_used_after_expr; +use clippy_utils::visitors::expr_visitor; +use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{ + BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, +}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_semver::RustcVersion; -use rustc_span::{symbol::sym, Span, SyntaxContext}; +use rustc_span::{sym, Span, Symbol, SyntaxContext}; use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; @@ -25,40 +30,41 @@ pub(super) fn check( return; } - let ctxt = expr.span.ctxt(); - let Some(usage) = parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) else { return }; - - let needless = match usage.kind { + let needless = |usage_kind| match usage_kind { IterUsageKind::Nth(n) => count > n + 1, IterUsageKind::NextTuple => count > 2, }; + let manual = count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE); - if needless { - let mut app = Applicability::MachineApplicable; - let (r, message) = if method_name == "splitn" { - ("", "unnecessary use of `splitn`") - } else { - ("r", "unnecessary use of `rsplitn`") - }; - - span_lint_and_sugg( - cx, - NEEDLESS_SPLITN, - expr.span, - message, - "try this", - format!( - "{}.{r}split({})", - snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0, - snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0, - ), - app, - ); - } else if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) { - check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage); + match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) { + Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg), + Some(usage) if manual => check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage), + None if manual => { + check_manual_split_once_indirect(cx, method_name, expr, self_arg, pat_arg); + }, + _ => {}, } } +fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) { + let mut app = Applicability::MachineApplicable; + let r = if method_name == "splitn" { "" } else { "r" }; + + span_lint_and_sugg( + cx, + NEEDLESS_SPLITN, + expr.span, + &format!("unnecessary use of `{r}splitn`"), + "try this", + format!( + "{}.{r}split({})", + snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut app).0, + snippet_with_context(cx, pat_arg.span, expr.span.ctxt(), "..", &mut app).0, + ), + app, + ); +} + fn check_manual_split_once( cx: &LateContext<'_>, method_name: &str, @@ -107,16 +113,171 @@ fn check_manual_split_once( span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app); } +/// checks for +/// +/// ``` +/// let mut iter = "a.b.c".splitn(2, '.'); +/// let a = iter.next(); +/// let b = iter.next(); +/// ``` +fn check_manual_split_once_indirect( + cx: &LateContext<'_>, + method_name: &str, + expr: &Expr<'_>, + self_arg: &Expr<'_>, + pat_arg: &Expr<'_>, +) -> Option<()> { + let ctxt = expr.span.ctxt(); + let mut parents = cx.tcx.hir().parent_iter(expr.hir_id); + if let (_, Node::Local(local)) = parents.next()? + && let PatKind::Binding(BindingAnnotation::Mutable, iter_binding_id, iter_ident, None) = local.pat.kind + && let (iter_stmt_id, Node::Stmt(_)) = parents.next()? + && let (_, Node::Block(enclosing_block)) = parents.next()? + + && let mut stmts = enclosing_block + .stmts + .iter() + .skip_while(|stmt| stmt.hir_id != iter_stmt_id) + .skip(1) + + && let first = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)? + && let second = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)? + && first.unwrap_kind == second.unwrap_kind + && first.name != second.name + && !local_used_after_expr(cx, iter_binding_id, second.init_expr) + { + let (r, lhs, rhs) = if method_name == "splitn" { + ("", first.name, second.name) + } else { + ("r", second.name, first.name) + }; + let msg = format!("manual implementation of `{r}split_once`"); + + let mut app = Applicability::MachineApplicable; + let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; + let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0; + + span_lint_and_then(cx, MANUAL_SPLIT_ONCE, local.span, &msg, |diag| { + diag.span_label(first.span, "first usage here"); + diag.span_label(second.span, "second usage here"); + + let unwrap = match first.unwrap_kind { + UnwrapKind::Unwrap => ".unwrap()", + UnwrapKind::QuestionMark => "?", + }; + diag.span_suggestion_verbose( + local.span, + &format!("try `{r}split_once`"), + format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"), + app, + ); + + let remove_msg = format!("remove the `{iter_ident}` usages"); + diag.span_suggestion( + first.span, + &remove_msg, + String::new(), + app, + ); + diag.span_suggestion( + second.span, + &remove_msg, + String::new(), + app, + ); + }); + } + + Some(()) +} + +#[derive(Debug)] +struct IndirectUsage<'a> { + name: Symbol, + span: Span, + init_expr: &'a Expr<'a>, + unwrap_kind: UnwrapKind, +} + +/// returns `Some(IndirectUsage)` for e.g. +/// +/// ```ignore +/// let name = binding.next()?; +/// let name = binding.next().unwrap(); +/// ``` +fn indirect_usage<'tcx>( + cx: &LateContext<'tcx>, + stmt: &Stmt<'tcx>, + binding: HirId, + ctxt: SyntaxContext, +) -> Option> { + if let StmtKind::Local(Local { + pat: + Pat { + kind: PatKind::Binding(BindingAnnotation::Unannotated, _, ident, None), + .. + }, + init: Some(init_expr), + hir_id: local_hir_id, + .. + }) = stmt.kind + { + let mut path_to_binding = None; + expr_visitor(cx, |expr| { + if path_to_local_id(expr, binding) { + path_to_binding = Some(expr); + } + + path_to_binding.is_none() + }) + .visit_expr(init_expr); + + let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id); + let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?; + + let (parent_id, _) = parents.find(|(_, node)| { + !matches!( + node, + Node::Expr(Expr { + kind: ExprKind::Match(.., MatchSource::TryDesugar), + .. + }) + ) + })?; + + if let IterUsage { + kind: IterUsageKind::Nth(0), + unwrap_kind: Some(unwrap_kind), + .. + } = iter_usage + { + if parent_id == *local_hir_id { + return Some(IndirectUsage { + name: ident.name, + span: stmt.span, + init_expr, + unwrap_kind, + }); + } + } + } + + None +} + +#[derive(Debug, Clone, Copy)] enum IterUsageKind { Nth(u128), NextTuple, } +#[derive(Debug, PartialEq)] enum UnwrapKind { Unwrap, QuestionMark, } +#[derive(Debug)] struct IterUsage { kind: IterUsageKind, unwrap_kind: Option, diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed index bffc7b9b631b9..c7ca770434a31 100644 --- a/tests/ui/manual_split_once.fixed +++ b/tests/ui/manual_split_once.fixed @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] -#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)] +#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] extern crate itertools; @@ -41,13 +41,107 @@ fn main() { let _ = s.rsplit_once('=').map(|x| x.0); } +fn indirect() -> Option<()> { + let (l, r) = "a.b.c".split_once('.').unwrap(); + + + + let (l, r) = "a.b.c".split_once('.')?; + + + + let (l, r) = "a.b.c".rsplit_once('.').unwrap(); + + + + let (l, r) = "a.b.c".rsplit_once('.')?; + + + + // could lint, currently doesn't + + let mut iter = "a.b.c".splitn(2, '.'); + let other = 1; + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let mut mut_binding = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let tuple = (iter.next()?, iter.next()?); + + // should not lint + + let mut missing_unwrap = "a.b.c".splitn(2, '.'); + let l = missing_unwrap.next(); + let r = missing_unwrap.next(); + + let mut mixed_unrap = "a.b.c".splitn(2, '.'); + let unwrap = mixed_unrap.next().unwrap(); + let question_mark = mixed_unrap.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let same_name = iter.next()?; + let same_name = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let shadows_existing = "d"; + let shadows_existing = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let becomes_shadowed = iter.next()?; + let becomes_shadowed = "d"; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + let third_usage = iter.next()?; + + let mut n_three = "a.b.c".splitn(3, '.'); + let l = n_three.next()?; + let r = n_three.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + { + let in_block = iter.next()?; + } + let r = iter.next()?; + + let mut lacks_binding = "a.b.c".splitn(2, '.'); + let _ = lacks_binding.next()?; + let r = lacks_binding.next()?; + + let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~"); + let l = iter.next()?; + let r = iter.next()?; + + let mut assigned = ""; + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + assigned = iter.next()?; + + None +} + fn _msrv_1_51() { #![clippy::msrv = "1.51"] // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); } fn _msrv_1_52() { #![clippy::msrv = "1.52"] let _ = "key=value".split_once('=').unwrap().1; + + let (a, b) = "a.b.c".split_once('.').unwrap(); + + } diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs index 147be7c7ab187..ee2848a251ee3 100644 --- a/tests/ui/manual_split_once.rs +++ b/tests/ui/manual_split_once.rs @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] -#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)] +#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] extern crate itertools; @@ -41,13 +41,107 @@ fn main() { let _ = s.rsplitn(2, '=').nth(1); } +fn indirect() -> Option<()> { + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next().unwrap(); + let r = iter.next().unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".rsplitn(2, '.'); + let r = iter.next().unwrap(); + let l = iter.next().unwrap(); + + let mut iter = "a.b.c".rsplitn(2, '.'); + let r = iter.next()?; + let l = iter.next()?; + + // could lint, currently doesn't + + let mut iter = "a.b.c".splitn(2, '.'); + let other = 1; + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let mut mut_binding = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let tuple = (iter.next()?, iter.next()?); + + // should not lint + + let mut missing_unwrap = "a.b.c".splitn(2, '.'); + let l = missing_unwrap.next(); + let r = missing_unwrap.next(); + + let mut mixed_unrap = "a.b.c".splitn(2, '.'); + let unwrap = mixed_unrap.next().unwrap(); + let question_mark = mixed_unrap.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let same_name = iter.next()?; + let same_name = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let shadows_existing = "d"; + let shadows_existing = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let becomes_shadowed = iter.next()?; + let becomes_shadowed = "d"; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + let third_usage = iter.next()?; + + let mut n_three = "a.b.c".splitn(3, '.'); + let l = n_three.next()?; + let r = n_three.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + { + let in_block = iter.next()?; + } + let r = iter.next()?; + + let mut lacks_binding = "a.b.c".splitn(2, '.'); + let _ = lacks_binding.next()?; + let r = lacks_binding.next()?; + + let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~"); + let l = iter.next()?; + let r = iter.next()?; + + let mut assigned = ""; + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + assigned = iter.next()?; + + None +} + fn _msrv_1_51() { #![clippy::msrv = "1.51"] // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); } fn _msrv_1_52() { #![clippy::msrv = "1.52"] let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); } diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr index 9635e6c5e4b46..2563a6904b77c 100644 --- a/tests/ui/manual_split_once.stderr +++ b/tests/ui/manual_split_once.stderr @@ -79,10 +79,135 @@ LL | let _ = s.rsplitn(2, '=').nth(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:52:13 + --> $DIR/manual_split_once.rs:45:5 + | +LL | let mut iter = "a.b.c".splitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let l = iter.next().unwrap(); + | ----------------------------- first usage here +LL | let r = iter.next().unwrap(); + | ----------------------------- second usage here + | +help: try `split_once` + | +LL | let (l, r) = "a.b.c".split_once('.').unwrap(); + | +help: remove the `iter` usages + | +LL - let l = iter.next().unwrap(); +LL + + | +help: remove the `iter` usages + | +LL - let r = iter.next().unwrap(); +LL + + | + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:49:5 + | +LL | let mut iter = "a.b.c".splitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let l = iter.next()?; + | --------------------- first usage here +LL | let r = iter.next()?; + | --------------------- second usage here + | +help: try `split_once` + | +LL | let (l, r) = "a.b.c".split_once('.')?; + | +help: remove the `iter` usages + | +LL - let l = iter.next()?; +LL + + | +help: remove the `iter` usages + | +LL - let r = iter.next()?; +LL + + | + +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:53:5 + | +LL | let mut iter = "a.b.c".rsplitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let r = iter.next().unwrap(); + | ----------------------------- first usage here +LL | let l = iter.next().unwrap(); + | ----------------------------- second usage here + | +help: try `rsplit_once` + | +LL | let (l, r) = "a.b.c".rsplit_once('.').unwrap(); + | +help: remove the `iter` usages + | +LL - let r = iter.next().unwrap(); +LL + + | +help: remove the `iter` usages + | +LL - let l = iter.next().unwrap(); +LL + + | + +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:57:5 + | +LL | let mut iter = "a.b.c".rsplitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let r = iter.next()?; + | --------------------- first usage here +LL | let l = iter.next()?; + | --------------------- second usage here + | +help: try `rsplit_once` + | +LL | let (l, r) = "a.b.c".rsplit_once('.')?; + | +help: remove the `iter` usages + | +LL - let r = iter.next()?; +LL + + | +help: remove the `iter` usages + | +LL - let l = iter.next()?; +LL + + | + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:142:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` -error: aborting due to 14 previous errors +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:144:5 + | +LL | let mut iter = "a.b.c".splitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let a = iter.next().unwrap(); + | ----------------------------- first usage here +LL | let b = iter.next().unwrap(); + | ----------------------------- second usage here + | +help: try `split_once` + | +LL | let (a, b) = "a.b.c".split_once('.').unwrap(); + | +help: remove the `iter` usages + | +LL - let a = iter.next().unwrap(); +LL + + | +help: remove the `iter` usages + | +LL - let b = iter.next().unwrap(); +LL + + | + +error: aborting due to 19 previous errors From 14667d147417546cee9f4c8ae8aa78f71e371e6c Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 21 Apr 2022 13:42:55 -0400 Subject: [PATCH 087/129] Fix missing whitespace in `collapsible_else_if` suggestion --- clippy_lints/src/collapsible_if.rs | 18 ++++++++++++++---- tests/ui/collapsible_else_if.fixed | 7 +++++++ tests/ui/collapsible_else_if.rs | 9 +++++++++ tests/ui/collapsible_else_if.stderr | 11 ++++++++++- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index eae2723a7dac1..3227e6e86af4e 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -13,13 +13,14 @@ //! This lint is **warn** by default use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet_block, snippet_block_with_applicability}; +use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability}; use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -102,7 +103,7 @@ impl EarlyLintPass for CollapsibleIf { fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) { if let ast::ExprKind::If(check, then, else_) = &expr.kind { if let Some(else_) = else_ { - check_collapsible_maybe_if_let(cx, else_); + check_collapsible_maybe_if_let(cx, then.span, else_); } else if let ast::ExprKind::Let(..) = check.kind { // Prevent triggering on `if let a = b { if c { .. } }`. } else { @@ -119,7 +120,7 @@ fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool { trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*") } -fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { +fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) { if_chain! { if let ast::ExprKind::Block(ref block, _) = else_.kind; if !block_starts_with_comment(cx, block); @@ -128,6 +129,11 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { if !else_.span.from_expansion(); if let ast::ExprKind::If(..) = else_.kind; then { + // Prevent "elseif" + // Check that the "else" is followed by whitespace + let up_to_else = then_span.between(block.span); + let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false }; + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -135,7 +141,11 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { block.span, "this `else { if .. }` block can be collapsed", "collapse nested if block", - snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(), + format!( + "{}{}", + if requires_space { " " } else { "" }, + snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability) + ), applicability, ); } diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index bb6c4c0703d51..d6a5a78506791 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -75,3 +75,10 @@ fn main() { } } } + +#[rustfmt::skip] +#[allow(dead_code)] +fn issue_7318() { + if true { println!("I've been resolved!") + }else if false {} +} diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 6d4f688db8c0a..4399fc8b2bd1d 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -89,3 +89,12 @@ fn main() { } } } + +#[rustfmt::skip] +#[allow(dead_code)] +fn issue_7318() { + if true { println!("I've been resolved!") + }else{ + if false {} + } +} diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 6970f66097908..45b2094c9948b 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -150,5 +150,14 @@ LL + println!("!") LL + } | -error: aborting due to 7 previous errors +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:97:10 + | +LL | }else{ + | __________^ +LL | | if false {} +LL | | } + | |_____^ help: collapse nested if block: `if false {}` + +error: aborting due to 8 previous errors From a96bc7af122ca2f84ea282e748339a0531392cd8 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 22 Apr 2022 13:05:53 +0100 Subject: [PATCH 088/129] dogfood: allow unknown lints when not running with `internal` feature --- tests/dogfood.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 67af9d05bf402..eb97d1933d5d7 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -80,9 +80,13 @@ fn run_clippy_for_package(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 feature if cfg!(feature = "internal") { + // internal lints only exist if we build with the internal feature command.args(&["-D", "clippy::internal"]); + } else { + // running a clippy built without internal lints on the clippy source + // that contains e.g. `allow(clippy::invalid_paths)` + command.args(&["-A", "unknown_lints"]); } let output = command.output().unwrap(); From f20e890a4b53848771f2041312cd52d31fb015bc Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 22 Apr 2022 18:03:23 -0400 Subject: [PATCH 089/129] Add macro export exemption to `redundant_pub_crate` --- clippy_lints/src/redundant_pub_crate.rs | 19 +++++++++++++++++-- tests/ui/redundant_pub_crate.fixed | 10 ++++++++++ tests/ui/redundant_pub_crate.rs | 10 ++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 2cee3c14d7f30..3e6086d56f06a 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind, VisibilityKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::hygiene::MacroKind; declare_clippy_lint! { /// ### What it does @@ -41,8 +43,11 @@ impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]); impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let VisibilityKind::Crate { .. } = item.vis.node { - if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false) { + if_chain! { + if let VisibilityKind::Crate { .. } = item.vis.node; + if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false); + if is_not_macro_export(item); + then { let span = item.span.with_hi(item.ident.span.hi()); let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id()); span_lint_and_then( @@ -73,3 +78,13 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { } } } + +fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool { + if let ItemKind::Use(path, _) = item.kind { + if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res { + return false; + } + } + + true +} diff --git a/tests/ui/redundant_pub_crate.fixed b/tests/ui/redundant_pub_crate.fixed index 25f2fd061b88e..106947de68c12 100644 --- a/tests/ui/redundant_pub_crate.fixed +++ b/tests/ui/redundant_pub_crate.fixed @@ -104,4 +104,14 @@ mod m4 { pub use m4::*; +mod issue_8732 { + #[allow(unused_macros)] + macro_rules! some_macro { + () => {}; + } + + #[allow(unused_imports)] + pub(crate) use some_macro; // ok: macro exports are exempt +} + fn main() {} diff --git a/tests/ui/redundant_pub_crate.rs b/tests/ui/redundant_pub_crate.rs index 616286b4f39f4..f96cfd3184384 100644 --- a/tests/ui/redundant_pub_crate.rs +++ b/tests/ui/redundant_pub_crate.rs @@ -104,4 +104,14 @@ mod m4 { pub use m4::*; +mod issue_8732 { + #[allow(unused_macros)] + macro_rules! some_macro { + () => {}; + } + + #[allow(unused_imports)] + pub(crate) use some_macro; // ok: macro exports are exempt +} + fn main() {} From b35c04f7dc8febef216b17209d030bd239c19f2c Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Fri, 22 Apr 2022 20:05:18 -0400 Subject: [PATCH 090/129] Extend `extra_unused_lifetimes` to handle impl lifetimes --- clippy_lints/src/lifetimes.rs | 65 +++++++++++++++++++++++--- tests/ui/crashes/ice-2865.rs | 2 +- tests/ui/crashes/ice-3151.rs | 2 +- tests/ui/extra_unused_lifetimes.rs | 16 +++++++ tests/ui/extra_unused_lifetimes.stderr | 20 +++++++- tests/ui/impl.rs | 2 +- tests/ui/new_without_default.rs | 2 +- 7 files changed, 97 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index b09c23f31e970..ec74b8606d9d9 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,16 +1,19 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, Visitor, + walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, + BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter as mir_nested_filter; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Ident, Symbol}; @@ -84,6 +87,8 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, ref generics, id) = item.kind { check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true); + } else if let ItemKind::Impl(ref impl_) = item.kind { + report_extra_impl_lifetimes(cx, impl_); } } @@ -194,8 +199,7 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: visitor.visit_ty(self_ty); !visitor.all_lts().is_empty() - } - else { + } else { false } } @@ -481,11 +485,29 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl false } -struct LifetimeChecker { +struct LifetimeChecker<'cx, 'tcx, F> { + cx: &'cx LateContext<'tcx>, map: FxHashMap, + phantom: std::marker::PhantomData, } -impl<'tcx> Visitor<'tcx> for LifetimeChecker { +impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> { + fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap) -> LifetimeChecker<'cx, 'tcx, F> { + Self { + cx, + map, + phantom: std::marker::PhantomData, + } + } +} + +impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F> +where + F: NestedFilter<'tcx>, +{ + type Map = rustc_middle::hir::map::Map<'tcx>; + type NestedFilter = F; + // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { self.map.remove(&lifetime.name.ident().name); @@ -501,6 +523,10 @@ impl<'tcx> Visitor<'tcx> for LifetimeChecker { walk_generic_param(self, param); } } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } } fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { @@ -512,7 +538,7 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, _ => None, }) .collect(); - let mut checker = LifetimeChecker { map: hs }; + let mut checker = LifetimeChecker::::new(cx, hs); walk_generics(&mut checker, generics); walk_fn_decl(&mut checker, func); @@ -527,6 +553,31 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, } } +fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) { + let hs = impl_ + .generics + .params + .iter() + .filter_map(|par| match par.kind { + GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)), + _ => None, + }) + .collect(); + let mut checker = LifetimeChecker::::new(cx, hs); + + if let Some(ref trait_ref) = impl_.of_trait { + walk_trait_ref(&mut checker, trait_ref); + } + walk_ty(&mut checker, impl_.self_ty); + for item in impl_.items { + walk_impl_item_ref(&mut checker, item); + } + + for &v in checker.map.values() { + span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl"); + } +} + struct BodyLifetimeChecker { lifetimes_used_in_body: bool, } diff --git a/tests/ui/crashes/ice-2865.rs b/tests/ui/crashes/ice-2865.rs index 6b1ceb5056933..c629813960162 100644 --- a/tests/ui/crashes/ice-2865.rs +++ b/tests/ui/crashes/ice-2865.rs @@ -1,4 +1,4 @@ -#[allow(dead_code)] +#![allow(dead_code, clippy::extra_unused_lifetimes)] /// Test for https://github.com/rust-lang/rust-clippy/issues/2865 diff --git a/tests/ui/crashes/ice-3151.rs b/tests/ui/crashes/ice-3151.rs index fef4d7db84ddf..268ba86fc7aa8 100644 --- a/tests/ui/crashes/ice-3151.rs +++ b/tests/ui/crashes/ice-3151.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/2865 +/// Test for https://github.com/rust-lang/rust-clippy/issues/3151 #[derive(Clone)] pub struct HashMap { diff --git a/tests/ui/extra_unused_lifetimes.rs b/tests/ui/extra_unused_lifetimes.rs index 150acfbfee759..69dc2f7965e10 100644 --- a/tests/ui/extra_unused_lifetimes.rs +++ b/tests/ui/extra_unused_lifetimes.rs @@ -72,4 +72,20 @@ mod issue4291 { } } +mod issue6437 { + pub struct Scalar; + + impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar { + fn add_assign(&mut self, _rhs: &Scalar) { + unimplemented!(); + } + } + + impl<'b> Scalar { + pub fn something<'c>() -> Self { + Self + } + } +} + fn main() {} diff --git a/tests/ui/extra_unused_lifetimes.stderr b/tests/ui/extra_unused_lifetimes.stderr index ebdb8e749520f..68f02c7ad0f48 100644 --- a/tests/ui/extra_unused_lifetimes.stderr +++ b/tests/ui/extra_unused_lifetimes.stderr @@ -24,5 +24,23 @@ error: this lifetime isn't used in the function definition LL | fn unused_lt<'a>(x: u8) {} | ^^ -error: aborting due to 4 previous errors +error: this lifetime isn't used in the impl + --> $DIR/extra_unused_lifetimes.rs:78:10 + | +LL | impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar { + | ^^ + +error: this lifetime isn't used in the impl + --> $DIR/extra_unused_lifetimes.rs:84:10 + | +LL | impl<'b> Scalar { + | ^^ + +error: this lifetime isn't used in the function definition + --> $DIR/extra_unused_lifetimes.rs:85:26 + | +LL | pub fn something<'c>() -> Self { + | ^^ + +error: aborting due to 7 previous errors diff --git a/tests/ui/impl.rs b/tests/ui/impl.rs index 3944377501536..aea52a852f987 100644 --- a/tests/ui/impl.rs +++ b/tests/ui/impl.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] +#![allow(dead_code, clippy::extra_unused_lifetimes)] #![warn(clippy::multiple_inherent_impl)] struct MyStruct; diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index e94f99c95f481..538927b18055a 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, clippy::missing_safety_doc)] +#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)] #![warn(clippy::new_without_default)] pub struct Foo; From fabc26f7b7adabd153f3ee11f22c58ee247ab259 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 13 Feb 2022 15:40:08 +0100 Subject: [PATCH 091/129] Stop visiting visibility. --- clippy_lints/src/cognitive_complexity.rs | 2 +- clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs | 4 ++-- clippy_lints/src/functions/too_many_arguments.rs | 3 +-- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/return_self_not_must_use.rs | 2 +- clippy_lints/src/unused_async.rs | 2 +- clippy_utils/src/lib.rs | 2 +- 8 files changed, 9 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 85f952375491f..2bf7f86890545 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -82,7 +82,7 @@ impl CognitiveComplexity { if rust_cc > self.limit.limit() { let fn_span = match kind { - FnKind::ItemFn(ident, _, _, _) | FnKind::Method(ident, _, _) => ident.span, + FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span, FnKind::Closure => { let header_span = body_span.with_hi(decl.output.span().lo()); let pos = snippet_opt(cx, header_span).and_then(|snip| { 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 830e3b32cfa2f..565a1c871d758 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -17,8 +17,8 @@ pub(super) fn check_fn<'tcx>( hir_id: hir::HirId, ) { let unsafety = match kind { - intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety, - intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety, + intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }) => unsafety, + intravisit::FnKind::Method(_, sig) => sig.header.unsafety, intravisit::FnKind::Closure => return, }; diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs index 3af960491ed01..5c8d8b8e7552c 100644 --- a/clippy_lints/src/functions/too_many_arguments.rs +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -26,9 +26,8 @@ pub(super) fn check_fn( header: hir::FnHeader { abi: Abi::Rust, .. }, .. }, - _, ) - | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _) => check_arg_number( + | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }) => check_arg_number( cx, decl, span.with_hi(decl.output.span().hi()), diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index d29d07da7b0ff..9c734221ebcea 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } match kind { - FnKind::ItemFn(.., header, _) => { + FnKind::ItemFn(.., header) => { let attrs = cx.tcx.hir().attrs(hir_id); if header.abi != Abi::Rust || requires_exact_signature(attrs) { return; diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index d59249d7f13d3..9af3059a37f93 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -251,7 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } match kind { - FnKind::ItemFn(.., header, _) => { + FnKind::ItemFn(.., header) => { if header.abi != Abi::Rust { return; } diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 79f104eac0be2..91e5e1e8b2892 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for ReturnSelfNotMustUse { ) { if_chain! { // We are only interested in methods, not in functions or associated functions. - if matches!(kind, FnKind::Method(_, _, _)); + if matches!(kind, FnKind::Method(_, _)); if let Some(fn_def) = cx.tcx.hir().opt_local_def_id(hir_id); if let Some(impl_def) = cx.tcx.impl_of_method(fn_def.to_def_id()); // We don't want this method to be te implementation of a trait because the diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 2b89398ecd6ad..41333bb2addf7 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { span: Span, hir_id: HirId, ) { - if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind { + if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }) = &fn_kind { if matches!(asyncness, IsAsync::Async) { let mut visitor = AsyncFnVisitor { cx, found_await: false }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index a275bac4ce63d..74978720424d4 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1690,7 +1690,7 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, /// Checks if the given function kind is an async function. pub fn is_async_fn(kind: FnKind<'_>) -> bool { - matches!(kind, FnKind::ItemFn(_, _, header, _) if header.asyncness == IsAsync::Async) + matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async) } /// Peels away all the compiler generated code surrounding the body of an async function, From abc8eb71e6489b7677da54d082871ff498c6c91f Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 13 Feb 2022 01:54:13 +0100 Subject: [PATCH 092/129] Drop vis in FieldDef. --- clippy_lints/src/exhaustive_items.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index b0f50b5c144bb..173d41b4b0506 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -78,7 +78,10 @@ impl LateLintPass<'_> for ExhaustiveItems { if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); then { let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { - if v.fields().iter().any(|f| !f.vis.node.is_pub()) { + if v.fields().iter().any(|f| { + let def_id = cx.tcx.hir().local_def_id(f.hir_id); + !cx.tcx.visibility(def_id).is_public() + }) { // skip structs with private fields return; } From 6ec33dfe4915a19129af765fdd53dd95f21c3788 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 13 Feb 2022 10:54:07 +0100 Subject: [PATCH 093/129] Drop vis in ImplItem. --- clippy_lints/src/utils/inspector.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index a04288e0a413e..dd94a8d649017 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -4,6 +4,7 @@ use clippy_utils::get_attr; use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty; use rustc_session::Session; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -45,14 +46,10 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { return; } println!("impl item `{}`", item.ident.name); - match item.vis.node { - hir::VisibilityKind::Public => println!("public"), - hir::VisibilityKind::Crate(_) => println!("visible crate wide"), - hir::VisibilityKind::Restricted { path, .. } => println!( - "visible in module `{}`", - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) - ), - hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"), + match cx.tcx.visibility(item.def_id) { + ty::Visibility::Public => println!("public"), + ty::Visibility::Restricted(def_id) => println!("visible in module `{}`", cx.tcx.def_path_str(def_id)), + ty::Visibility::Invisible => println!("invisible"), } match item.kind { hir::ImplItemKind::Const(_, body_id) => { From 04024bacba1f74f5d8cdb7fb91798ba77bc2b8f5 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 13 Feb 2022 11:30:48 +0100 Subject: [PATCH 094/129] Drop vis in Item. --- clippy_lints/src/enum_variants.rs | 2 +- clippy_lints/src/redundant_pub_crate.rs | 8 +++++--- clippy_lints/src/utils/inspector.rs | 12 ++++-------- clippy_lints/src/wildcard_imports.rs | 4 +++- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 1f4353fa4f72b..346d03ca5568f 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -260,7 +260,7 @@ impl LateLintPass<'_> for EnumVariantNames { } // The `module_name_repetitions` lint should only trigger if the item has the module in its // name. Having the same name is accepted. - if item.vis.node.is_pub() && item_camel.len() > mod_camel.len() { + if cx.tcx.visibility(item.def_id).is_public() && item_camel.len() > mod_camel.len() { let matching = count_match_start(mod_camel, &item_camel); let rmatching = count_match_end(mod_camel, &item_camel); let nchars = mod_camel.chars().count(); diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 2cee3c14d7f30..e2e2400f8e267 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; -use rustc_hir::{Item, ItemKind, VisibilityKind}; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::def_id::CRATE_DEF_ID; declare_clippy_lint! { /// ### What it does @@ -41,7 +43,7 @@ impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]); impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let VisibilityKind::Crate { .. } = item.vis.node { + if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) { if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false) { let span = item.span.with_hi(item.ident.span.hi()); let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id()); @@ -52,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { &format!("pub(crate) {} inside private module", descr), |diag| { diag.span_suggestion( - item.vis.span, + item.vis_span, "consider using", "pub".to_string(), Applicability::MachineApplicable, diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index dd94a8d649017..e4abfd07866a2 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -357,14 +357,10 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { let did = item.def_id; println!("item `{}`", item.ident.name); - match item.vis.node { - hir::VisibilityKind::Public => println!("public"), - hir::VisibilityKind::Crate(_) => println!("visible crate wide"), - hir::VisibilityKind::Restricted { path, .. } => println!( - "visible in module `{}`", - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) - ), - hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"), + match cx.tcx.visibility(item.def_id) { + ty::Visibility::Public => println!("public"), + ty::Visibility::Restricted(def_id) => println!("visible in module `{}`", cx.tcx.def_path_str(def_id)), + ty::Visibility::Invisible => println!("invisible"), } match item.kind { hir::ItemKind::ExternCrate(ref _renamed_from) => { diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 832da66a53695..2f74eaf3cf5c3 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -8,6 +8,7 @@ use rustc_hir::{ Item, ItemKind, PathSegment, UseKind, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::kw; use rustc_span::{sym, BytePos}; @@ -115,7 +116,8 @@ impl LateLintPass<'_> for WildcardImports { if is_test_module_or_function(cx.tcx, item) { self.test_modules_deep = self.test_modules_deep.saturating_add(1); } - if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { + let module = cx.tcx.parent_module_from_def_id(item.def_id); + if cx.tcx.visibility(item.def_id) != ty::Visibility::Restricted(module.to_def_id()) { return; } if_chain! { From 0c164bbfdbe92e4a3732fc4d318e4eb85c843a1f Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 23 Apr 2022 12:23:18 +0100 Subject: [PATCH 095/129] ignore `redundant_pub_crate` in `useless_attribute` --- clippy_lints/src/attrs.rs | 13 ++++++------- tests/ui/useless_attribute.fixed | 6 ++++++ tests/ui/useless_attribute.rs | 6 ++++++ tests/ui/useless_attribute.stderr | 2 +- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index d0b03c0a9c276..e880876218ec6 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -335,9 +335,6 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } if let Some(lint_list) = &attr.meta_item_list() { if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) { - // permit `unused_imports`, `deprecated`, `unreachable_pub`, - // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items - // and `unused_imports` for `extern crate` items with `macro_use` for lint in lint_list { match item.kind { ItemKind::Use(..) => { @@ -345,10 +342,12 @@ 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| { + matches!( + s.as_str(), + "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate", + ) + }) { return; } diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index ce58a80347b55..c23231a99e9f0 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -57,6 +57,12 @@ pub use std::io::prelude::*; #[allow(clippy::enum_glob_use)] pub use std::cmp::Ordering::*; +// don't lint on clippy::redundant_pub_crate +mod c { + #[allow(clippy::redundant_pub_crate)] + pub(crate) struct S; +} + fn test_indented_attr() { #![allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index c82bb9ba07fd7..7a7b198ea6078 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -57,6 +57,12 @@ pub use std::io::prelude::*; #[allow(clippy::enum_glob_use)] pub use std::cmp::Ordering::*; +// don't lint on clippy::redundant_pub_crate +mod c { + #[allow(clippy::redundant_pub_crate)] + pub(crate) struct S; +} + fn test_indented_attr() { #[allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.stderr b/tests/ui/useless_attribute.stderr index d0194e4bbbe5b..255d287635531 100644 --- a/tests/ui/useless_attribute.stderr +++ b/tests/ui/useless_attribute.stderr @@ -13,7 +13,7 @@ LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` error: useless lint attribute - --> $DIR/useless_attribute.rs:61:5 + --> $DIR/useless_attribute.rs:67:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` From 51db157fb4c44212fec54d61af19f80bd4335286 Mon Sep 17 00:00:00 2001 From: tamaron Date: Sat, 23 Apr 2022 22:45:26 +0900 Subject: [PATCH 096/129] fix --- clippy_lints/src/methods/mod.rs | 18 +++++++++--------- .../src/methods/wrong_self_convention.rs | 2 +- tests/ui/wrong_self_convention.rs | 5 ----- tests/ui/wrong_self_convention.stderr | 18 +++++------------- tests/ui/wrong_self_convention2.rs | 10 ++++++++++ 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7a1bf36489a18..f3be71f6b8bb8 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -296,15 +296,15 @@ declare_clippy_lint! { /// Checks for methods with certain name prefixes and which /// doesn't match how self is taken. The actual rules are: /// - /// |Prefix |Postfix |`self` taken | `self` type | - /// |-------|------------|-----------------------|--------------| - /// |`as_` | none |`&self` or `&mut self` | any | - /// |`from_`| none | none | any | - /// |`into_`| none |`self` | any | - /// |`is_` | none |`&self` or none | any | - /// |`to_` | `_mut` |`&mut self` | any | - /// |`to_` | not `_mut` |`self` | `Copy` | - /// |`to_` | not `_mut` |`&self` | not `Copy` | + /// |Prefix |Postfix |`self` taken | `self` type | + /// |-------|------------|-------------------------------|--------------| + /// |`as_` | none |`&self` or `&mut self` | any | + /// |`from_`| none | none | any | + /// |`into_`| none |`self` | any | + /// |`is_` | none |`&mut self` or `&self` or none | any | + /// |`to_` | `_mut` |`&mut self` | any | + /// |`to_` | not `_mut` |`self` | `Copy` | + /// |`to_` | not `_mut` |`&self` | not `Copy` | /// /// Note: Clippy doesn't trigger methods with `to_` prefix in: /// - Traits definition. diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index aecfea9c141cf..4b368d3ffae25 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -14,7 +14,7 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]), (&[Convention::StartsWith("from_")], &[SelfKind::No]), (&[Convention::StartsWith("into_")], &[SelfKind::Value]), - (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]), + (&[Convention::StartsWith("is_")], &[SelfKind::RefMut, SelfKind::Ref, SelfKind::No]), (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]), (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]), diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index f8fee4b3ab2d8..e3cc90ee222ad 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -193,11 +193,6 @@ 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 diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 5493a99572e06..2e7ee51d7e11a 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -31,7 +31,7 @@ LL | fn into_i32(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:38:15 | LL | fn is_i32(self) {} @@ -71,7 +71,7 @@ LL | pub fn into_i64(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn is_i64(self) {} @@ -111,7 +111,7 @@ LL | fn into_i32_ref(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:98:19 | LL | fn is_i32(self) {} @@ -143,7 +143,7 @@ LL | fn into_i32_ref(&self); | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:122:19 | LL | fn is_i32(self); @@ -191,13 +191,5 @@ LL | fn to_u64(self) -> u64 { | = help: consider choosing a less ambiguous name -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 +error: aborting due to 24 previous errors diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index a8fe833113377..0dcf4743e8b8d 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -104,3 +104,13 @@ mod issue4546 { pub fn to_other_thingy(self: Pin<&Self>) {} } } + +mod issue_8480_8513 { + struct Cat(String); + + impl Cat { + fn is_animal(&mut self) -> bool { + todo!(); + } + } +} From af9dfa36921a66116e2cbd4a2aeb8f8f7e0082e9 Mon Sep 17 00:00:00 2001 From: asquared31415 <34665709+asquared31415@users.noreply.github.com> Date: Sat, 23 Apr 2022 12:49:40 -0400 Subject: [PATCH 097/129] fix ICE by using a type to return the info we want and also fix some bugs in displaying an extra mut when a TypeAndMut was wrong --- .../src/casts/cast_slice_different_sizes.rs | 109 +++++++++++------- tests/ui/cast_slice_different_sizes.rs | 10 ++ tests/ui/cast_slice_different_sizes.stderr | 50 +++++--- 3 files changed, 113 insertions(+), 56 deletions(-) diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index ff03f416d0bdd..893af60db2c5e 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -8,31 +8,6 @@ use rustc_semver::RustcVersion; use super::CAST_SLICE_DIFFERENT_SIZES; -fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let map = cx.tcx.hir(); - if_chain! { - if let Some(parent_id) = map.find_parent_node(expr.hir_id); - if let Some(parent) = map.find(parent_id); - then { - let expr = match parent { - Node::Block(block) => { - if let Some(parent_expr) = block.expr { - parent_expr - } else { - return false; - } - }, - Node::Expr(expr) => expr, - _ => return false, - }; - - matches!(expr.kind, ExprKind::Cast(..)) - } else { - false - } - } -} - pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option) { // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) { @@ -45,8 +20,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option, expr: &Expr<'_>, msrv: &Option cast_expr, - _ => unreachable!("expr should be a cast as checked by expr_cast_chain_tys"), - }; - let ptr_snippet = snippet_opt(cx, cast_expr.span).unwrap(); + let ptr_snippet = snippet_opt(cx, left_cast.span).unwrap(); - let (mutbl_fn_str, mutbl_ptr_str) = match to_slice_ty.mutbl { + let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl { Mutability::Mut => ("_mut", "mut"), Mutability::Not => ("", "const"), }; let sugg = format!( - "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {to_slice_ty}, ..)" + "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)", + // get just the ty from the TypeAndMut so that the printed type isn't something like `mut + // T`, extract just the `T` + end_ty.ty ); diag.span_suggestion( @@ -86,6 +65,31 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option, expr: &Expr<'_>) -> bool { + let map = cx.tcx.hir(); + if_chain! { + if let Some(parent_id) = map.find_parent_node(expr.hir_id); + if let Some(parent) = map.find(parent_id); + then { + let expr = match parent { + Node::Block(block) => { + if let Some(parent_expr) = block.expr { + parent_expr + } else { + return false; + } + }, + Node::Expr(expr) => expr, + _ => return false, + }; + + matches!(expr.kind, ExprKind::Cast(..)) + } else { + false + } + } +} + /// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if /// the type is one of those slices fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option> { @@ -98,18 +102,43 @@ fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option> { } } -/// Returns the pair (original ptr T, final ptr U) if the expression is composed of casts +struct CastChainInfo<'expr, 'tcx> { + /// The left most part of the cast chain, or in other words, the first cast in the chain + /// Used for diagnostics + left_cast: &'expr Expr<'expr>, + /// The starting type of the cast chain + start_ty: TypeAndMut<'tcx>, + /// The final type of the cast chain + end_ty: TypeAndMut<'tcx>, +} + +// FIXME(asquared31415): unbounded recursion linear with the number of casts in an expression +/// Returns a `CastChainInfo` with the left-most cast in the chain and the original ptr T and final +/// ptr U if the expression is composed of casts. /// Returns None if the expr is not a Cast -fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<(TypeAndMut<'tcx>, TypeAndMut<'tcx>)> { +fn expr_cast_chain_tys<'tcx, 'expr>(cx: &LateContext<'tcx>, expr: &Expr<'expr>) -> Option> { if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind { let cast_to = cx.typeck_results().expr_ty(expr); let to_slice_ty = get_raw_slice_ty_mut(cast_to)?; - if let Some((inner_from_ty, _inner_to_ty)) = expr_cast_chain_tys(cx, cast_expr) { - Some((inner_from_ty, to_slice_ty)) + if let Some(CastChainInfo { + left_cast, + start_ty, + end_ty: _, + }) = expr_cast_chain_tys(cx, cast_expr) + { + Some(CastChainInfo { + left_cast, + start_ty, + end_ty: to_slice_ty, + }) } else { let cast_from = cx.typeck_results().expr_ty(cast_expr); let from_slice_ty = get_raw_slice_ty_mut(cast_from)?; - Some((from_slice_ty, to_slice_ty)) + Some(CastChainInfo { + left_cast: cast_expr, + start_ty: from_slice_ty, + end_ty: to_slice_ty, + }) } } else { None diff --git a/tests/ui/cast_slice_different_sizes.rs b/tests/ui/cast_slice_different_sizes.rs index 7ec137cc5b293..24d7eb28a197a 100644 --- a/tests/ui/cast_slice_different_sizes.rs +++ b/tests/ui/cast_slice_different_sizes.rs @@ -62,6 +62,16 @@ fn bar2(x: *mut [u16]) -> *mut [u8] { x as _ } +// constify +fn bar3(x: *mut [u16]) -> *const [u8] { + x as _ +} + +// unconstify +fn bar4(x: *const [u16]) -> *mut [u8] { + x as _ +} + // function returns plus blocks fn blocks(x: *mut [u16]) -> *mut [u8] { ({ x }) as _ diff --git a/tests/ui/cast_slice_different_sizes.stderr b/tests/ui/cast_slice_different_sizes.stderr index 0e018b62e60ae..40721dcd05d5d 100644 --- a/tests/ui/cast_slice_different_sizes.stderr +++ b/tests/ui/cast_slice_different_sizes.stderr @@ -46,58 +46,76 @@ error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (elem --> $DIR/cast_slice_different_sizes.rs:38:27 | LL | let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const u8, ..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)` -error: casting between raw pointers to `[mut u16]` (element size 2) and `[mut u8]` (element size 1) does not adjust the count +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count --> $DIR/cast_slice_different_sizes.rs:53:36 | LL | fn bar(x: *mut [u16]) -> *mut [u8] { | ____________________________________^ LL | | x as *mut [u8] LL | | } - | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut mut u8, ..)` + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` -error: casting between raw pointers to `[mut u16]` (element size 2) and `[mut u8]` (element size 1) does not adjust the count +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count --> $DIR/cast_slice_different_sizes.rs:57:36 | LL | fn uwu(x: *mut [u16]) -> *mut [u8] { | ____________________________________^ LL | | x as *mut _ LL | | } - | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut mut u8, ..)` + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` -error: casting between raw pointers to `[mut u16]` (element size 2) and `[mut u8]` (element size 1) does not adjust the count +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count --> $DIR/cast_slice_different_sizes.rs:61:37 | LL | fn bar2(x: *mut [u16]) -> *mut [u8] { | _____________________________________^ LL | | x as _ LL | | } - | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut mut u8, ..)` + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` -error: casting between raw pointers to `[mut u16]` (element size 2) and `[mut u8]` (element size 1) does not adjust the count +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count --> $DIR/cast_slice_different_sizes.rs:66:39 | +LL | fn bar3(x: *mut [u16]) -> *const [u8] { + | _______________________________________^ +LL | | x as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(x as *const u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:71:39 + | +LL | fn bar4(x: *const [u16]) -> *mut [u8] { + | _______________________________________^ +LL | | x as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:76:39 + | LL | fn blocks(x: *mut [u16]) -> *mut [u8] { | _______________________________________^ LL | | ({ x }) as _ LL | | } - | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut mut u8, ..)` + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)` -error: casting between raw pointers to `[mut u16]` (element size 2) and `[mut u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:70:44 +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:80:44 | LL | fn more_blocks(x: *mut [u16]) -> *mut [u8] { | ____________________________________________^ LL | | { ({ x }) as _ } LL | | } - | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut mut u8, ..)` + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)` -error: casting between raw pointers to `[mut u16]` (element size 2) and `[mut u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:71:5 +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:81:5 | LL | { ({ x }) as _ } - | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut mut u8, ..)` + | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)` -error: aborting due to 12 previous errors +error: aborting due to 14 previous errors From ec3afba5d48427801a490ba91e6ce6d0bd3bfae9 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 14 Feb 2022 13:20:47 +0100 Subject: [PATCH 098/129] Make clippy inspector more precise. --- clippy_lints/src/utils/inspector.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index e4abfd07866a2..37b114a0cfbc2 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -48,7 +48,13 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { println!("impl item `{}`", item.ident.name); match cx.tcx.visibility(item.def_id) { ty::Visibility::Public => println!("public"), - ty::Visibility::Restricted(def_id) => println!("visible in module `{}`", cx.tcx.def_path_str(def_id)), + ty::Visibility::Restricted(def_id) => { + if def_id.is_top_level_module() { + println!("visible crate wide") + } else { + println!("visible in module `{}`", cx.tcx.def_path_str(def_id)) + } + }, ty::Visibility::Invisible => println!("invisible"), } match item.kind { @@ -359,7 +365,13 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { println!("item `{}`", item.ident.name); match cx.tcx.visibility(item.def_id) { ty::Visibility::Public => println!("public"), - ty::Visibility::Restricted(def_id) => println!("visible in module `{}`", cx.tcx.def_path_str(def_id)), + ty::Visibility::Restricted(def_id) => { + if def_id.is_top_level_module() { + println!("visible crate wide") + } else { + println!("visible in module `{}`", cx.tcx.def_path_str(def_id)) + } + }, ty::Visibility::Invisible => println!("invisible"), } match item.kind { From b3de32ba3cc2985c4e1d890732548a99fd55bba0 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 3 Apr 2022 20:28:47 -0400 Subject: [PATCH 099/129] Add `rename_lint` command --- clippy_dev/Cargo.toml | 1 + clippy_dev/src/lib.rs | 1 + clippy_dev/src/main.rs | 33 +++- clippy_dev/src/update_lints.rs | 308 ++++++++++++++++++++++++++++-- clippy_lints/src/renamed_lints.rs | 50 ++--- tests/ui/rename.fixed | 84 ++++---- tests/ui/rename.rs | 84 ++++---- tests/ui/rename.stderr | 180 ++++++++--------- 8 files changed, 523 insertions(+), 218 deletions(-) diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 1f2d8adecee74..7bca17dc0fdc1 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.1" edition = "2021" [dependencies] +aho-corasick = "0.7" clap = "2.33" indoc = "1.0" itertools = "0.10.1" diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 414b403827d9e..c4bb0b97e25f8 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(let_chains)] #![feature(let_else)] #![feature(once_cell)] #![feature(rustc_private)] diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 30a241c8ba151..b30f67e69599b 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -18,9 +18,9 @@ fn main() { if matches.is_present("print-only") { update_lints::print_lints(); } else if matches.is_present("check") { - update_lints::run(update_lints::UpdateMode::Check); + update_lints::update(update_lints::UpdateMode::Check); } else { - update_lints::run(update_lints::UpdateMode::Change); + update_lints::update(update_lints::UpdateMode::Change); } }, ("new_lint", Some(matches)) => { @@ -30,7 +30,7 @@ fn main() { matches.value_of("category"), matches.is_present("msrv"), ) { - Ok(_) => update_lints::run(update_lints::UpdateMode::Change), + Ok(_) => update_lints::update(update_lints::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {}", e), } }, @@ -59,6 +59,12 @@ fn main() { let filename = matches.value_of("filename").unwrap(); lint::run(filename); }, + ("rename_lint", Some(matches)) => { + let old_name = matches.value_of("old_name").unwrap(); + let new_name = matches.value_of("new_name").unwrap_or(old_name); + let uplift = matches.is_present("uplift"); + update_lints::rename(old_name, new_name, uplift); + }, _ => {}, } } @@ -232,5 +238,26 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .help("The path to a file to lint"), ), ) + .subcommand( + SubCommand::with_name("rename_lint") + .about("Renames the given lint") + .arg( + Arg::with_name("old_name") + .index(1) + .required(true) + .help("The name of the lint to rename"), + ) + .arg( + Arg::with_name("new_name") + .index(2) + .required_unless("uplift") + .help("The new name of the lint"), + ) + .arg( + Arg::with_name("uplift") + .long("uplift") + .help("This lint will be uplifted into rustc"), + ), + ) .get_matches() } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index f15b00ecad1e6..bbce3875e1dba 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,11 +1,13 @@ -use core::fmt::Write; +use aho_corasick::AhoCorasickBuilder; +use core::fmt::Write as _; use itertools::Itertools; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fs; -use std::path::Path; -use walkdir::WalkDir; +use std::io::{self, Read as _, Seek as _, Write as _}; +use std::path::{Path, PathBuf}; +use walkdir::{DirEntry, WalkDir}; use crate::clippy_project_root; @@ -30,12 +32,19 @@ pub enum UpdateMode { /// # Panics /// /// Panics if a file path could not read from or then written to -#[allow(clippy::too_many_lines)] -pub fn run(update_mode: UpdateMode) { +pub fn update(update_mode: UpdateMode) { let (lints, deprecated_lints, renamed_lints) = gather_all(); + generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); +} - let internal_lints = Lint::internal_lints(&lints); - let usable_lints = Lint::usable_lints(&lints); +fn generate_lint_files( + update_mode: UpdateMode, + lints: &[Lint], + deprecated_lints: &[DeprecatedLint], + renamed_lints: &[RenamedLint], +) { + let internal_lints = Lint::internal_lints(lints); + let usable_lints = Lint::usable_lints(lints); let mut sorted_usable_lints = usable_lints.clone(); sorted_usable_lints.sort_by_key(|lint| lint.name.clone()); @@ -87,7 +96,7 @@ pub fn run(update_mode: UpdateMode) { process_file( "clippy_lints/src/lib.deprecated.rs", update_mode, - &gen_deprecated(&deprecated_lints), + &gen_deprecated(deprecated_lints), ); let all_group_lints = usable_lints.iter().filter(|l| { @@ -108,10 +117,10 @@ pub fn run(update_mode: UpdateMode) { ); } - let content = gen_deprecated_lints_test(&deprecated_lints); + let content = gen_deprecated_lints_test(deprecated_lints); process_file("tests/ui/deprecated.rs", update_mode, &content); - let content = gen_renamed_lints_test(&renamed_lints); + let content = gen_renamed_lints_test(renamed_lints); process_file("tests/ui/rename.rs", update_mode, &content); } @@ -134,6 +143,209 @@ pub fn print_lints() { println!("there are {} lints", usable_lint_count); } +/// Runs the `rename_lint` command. +/// +/// This does the following: +/// * Adds an entry to `renamed_lints.rs`. +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). +/// * Renames the lint struct to the new name. +/// * Renames the module containing the lint struct to the new name if it shares a name with the +/// lint. +/// +/// # Panics +/// Panics for the following conditions: +/// * If a file path could not read from or then written to +/// * If either lint name has a prefix +/// * If `old_name` doesn't name an existing lint. +/// * If `old_name` names a deprecated or renamed lint. +#[allow(clippy::too_many_lines)] +pub fn rename(old_name: &str, new_name: &str, uplift: bool) { + if let Some((prefix, _)) = old_name.split_once("::") { + panic!("`{}` should not contain the `{}` prefix", old_name, prefix); + } + if let Some((prefix, _)) = new_name.split_once("::") { + panic!("`{}` should not contain the `{}` prefix", new_name, prefix); + } + + let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); + let mut old_lint_index = None; + let mut found_new_name = false; + for (i, lint) in lints.iter().enumerate() { + if lint.name == old_name { + old_lint_index = Some(i); + } else if lint.name == new_name { + found_new_name = true; + } + } + let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name)); + + let lint = RenamedLint { + old_name: format!("clippy::{}", old_name), + new_name: if uplift { + new_name.into() + } else { + format!("clippy::{}", new_name) + }, + }; + + // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in + // case. + assert!( + !renamed_lints.iter().any(|l| lint.old_name == l.old_name), + "`{}` has already been renamed", + old_name + ); + assert!( + !deprecated_lints.iter().any(|l| lint.old_name == l.name), + "`{}` has already been deprecated", + old_name + ); + + // Update all lint level attributes. (`clippy::lint_name`) + for file in WalkDir::new(clippy_project_root()) + .into_iter() + .map(Result::unwrap) + .filter(|f| { + let name = f.path().file_name(); + let ext = f.path().extension(); + (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) + && name != Some(OsStr::new("rename.rs")) + && name != Some(OsStr::new("renamed_lints.rs")) + }) + { + rewrite_file(file.path(), |s| { + replace_ident_like(s, &[(&lint.old_name, &lint.new_name)]) + }); + } + + renamed_lints.push(lint); + renamed_lints.sort_by(|lhs, rhs| { + lhs.new_name + .starts_with("clippy::") + .cmp(&rhs.new_name.starts_with("clippy::")) + .reverse() + .then_with(|| lhs.old_name.cmp(&rhs.old_name)) + }); + + write_file( + Path::new("clippy_lints/src/renamed_lints.rs"), + &gen_renamed_lints_list(&renamed_lints), + ); + + if uplift { + write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); + println!( + "`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.", + old_name + ); + } else if found_new_name { + write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); + println!( + "`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.", + new_name + ); + } else { + // Rename the lint struct and source files sharing a name with the lint. + let lint = &mut lints[old_lint_index]; + let old_name_upper = old_name.to_uppercase(); + let new_name_upper = new_name.to_uppercase(); + lint.name = new_name.into(); + + // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. + if try_rename_file( + Path::new(&format!("tests/ui/{}.rs", old_name)), + Path::new(&format!("tests/ui/{}.rs", new_name)), + ) { + try_rename_file( + Path::new(&format!("tests/ui/{}.stderr", old_name)), + Path::new(&format!("tests/ui/{}.stderr", new_name)), + ); + try_rename_file( + Path::new(&format!("tests/ui/{}.fixed", old_name)), + Path::new(&format!("tests/ui/{}.fixed", new_name)), + ); + } + + // Try to rename the file containing the lint if the file name matches the lint's name. + let replacements; + let replacements = if lint.module == old_name + && try_rename_file( + Path::new(&format!("clippy_lints/src/{}.rs", old_name)), + Path::new(&format!("clippy_lints/src/{}.rs", new_name)), + ) { + // Edit the module name in the lint list. Note there could be multiple lints. + for lint in lints.iter_mut().filter(|l| l.module == old_name) { + lint.module = new_name.into(); + } + replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; + replacements.as_slice() + } else if !lint.module.contains("::") + // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` + && try_rename_file( + Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)), + Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)), + ) + { + // Edit the module name in the lint list. Note there could be multiple lints, or none. + let renamed_mod = format!("{}::{}", lint.module, old_name); + for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { + lint.module = format!("{}::{}", lint.module, new_name); + } + replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; + replacements.as_slice() + } else { + replacements = [(&*old_name_upper, &*new_name_upper), ("", "")]; + &replacements[0..1] + }; + + // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being + // renamed. + for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) { + rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); + } + + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("{} has been successfully renamed", old_name); + } + + println!("note: `cargo uitest` still needs to be run to update the test results"); +} + +/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there +/// were no replacements. +fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option { + fn is_ident_char(c: u8) -> bool { + matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') + } + + let searcher = AhoCorasickBuilder::new() + .dfa(true) + .match_kind(aho_corasick::MatchKind::LeftmostLongest) + .build_with_size::(replacements.iter().map(|&(x, _)| x.as_bytes())) + .unwrap(); + + let mut result = String::with_capacity(contents.len() + 1024); + let mut pos = 0; + let mut edited = false; + for m in searcher.find_iter(contents) { + let (old, new) = replacements[m.pattern()]; + result.push_str(&contents[pos..m.start()]); + result.push_str( + if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) + && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0)) + { + edited = true; + new + } else { + old + }, + ); + pos = m.end(); + } + result.push_str(&contents[pos..]); + edited.then(|| result) +} + fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } @@ -323,19 +535,27 @@ fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { res } +fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String { + const HEADER: &str = "\ + // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\ + #[rustfmt::skip]\n\ + pub static RENAMED_LINTS: &[(&str, &str)] = &[\n"; + + let mut res = String::from(HEADER); + for lint in lints { + writeln!(res, " (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap(); + } + res.push_str("];\n"); + res +} + /// Gathers all lints defined in `clippy_lints/src` fn gather_all() -> (Vec, Vec, Vec) { let mut lints = Vec::with_capacity(1000); let mut deprecated_lints = Vec::with_capacity(50); let mut renamed_lints = Vec::with_capacity(50); - let root_path = clippy_project_root().join("clippy_lints/src"); - for (rel_path, file) in WalkDir::new(&root_path) - .into_iter() - .map(Result::unwrap) - .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) - .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) - { + for (rel_path, file) in clippy_lints_src_files() { let path = file.path(); let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); @@ -362,6 +582,14 @@ fn gather_all() -> (Vec, Vec, Vec) { (lints, deprecated_lints, renamed_lints) } +fn clippy_lints_src_files() -> impl Iterator { + let root_path = clippy_project_root().join("clippy_lints/src"); + let iter = WalkDir::new(&root_path).into_iter(); + iter.map(Result::unwrap) + .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) + .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) +} + macro_rules! match_tokens { ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => { { @@ -526,6 +754,52 @@ fn replace_region_in_text<'a>( Ok(res) } +fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { + match fs::OpenOptions::new().create_new(true).write(true).open(new_name) { + Ok(file) => drop(file), + Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, + Err(e) => panic_file(e, new_name, "create"), + }; + match fs::rename(old_name, new_name) { + Ok(()) => true, + Err(e) => { + drop(fs::remove_file(new_name)); + if e.kind() == io::ErrorKind::NotFound { + false + } else { + panic_file(e, old_name, "rename"); + } + }, + } +} + +#[allow(clippy::needless_pass_by_value)] +fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { + panic!("failed to {} file `{}`: {}", action, name.display(), error) +} + +fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { + let mut file = fs::OpenOptions::new() + .write(true) + .read(true) + .open(path) + .unwrap_or_else(|e| panic_file(e, path, "open")); + let mut buf = String::new(); + file.read_to_string(&mut buf) + .unwrap_or_else(|e| panic_file(e, path, "read")); + if let Some(new_contents) = f(&buf) { + file.rewind().unwrap_or_else(|e| panic_file(e, path, "write")); + file.write_all(new_contents.as_bytes()) + .unwrap_or_else(|e| panic_file(e, path, "write")); + file.set_len(new_contents.len() as u64) + .unwrap_or_else(|e| panic_file(e, path, "write")); + } +} + +fn write_file(path: &Path, contents: &str) { + fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write")); +} + #[cfg(test)] mod tests { use super::*; diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index e10dc0e1bfeb7..bfc03116fe2d9 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -1,37 +1,39 @@ +// This file is managed by `cargo dev rename_lint`. Prefer using that when possible. + +#[rustfmt::skip] pub static RENAMED_LINTS: &[(&str, &str)] = &[ - ("clippy::stutter", "clippy::module_name_repetitions"), - ("clippy::new_without_default_derive", "clippy::new_without_default"), - ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), - ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), - ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), - ("clippy::box_vec", "clippy::box_collection"), ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"), ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"), - ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"), - ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"), - ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"), - ("clippy::option_unwrap_used", "clippy::unwrap_used"), - ("clippy::result_unwrap_used", "clippy::unwrap_used"), - ("clippy::option_expect_used", "clippy::expect_used"), - ("clippy::result_expect_used", "clippy::expect_used"), + ("clippy::box_vec", "clippy::box_collection"), + ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), + ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), + ("clippy::disallowed_method", "clippy::disallowed_methods"), + ("clippy::disallowed_type", "clippy::disallowed_types"), ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"), ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"), ("clippy::identity_conversion", "clippy::useless_conversion"), - ("clippy::zero_width_space", "clippy::invisible_characters"), - ("clippy::single_char_push_str", "clippy::single_char_add_str"), ("clippy::if_let_some_result", "clippy::match_result_ok"), - ("clippy::disallowed_type", "clippy::disallowed_types"), - ("clippy::disallowed_method", "clippy::disallowed_methods"), + ("clippy::new_without_default_derive", "clippy::new_without_default"), + ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), + ("clippy::option_expect_used", "clippy::expect_used"), + ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"), + ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"), + ("clippy::option_unwrap_used", "clippy::unwrap_used"), ("clippy::ref_in_deref", "clippy::needless_borrow"), + ("clippy::result_expect_used", "clippy::expect_used"), + ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"), + ("clippy::result_unwrap_used", "clippy::unwrap_used"), + ("clippy::single_char_push_str", "clippy::single_char_add_str"), + ("clippy::stutter", "clippy::module_name_repetitions"), ("clippy::to_string_in_display", "clippy::recursive_format_impl"), - // uplifted lints - ("clippy::invalid_ref", "invalid_value"), - ("clippy::into_iter_on_array", "array_into_iter"), - ("clippy::unused_label", "unused_labels"), + ("clippy::zero_width_space", "clippy::invisible_characters"), ("clippy::drop_bounds", "drop_bounds"), - ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), - ("clippy::panic_params", "non_fmt_panics"), - ("clippy::unknown_clippy_lints", "unknown_lints"), + ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), + ("clippy::invalid_ref", "invalid_value"), ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), + ("clippy::panic_params", "non_fmt_panics"), + ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), + ("clippy::unknown_clippy_lints", "unknown_lints"), + ("clippy::unused_label", "unused_labels"), ]; diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 325f63a64dd90..9c4079ad6d306 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -4,67 +4,67 @@ // run-rustfix -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::new_without_default)] -#![allow(clippy::cognitive_complexity)] -#![allow(clippy::redundant_static_lifetimes)] -#![allow(clippy::bind_instead_of_map)] -#![allow(clippy::box_collection)] #![allow(clippy::blocks_in_if_conditions)] -#![allow(clippy::map_unwrap_or)] -#![allow(clippy::unwrap_used)] -#![allow(clippy::expect_used)] +#![allow(clippy::box_collection)] +#![allow(clippy::redundant_static_lifetimes)] +#![allow(clippy::cognitive_complexity)] +#![allow(clippy::disallowed_methods)] +#![allow(clippy::disallowed_types)] #![allow(clippy::for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] -#![allow(clippy::invisible_characters)] -#![allow(clippy::single_char_add_str)] #![allow(clippy::match_result_ok)] -#![allow(clippy::disallowed_types)] -#![allow(clippy::disallowed_methods)] +#![allow(clippy::new_without_default)] +#![allow(clippy::bind_instead_of_map)] +#![allow(clippy::expect_used)] +#![allow(clippy::map_unwrap_or)] +#![allow(clippy::unwrap_used)] #![allow(clippy::needless_borrow)] +#![allow(clippy::single_char_add_str)] +#![allow(clippy::module_name_repetitions)] #![allow(clippy::recursive_format_impl)] -#![allow(invalid_value)] -#![allow(array_into_iter)] -#![allow(unused_labels)] +#![allow(clippy::invisible_characters)] #![allow(drop_bounds)] -#![allow(temporary_cstring_as_ptr)] -#![allow(non_fmt_panics)] -#![allow(unknown_lints)] +#![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] +#![allow(invalid_value)] #![allow(enum_intrinsics_non_enums)] -#![warn(clippy::module_name_repetitions)] -#![warn(clippy::new_without_default)] -#![warn(clippy::cognitive_complexity)] -#![warn(clippy::redundant_static_lifetimes)] -#![warn(clippy::bind_instead_of_map)] -#![warn(clippy::box_collection)] +#![allow(non_fmt_panics)] +#![allow(temporary_cstring_as_ptr)] +#![allow(unknown_lints)] +#![allow(unused_labels)] #![warn(clippy::blocks_in_if_conditions)] #![warn(clippy::blocks_in_if_conditions)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::unwrap_used)] -#![warn(clippy::unwrap_used)] -#![warn(clippy::expect_used)] -#![warn(clippy::expect_used)] +#![warn(clippy::box_collection)] +#![warn(clippy::redundant_static_lifetimes)] +#![warn(clippy::cognitive_complexity)] +#![warn(clippy::disallowed_methods)] +#![warn(clippy::disallowed_types)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::useless_conversion)] -#![warn(clippy::invisible_characters)] -#![warn(clippy::single_char_add_str)] #![warn(clippy::match_result_ok)] -#![warn(clippy::disallowed_types)] -#![warn(clippy::disallowed_methods)] +#![warn(clippy::new_without_default)] +#![warn(clippy::bind_instead_of_map)] +#![warn(clippy::expect_used)] +#![warn(clippy::map_unwrap_or)] +#![warn(clippy::map_unwrap_or)] +#![warn(clippy::unwrap_used)] #![warn(clippy::needless_borrow)] +#![warn(clippy::expect_used)] +#![warn(clippy::map_unwrap_or)] +#![warn(clippy::unwrap_used)] +#![warn(clippy::single_char_add_str)] +#![warn(clippy::module_name_repetitions)] #![warn(clippy::recursive_format_impl)] -#![warn(invalid_value)] -#![warn(array_into_iter)] -#![warn(unused_labels)] +#![warn(clippy::invisible_characters)] #![warn(drop_bounds)] -#![warn(temporary_cstring_as_ptr)] -#![warn(non_fmt_panics)] -#![warn(unknown_lints)] +#![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] +#![warn(invalid_value)] #![warn(enum_intrinsics_non_enums)] +#![warn(non_fmt_panics)] +#![warn(temporary_cstring_as_ptr)] +#![warn(unknown_lints)] +#![warn(unused_labels)] fn main() {} diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index a21b4a242880b..e83e66b7fbd42 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -4,67 +4,67 @@ // run-rustfix -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::new_without_default)] -#![allow(clippy::cognitive_complexity)] -#![allow(clippy::redundant_static_lifetimes)] -#![allow(clippy::bind_instead_of_map)] -#![allow(clippy::box_collection)] #![allow(clippy::blocks_in_if_conditions)] -#![allow(clippy::map_unwrap_or)] -#![allow(clippy::unwrap_used)] -#![allow(clippy::expect_used)] +#![allow(clippy::box_collection)] +#![allow(clippy::redundant_static_lifetimes)] +#![allow(clippy::cognitive_complexity)] +#![allow(clippy::disallowed_methods)] +#![allow(clippy::disallowed_types)] #![allow(clippy::for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] -#![allow(clippy::invisible_characters)] -#![allow(clippy::single_char_add_str)] #![allow(clippy::match_result_ok)] -#![allow(clippy::disallowed_types)] -#![allow(clippy::disallowed_methods)] +#![allow(clippy::new_without_default)] +#![allow(clippy::bind_instead_of_map)] +#![allow(clippy::expect_used)] +#![allow(clippy::map_unwrap_or)] +#![allow(clippy::unwrap_used)] #![allow(clippy::needless_borrow)] +#![allow(clippy::single_char_add_str)] +#![allow(clippy::module_name_repetitions)] #![allow(clippy::recursive_format_impl)] -#![allow(invalid_value)] -#![allow(array_into_iter)] -#![allow(unused_labels)] +#![allow(clippy::invisible_characters)] #![allow(drop_bounds)] -#![allow(temporary_cstring_as_ptr)] -#![allow(non_fmt_panics)] -#![allow(unknown_lints)] +#![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] +#![allow(invalid_value)] #![allow(enum_intrinsics_non_enums)] -#![warn(clippy::stutter)] -#![warn(clippy::new_without_default_derive)] -#![warn(clippy::cyclomatic_complexity)] -#![warn(clippy::const_static_lifetime)] -#![warn(clippy::option_and_then_some)] -#![warn(clippy::box_vec)] +#![allow(non_fmt_panics)] +#![allow(temporary_cstring_as_ptr)] +#![allow(unknown_lints)] +#![allow(unused_labels)] #![warn(clippy::block_in_if_condition_expr)] #![warn(clippy::block_in_if_condition_stmt)] -#![warn(clippy::option_map_unwrap_or)] -#![warn(clippy::option_map_unwrap_or_else)] -#![warn(clippy::result_map_unwrap_or_else)] -#![warn(clippy::option_unwrap_used)] -#![warn(clippy::result_unwrap_used)] -#![warn(clippy::option_expect_used)] -#![warn(clippy::result_expect_used)] +#![warn(clippy::box_vec)] +#![warn(clippy::const_static_lifetime)] +#![warn(clippy::cyclomatic_complexity)] +#![warn(clippy::disallowed_method)] +#![warn(clippy::disallowed_type)] #![warn(clippy::for_loop_over_option)] #![warn(clippy::for_loop_over_result)] #![warn(clippy::identity_conversion)] -#![warn(clippy::zero_width_space)] -#![warn(clippy::single_char_push_str)] #![warn(clippy::if_let_some_result)] -#![warn(clippy::disallowed_type)] -#![warn(clippy::disallowed_method)] +#![warn(clippy::new_without_default_derive)] +#![warn(clippy::option_and_then_some)] +#![warn(clippy::option_expect_used)] +#![warn(clippy::option_map_unwrap_or)] +#![warn(clippy::option_map_unwrap_or_else)] +#![warn(clippy::option_unwrap_used)] #![warn(clippy::ref_in_deref)] +#![warn(clippy::result_expect_used)] +#![warn(clippy::result_map_unwrap_or_else)] +#![warn(clippy::result_unwrap_used)] +#![warn(clippy::single_char_push_str)] +#![warn(clippy::stutter)] #![warn(clippy::to_string_in_display)] -#![warn(clippy::invalid_ref)] -#![warn(clippy::into_iter_on_array)] -#![warn(clippy::unused_label)] +#![warn(clippy::zero_width_space)] #![warn(clippy::drop_bounds)] -#![warn(clippy::temporary_cstring_as_ptr)] -#![warn(clippy::panic_params)] -#![warn(clippy::unknown_clippy_lints)] +#![warn(clippy::into_iter_on_array)] #![warn(clippy::invalid_atomic_ordering)] +#![warn(clippy::invalid_ref)] #![warn(clippy::mem_discriminant_non_enum)] +#![warn(clippy::panic_params)] +#![warn(clippy::temporary_cstring_as_ptr)] +#![warn(clippy::unknown_clippy_lints)] +#![warn(clippy::unused_label)] fn main() {} diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 54e12d5fae5ea..f811b10d01710 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,22 +1,22 @@ -error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` +error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` --> $DIR/rename.rs:35:9 | -LL | #![warn(clippy::stutter)] - | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` +LL | #![warn(clippy::block_in_if_condition_expr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` | = note: `-D renamed-and-removed-lints` implied by `-D warnings` -error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` +error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` --> $DIR/rename.rs:36:9 | -LL | #![warn(clippy::new_without_default_derive)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` +LL | #![warn(clippy::block_in_if_condition_stmt)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` -error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` +error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` --> $DIR/rename.rs:37:9 | -LL | #![warn(clippy::cyclomatic_complexity)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` +LL | #![warn(clippy::box_vec)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` --> $DIR/rename.rs:38:9 @@ -24,59 +24,59 @@ error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redunda LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` -error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` +error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` --> $DIR/rename.rs:39:9 | -LL | #![warn(clippy::option_and_then_some)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` +LL | #![warn(clippy::cyclomatic_complexity)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` -error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` +error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` --> $DIR/rename.rs:40:9 | -LL | #![warn(clippy::box_vec)] - | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` +LL | #![warn(clippy::disallowed_method)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` -error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` +error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` --> $DIR/rename.rs:41:9 | -LL | #![warn(clippy::block_in_if_condition_expr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` +LL | #![warn(clippy::disallowed_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` -error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` +error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` --> $DIR/rename.rs:42:9 | -LL | #![warn(clippy::block_in_if_condition_stmt)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` +LL | #![warn(clippy::for_loop_over_option)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` -error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` --> $DIR/rename.rs:43:9 | -LL | #![warn(clippy::option_map_unwrap_or)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::for_loop_over_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` -error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` --> $DIR/rename.rs:44:9 | -LL | #![warn(clippy::option_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::identity_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` -error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` --> $DIR/rename.rs:45:9 | -LL | #![warn(clippy::result_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::if_let_some_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` -error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` +error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` --> $DIR/rename.rs:46:9 | -LL | #![warn(clippy::option_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` +LL | #![warn(clippy::new_without_default_derive)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` -error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` +error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` --> $DIR/rename.rs:47:9 | -LL | #![warn(clippy::result_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` +LL | #![warn(clippy::option_and_then_some)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` --> $DIR/rename.rs:48:9 @@ -84,77 +84,77 @@ error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_use LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` -error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` +error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` --> $DIR/rename.rs:49:9 | -LL | #![warn(clippy::result_expect_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` +LL | #![warn(clippy::option_map_unwrap_or)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` --> $DIR/rename.rs:50:9 | -LL | #![warn(clippy::for_loop_over_option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` +LL | #![warn(clippy::option_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` --> $DIR/rename.rs:51:9 | -LL | #![warn(clippy::for_loop_over_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` +LL | #![warn(clippy::option_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` -error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` +error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` --> $DIR/rename.rs:52:9 | -LL | #![warn(clippy::identity_conversion)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` +LL | #![warn(clippy::ref_in_deref)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` -error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` +error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` --> $DIR/rename.rs:53:9 | -LL | #![warn(clippy::zero_width_space)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` +LL | #![warn(clippy::result_expect_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` -error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` +error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` --> $DIR/rename.rs:54:9 | -LL | #![warn(clippy::single_char_push_str)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` +LL | #![warn(clippy::result_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` +error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` --> $DIR/rename.rs:55:9 | -LL | #![warn(clippy::if_let_some_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` +LL | #![warn(clippy::result_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` -error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` +error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` --> $DIR/rename.rs:56:9 | -LL | #![warn(clippy::disallowed_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` +LL | #![warn(clippy::single_char_push_str)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` -error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` +error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` --> $DIR/rename.rs:57:9 | -LL | #![warn(clippy::disallowed_method)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` +LL | #![warn(clippy::stutter)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` -error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` +error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` --> $DIR/rename.rs:58:9 | -LL | #![warn(clippy::ref_in_deref)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` +LL | #![warn(clippy::to_string_in_display)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` -error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` +error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` --> $DIR/rename.rs:59:9 | -LL | #![warn(clippy::to_string_in_display)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` +LL | #![warn(clippy::zero_width_space)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` -error: lint `clippy::invalid_ref` has been renamed to `invalid_value` +error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` --> $DIR/rename.rs:60:9 | -LL | #![warn(clippy::invalid_ref)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` +LL | #![warn(clippy::drop_bounds)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` --> $DIR/rename.rs:61:9 @@ -162,23 +162,23 @@ error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` -error: lint `clippy::unused_label` has been renamed to `unused_labels` +error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` --> $DIR/rename.rs:62:9 | -LL | #![warn(clippy::unused_label)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` +LL | #![warn(clippy::invalid_atomic_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` -error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` +error: lint `clippy::invalid_ref` has been renamed to `invalid_value` --> $DIR/rename.rs:63:9 | -LL | #![warn(clippy::drop_bounds)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` +LL | #![warn(clippy::invalid_ref)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` -error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` +error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` --> $DIR/rename.rs:64:9 | -LL | #![warn(clippy::temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` +LL | #![warn(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` --> $DIR/rename.rs:65:9 @@ -186,23 +186,23 @@ error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` -error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` --> $DIR/rename.rs:66:9 | -LL | #![warn(clippy::unknown_clippy_lints)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` +LL | #![warn(clippy::temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` -error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` +error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` --> $DIR/rename.rs:67:9 | -LL | #![warn(clippy::invalid_atomic_ordering)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` +LL | #![warn(clippy::unknown_clippy_lints)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` -error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` +error: lint `clippy::unused_label` has been renamed to `unused_labels` --> $DIR/rename.rs:68:9 | -LL | #![warn(clippy::mem_discriminant_non_enum)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` +LL | #![warn(clippy::unused_label)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: aborting due to 34 previous errors From f2902494614643509685c3bf974287ac9844b2fd Mon Sep 17 00:00:00 2001 From: goth-turtle <93494392+goth-turtle@users.noreply.github.com> Date: Sat, 23 Apr 2022 22:20:50 +0200 Subject: [PATCH 100/129] mistyped_literal_suffixes: improve suggestions for integer types Instead of just always suggesting signed suffixes regardless of size of the value, it now suggests an unsigned suffix when the value wouldn't fit into the corresponding signed type, and ignores the literal entirely if it is too big for the unsigned type as well. --- clippy_lints/src/literal_representation.rs | 50 ++++++++++++++------ tests/ui/mistyped_literal_suffix.fixed | 12 ++++- tests/ui/mistyped_literal_suffix.rs | 12 ++++- tests/ui/mistyped_literal_suffix.stderr | 54 +++++++++++++++++----- 4 files changed, 101 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index b7430f49229ae..362d45991bb54 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -42,17 +42,12 @@ declare_clippy_lint! { /// This is most probably a typo /// /// ### Known problems - /// - Recommends a signed suffix, even though the number might be too big and an unsigned - /// suffix is required + /// - Does not match on integers too large to fit in the corresponding unsigned type /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers /// /// ### Example - /// ```rust - /// // Probably mistyped - /// 2_32; - /// - /// // Good - /// 2_i32; + /// `2_32` => `2_i32` + /// `250_8 => `250_u8` /// ``` #[clippy::version = "1.30.0"] pub MISTYPED_LITERAL_SUFFIXES, @@ -310,18 +305,47 @@ impl LiteralDigitGrouping { return true; } - let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { - (exponent, &["32", "64"][..], 'f') + let (part, mistyped_suffixes, is_float) = if let Some((_, exponent)) = &mut num_lit.exponent { + (exponent, &["32", "64"][..], true) } else if num_lit.fraction.is_some() { - (&mut num_lit.integer, &["32", "64"][..], 'f') + (&mut num_lit.integer, &["32", "64"][..], true) } else { - (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i') + (&mut num_lit.integer, &["8", "16", "32", "64"][..], false) }; let mut split = part.rsplit('_'); let last_group = split.next().expect("At least one group"); if split.next().is_some() && mistyped_suffixes.contains(&last_group) { - *part = &part[..part.len() - last_group.len()]; + let main_part = &part[..part.len() - last_group.len()]; + let missing_char; + if is_float { + missing_char = 'f'; + } else { + let radix = match num_lit.radix { + Radix::Binary => 2, + Radix::Octal => 8, + Radix::Decimal => 10, + Radix::Hexadecimal => 16, + }; + if let Ok(int) = u64::from_str_radix(&main_part.replace('_', ""), radix) { + missing_char = match (last_group, int) { + ("8", i) if i8::try_from(i).is_ok() => 'i', + ("16", i) if i16::try_from(i).is_ok() => 'i', + ("32", i) if i32::try_from(i).is_ok() => 'i', + ("64", i) if i64::try_from(i).is_ok() => 'i', + ("8", u) if u8::try_from(u).is_ok() => 'u', + ("16", u) if u16::try_from(u).is_ok() => 'u', + ("32", u) if u32::try_from(u).is_ok() => 'u', + ("64", _) => 'u', + _ => { + return true; + }, + } + } else { + return true; + } + } + *part = main_part; let mut sugg = num_lit.format(); sugg.push('_'); sugg.push(missing_char); diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index 5d57638af4349..a59384e0725d7 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -5,7 +5,8 @@ unused_variables, overflowing_literals, clippy::excessive_precision, - clippy::inconsistent_digit_grouping + clippy::inconsistent_digit_grouping, + clippy::unusual_byte_groupings )] fn main() { @@ -25,5 +26,14 @@ fn main() { let fail28 = 241_251_235E723_f64; let ok29 = 42279.911_32; + // testing that the suggestion actually fits in its type + let fail30 = 127_i8; // should be i8 + let fail31 = 240_u8; // should be u8 + let ok32 = 360_8; // doesnt fit in either, should be ignored + let fail33 = 0x1234_i16; + let fail34 = 0xABCD_u16; + let ok35 = 0x12345_16; + let fail36 = 0xFFFF_FFFF_FFFF_FFFF_u64; // u64 + let _ = 1.123_45E1_f32; } diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index 12171452885d2..01e425f9f1d7b 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -5,7 +5,8 @@ unused_variables, overflowing_literals, clippy::excessive_precision, - clippy::inconsistent_digit_grouping + clippy::inconsistent_digit_grouping, + clippy::unusual_byte_groupings )] fn main() { @@ -25,5 +26,14 @@ fn main() { let fail28 = 241251235E723_64; let ok29 = 42279.911_32; + // testing that the suggestion actually fits in its type + let fail30 = 127_8; // should be i8 + let fail31 = 240_8; // should be u8 + let ok32 = 360_8; // doesnt fit in either, should be ignored + let fail33 = 0x1234_16; + let fail34 = 0xABCD_16; + let ok35 = 0x12345_16; + let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 + let _ = 1.12345E1_32; } diff --git a/tests/ui/mistyped_literal_suffix.stderr b/tests/ui/mistyped_literal_suffix.stderr index d24543c26e4b0..6fcd1f2f18050 100644 --- a/tests/ui/mistyped_literal_suffix.stderr +++ b/tests/ui/mistyped_literal_suffix.stderr @@ -1,5 +1,5 @@ error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:12:18 + --> $DIR/mistyped_literal_suffix.rs:13:18 | LL | let fail14 = 2_32; | ^^^^ help: did you mean to write: `2_i32` @@ -7,64 +7,94 @@ LL | let fail14 = 2_32; = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:13:18 + --> $DIR/mistyped_literal_suffix.rs:14:18 | LL | let fail15 = 4_64; | ^^^^ help: did you mean to write: `4_i64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:14:18 + --> $DIR/mistyped_literal_suffix.rs:15:18 | LL | let fail16 = 7_8; // | ^^^ help: did you mean to write: `7_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:15:18 + --> $DIR/mistyped_literal_suffix.rs:16:18 | LL | let fail17 = 23_16; // | ^^^^^ help: did you mean to write: `23_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:18:18 + --> $DIR/mistyped_literal_suffix.rs:19:18 | LL | let fail20 = 2__8; // | ^^^^ help: did you mean to write: `2_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:19:18 + --> $DIR/mistyped_literal_suffix.rs:20:18 | LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:22:18 + --> $DIR/mistyped_literal_suffix.rs:23:18 | LL | let fail25 = 1E2_32; | ^^^^^^ help: did you mean to write: `1E2_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:23:18 + --> $DIR/mistyped_literal_suffix.rs:24:18 | LL | let fail26 = 43E7_64; | ^^^^^^^ help: did you mean to write: `43E7_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:24:18 + --> $DIR/mistyped_literal_suffix.rs:25:18 | LL | let fail27 = 243E17_32; | ^^^^^^^^^ help: did you mean to write: `243E17_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:25:18 + --> $DIR/mistyped_literal_suffix.rs:26:18 | LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:28:13 + --> $DIR/mistyped_literal_suffix.rs:30:18 + | +LL | let fail30 = 127_8; // should be i8 + | ^^^^^ help: did you mean to write: `127_i8` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:31:18 + | +LL | let fail31 = 240_8; // should be u8 + | ^^^^^ help: did you mean to write: `240_u8` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:33:18 + | +LL | let fail33 = 0x1234_16; + | ^^^^^^^^^ help: did you mean to write: `0x1234_i16` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:34:18 + | +LL | let fail34 = 0xABCD_16; + | ^^^^^^^^^ help: did you mean to write: `0xABCD_u16` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:36:18 + | +LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:38:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` -error: aborting due to 11 previous errors +error: aborting due to 16 previous errors From b4a50e9ee5adda55ad68b0dc637433fa0936e10e Mon Sep 17 00:00:00 2001 From: goth-turtle <93494392+goth-turtle@users.noreply.github.com> Date: Sun, 24 Apr 2022 15:06:48 +0200 Subject: [PATCH 101/129] mistyped_literal_suffixes: ignore floats without exponent Previously this lint would only look at the integer part of floating point literals without an exponent, giving wrong suggestions like: ``` | 8 | let _ = 123_32.123; | ^^^^^^^^^^ help: did you mean to write: `123.123_f32` | ``` Instead, it now ignores these literals. Fixes #6129 --- clippy_lints/src/literal_representation.rs | 2 +- tests/ui/mistyped_literal_suffix.fixed | 4 ++++ tests/ui/mistyped_literal_suffix.rs | 4 ++++ tests/ui/mistyped_literal_suffix.stderr | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 362d45991bb54..269d3c62eafcd 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -308,7 +308,7 @@ impl LiteralDigitGrouping { let (part, mistyped_suffixes, is_float) = if let Some((_, exponent)) = &mut num_lit.exponent { (exponent, &["32", "64"][..], true) } else if num_lit.fraction.is_some() { - (&mut num_lit.integer, &["32", "64"][..], true) + return true; } else { (&mut num_lit.integer, &["8", "16", "32", "64"][..], false) }; diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index a59384e0725d7..a7b36d53cd26c 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -35,5 +35,9 @@ fn main() { let ok35 = 0x12345_16; let fail36 = 0xFFFF_FFFF_FFFF_FFFF_u64; // u64 + // issue #6129 + let ok37 = 123_32.123; + let ok38 = 124_64.0; + let _ = 1.123_45E1_f32; } diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index 01e425f9f1d7b..c97b31965c75a 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -35,5 +35,9 @@ fn main() { let ok35 = 0x12345_16; let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 + // issue #6129 + let ok37 = 123_32.123; + let ok38 = 124_64.0; + let _ = 1.12345E1_32; } diff --git a/tests/ui/mistyped_literal_suffix.stderr b/tests/ui/mistyped_literal_suffix.stderr index 6fcd1f2f18050..fb761d9bde452 100644 --- a/tests/ui/mistyped_literal_suffix.stderr +++ b/tests/ui/mistyped_literal_suffix.stderr @@ -91,7 +91,7 @@ LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 | ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:38:13 + --> $DIR/mistyped_literal_suffix.rs:42:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` From a85dc87c4cb0640915a3d5a5a318c2901ff839d3 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 21 Apr 2022 11:01:18 -0400 Subject: [PATCH 102/129] Add `large_include_file` lint --- CHANGELOG.md | 1 + clippy_lints/src/large_include_file.rs | 86 +++++++++++++++++++ clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_restriction.rs | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/utils/conf.rs | 4 + tests/ui-toml/large_include_file/clippy.toml | 1 + .../large_include_file/large_include_file.rs | 16 ++++ .../large_include_file.stderr | 21 +++++ tests/ui-toml/large_include_file/too_big.txt | 1 + .../toml_unknown_key/conf_unknown_key.stderr | 2 +- 11 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/large_include_file.rs create mode 100644 tests/ui-toml/large_include_file/clippy.toml create mode 100644 tests/ui-toml/large_include_file/large_include_file.rs create mode 100644 tests/ui-toml/large_include_file/large_include_file.stderr create mode 100644 tests/ui-toml/large_include_file/too_big.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 2948c24fb3b67..c88ead99fa6f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3478,6 +3478,7 @@ Released 2018-09-13 [`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays [`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups [`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant +[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs new file mode 100644 index 0000000000000..5ed275726c530 --- /dev/null +++ b/clippy_lints/src/large_include_file.rs @@ -0,0 +1,86 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::is_lint_allowed; +use clippy_utils::macros::root_macro_call_first_node; +use rustc_ast::LitKind; +use rustc_hir::Expr; +use rustc_hir::ExprKind; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the inclusion of large files via `include_bytes!()` + /// and `include_str!()` + /// + /// ### Why is this bad? + /// Including large files can increase the size of the binary + /// + /// ### Example + /// ```rust,ignore + /// let included_str = include_str!("very_large_file.txt"); + /// let included_bytes = include_bytes!("very_large_file.txt); + /// ``` + /// + /// Instead, you can load the file at runtime: + /// ```rust,ignore + /// use std::fs; + /// + /// let string = fs::read_to_string("very_large_file.txt")?; + /// let bytes = fs::read("very_large_file.txt")?; + /// ``` + #[clippy::version = "1.62.0"] + pub LARGE_INCLUDE_FILE, + restriction, + "including a large file" +} + +pub struct LargeIncludeFile { + max_file_size: u64, +} + +impl LargeIncludeFile { + #[must_use] + pub fn new(max_file_size: u64) -> Self { + Self { max_file_size } + } +} + +impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]); + +impl LateLintPass<'_> for LargeIncludeFile { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if_chain! { + if let Some(macro_call) = root_macro_call_first_node(cx, expr); + if !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id); + if cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) + || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id); + if let ExprKind::Lit(lit) = &expr.kind; + then { + let len = match &lit.node { + // include_bytes + LitKind::ByteStr(bstr) => bstr.len(), + // include_str + LitKind::Str(sym, _) => sym.as_str().len(), + _ => return, + }; + + if len as u64 <= self.max_file_size { + return; + } + + span_lint_and_note( + cx, + LARGE_INCLUDE_FILE, + expr.span, + "attempted to include a large file", + None, + &format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + ), + ); + } + } + } +} diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 8507531c8d307..e362431602794 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -209,6 +209,7 @@ store.register_lints(&[ iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR, large_const_arrays::LARGE_CONST_ARRAYS, large_enum_variant::LARGE_ENUM_VARIANT, + large_include_file::LARGE_INCLUDE_FILE, large_stack_arrays::LARGE_STACK_ARRAYS, len_zero::COMPARISON_TO_EMPTY, len_zero::LEN_WITHOUT_IS_EMPTY, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 46c79c56a47db..77ec6c83ba4b4 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -27,6 +27,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(indexing_slicing::INDEXING_SLICING), LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), LintId::of(integer_division::INTEGER_DIVISION), + LintId::of(large_include_file::LARGE_INCLUDE_FILE), LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), LintId::of(map_err_ignore::MAP_ERR_IGNORE), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 335467bf9d2ae..57d48e1762705 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -262,6 +262,7 @@ mod items_after_statements; mod iter_not_returning_iterator; mod large_const_arrays; mod large_enum_variant; +mod large_include_file; mod large_stack_arrays; mod len_zero; mod let_if_seq; @@ -884,6 +885,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_late_pass(|| Box::new(format_push_string::FormatPushString)); store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen)); + let max_include_file_size = conf.max_include_file_size; + store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index e6abe499b430d..74b0168a1794b 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -312,6 +312,10 @@ define_Conf! { (max_suggested_slice_pattern_length: u64 = 3), /// Lint: AWAIT_HOLDING_INVALID_TYPE (await_holding_invalid_types: Vec = Vec::new()), + /// Lint: LARGE_INCLUDE_FILE. + /// + /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes + (max_include_file_size: u64 = 1_000_000), } /// Search for the configuration file. diff --git a/tests/ui-toml/large_include_file/clippy.toml b/tests/ui-toml/large_include_file/clippy.toml new file mode 100644 index 0000000000000..ea34bf9fbe01d --- /dev/null +++ b/tests/ui-toml/large_include_file/clippy.toml @@ -0,0 +1 @@ +max-include-file-size = 600 diff --git a/tests/ui-toml/large_include_file/large_include_file.rs b/tests/ui-toml/large_include_file/large_include_file.rs new file mode 100644 index 0000000000000..f3dbb6ad1cf59 --- /dev/null +++ b/tests/ui-toml/large_include_file/large_include_file.rs @@ -0,0 +1,16 @@ +#![warn(clippy::large_include_file)] + +// Good +const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs"); +const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs"); + +#[allow(clippy::large_include_file)] +const ALLOWED_TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); +#[allow(clippy::large_include_file)] +const ALLOWED_TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + +// Bad +const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); +const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + +fn main() {} diff --git a/tests/ui-toml/large_include_file/large_include_file.stderr b/tests/ui-toml/large_include_file/large_include_file.stderr new file mode 100644 index 0000000000000..6a685a58318b9 --- /dev/null +++ b/tests/ui-toml/large_include_file/large_include_file.stderr @@ -0,0 +1,21 @@ +error: attempted to include a large file + --> $DIR/large_include_file.rs:13:43 + | +LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::large-include-file` implied by `-D warnings` + = note: the configuration allows a maximum size of 600 bytes + = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: attempted to include a large file + --> $DIR/large_include_file.rs:14:35 + | +LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the configuration allows a maximum size of 600 bytes + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/large_include_file/too_big.txt b/tests/ui-toml/large_include_file/too_big.txt new file mode 100644 index 0000000000000..9829c46bc00f6 --- /dev/null +++ b/tests/ui-toml/large_include_file/too_big.txt @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Maecenas accumsan lacus vel facilisis volutpat. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Tellus id interdum velit laoreet id donec ultrices. Est ultricies integer quis auctor elit sed vulputate. Erat velit scelerisque in dictum non consectetur a erat nam. Sed blandit libero volutpat sed. Tortor condimentum lacinia quis vel eros. Enim ut tellus elementum sagittis vitae et leo duis. Congue mauris rhoncus aenean vel elit scelerisque. Id consectetur purus ut faucibus pulvinar elementum integer. \ No newline at end of file diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index f60afb8d4e382..8701809b4daaa 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `third-party` at line 5 column 1 error: aborting due to previous error From cf99f504fde3cab40e4b961281e53bbe18858829 Mon Sep 17 00:00:00 2001 From: asquared31415 <34665709+asquared31415@users.noreply.github.com> Date: Sun, 24 Apr 2022 19:41:43 -0400 Subject: [PATCH 103/129] remove extra lifetime --- clippy_lints/src/casts/cast_slice_different_sizes.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 893af60db2c5e..16c9e49ff11d2 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -8,7 +8,7 @@ use rustc_semver::RustcVersion; use super::CAST_SLICE_DIFFERENT_SIZES; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Option) { // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) { return; @@ -102,21 +102,20 @@ fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option> { } } -struct CastChainInfo<'expr, 'tcx> { +struct CastChainInfo<'tcx> { /// The left most part of the cast chain, or in other words, the first cast in the chain /// Used for diagnostics - left_cast: &'expr Expr<'expr>, + left_cast: &'tcx Expr<'tcx>, /// The starting type of the cast chain start_ty: TypeAndMut<'tcx>, /// The final type of the cast chain end_ty: TypeAndMut<'tcx>, } -// FIXME(asquared31415): unbounded recursion linear with the number of casts in an expression /// Returns a `CastChainInfo` with the left-most cast in the chain and the original ptr T and final /// ptr U if the expression is composed of casts. /// Returns None if the expr is not a Cast -fn expr_cast_chain_tys<'tcx, 'expr>(cx: &LateContext<'tcx>, expr: &Expr<'expr>) -> Option> { +fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option> { if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind { let cast_to = cx.typeck_results().expr_ty(expr); let to_slice_ty = get_raw_slice_ty_mut(cast_to)?; From e6fa163fad15963be686d2bad121f44c8a407d90 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Mon, 25 Apr 2022 05:20:08 -0400 Subject: [PATCH 104/129] Fix false positives --- clippy_lints/src/lifetimes.rs | 1 + tests/ui/extra_unused_lifetimes.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index ec74b8606d9d9..b7bf5f05f7c2a 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -565,6 +565,7 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' .collect(); let mut checker = LifetimeChecker::::new(cx, hs); + walk_generics(&mut checker, &impl_.generics); if let Some(ref trait_ref) = impl_.of_trait { walk_trait_ref(&mut checker, trait_ref); } diff --git a/tests/ui/extra_unused_lifetimes.rs b/tests/ui/extra_unused_lifetimes.rs index 69dc2f7965e10..f76127a7105fd 100644 --- a/tests/ui/extra_unused_lifetimes.rs +++ b/tests/ui/extra_unused_lifetimes.rs @@ -88,4 +88,30 @@ mod issue6437 { } } +// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213 +mod first_case { + use serde::de::Visitor; + pub trait Expected { + fn fmt(&self, formatter: &mut std::fmt::Formatter); + } + + impl<'de, T> Expected for T + where + T: Visitor<'de>, + { + fn fmt(&self, formatter: &mut std::fmt::Formatter) {} + } +} + +// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213 +mod second_case { + pub trait Source { + fn hey(); + } + + impl<'a, T: Source + ?Sized + 'a> Source for Box { + fn hey() {} + } +} + fn main() {} From c22bb06bc02c81c132ef2dbb5513b6faac09688c Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Mon, 25 Apr 2022 09:23:59 -0400 Subject: [PATCH 105/129] Fix naming --- clippy_lints/src/lifetimes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index b7bf5f05f7c2a..54b80bf3b27d5 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -13,7 +13,7 @@ use rustc_hir::{ TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::nested_filter as mir_nested_filter; +use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Ident, Symbol}; @@ -563,7 +563,7 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' _ => None, }) .collect(); - let mut checker = LifetimeChecker::::new(cx, hs); + let mut checker = LifetimeChecker::::new(cx, hs); walk_generics(&mut checker, &impl_.generics); if let Some(ref trait_ref) = impl_.of_trait { From 1d1fecff0fbb727919067a1a6a5d28d30c693cd3 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 1 Apr 2022 18:23:47 +0100 Subject: [PATCH 106/129] `needless_late_init`: ignore `if let`, `let mut` and significant drops --- .../src/matches/redundant_pattern_match.rs | 66 ++--------------- clippy_lints/src/needless_late_init.rs | 40 +++++++++- clippy_utils/src/ty.rs | 71 +++++++++++++++++- tests/ui/needless_late_init.rs | 74 ++++++++++++++++--- tests/ui/needless_late_init.stderr | 72 ++++++++++-------- tests/ui/needless_late_init_fixable.fixed | 8 +- tests/ui/needless_late_init_fixable.rs | 10 +-- tests/ui/needless_late_init_fixable.stderr | 19 +---- 8 files changed, 221 insertions(+), 139 deletions(-) diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 777ec9b75bc24..a34ed58c9c359 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -2,17 +2,16 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type}; +use clippy_utils::ty::needs_ordered_drop; use clippy_utils::{higher, match_def_path}; use clippy_utils::{is_lang_ctor, is_trait_method, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, PollPending}; use rustc_hir::{ intravisit::{walk_expr, Visitor}, - Arm, Block, Expr, ExprKind, LangItem, Node, Pat, PatKind, QPath, UnOp, + Arm, Block, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp, }; use rustc_lint::LateContext; use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty}; @@ -32,59 +31,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { } } -/// 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<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) -} - -fn type_needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet>) -> bool { - if !seen.insert(ty) { - return false; - } - if !ty.needs_drop(cx.tcx, cx.param_env) { - false - } else if !cx - .tcx - .lang_items() - .drop_trait() - .map_or(false, |id| implements_trait(cx, ty, id, &[])) - { - // This type doesn't implement drop, so no side effects here. - // Check if any component type has any. - match ty.kind() { - ty::Tuple(fields) => fields.iter().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)), - ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, *ty, seen), - ty::Adt(adt, subs) => adt - .all_fields() - .map(|f| f.ty(cx.tcx, subs)) - .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)), - _ => true, - } - } - // Check for std types which implement drop, but only for memory allocation. - else if is_type_diagnostic_item(cx, ty, sym::Vec) - || is_type_lang_item(cx, ty, LangItem::OwnedBox) - || is_type_diagnostic_item(cx, ty, sym::Rc) - || is_type_diagnostic_item(cx, ty, sym::Arc) - || is_type_diagnostic_item(cx, ty, sym::cstring_type) - || is_type_diagnostic_item(cx, ty, sym::BTreeMap) - || is_type_diagnostic_item(cx, ty, sym::LinkedList) - || match_type(cx, ty, &paths::WEAK_RC) - || match_type(cx, ty, &paths::WEAK_ARC) - { - // Check all of the generic arguments. - if let ty::Adt(_, subs) = ty.kind() { - subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)) - } else { - true - } - } else { - true - } -} - // Extract the generic arguments out of a type fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { if_chain! { @@ -115,7 +61,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< // e.g. In `(String::new(), 0).1` the string is a temporary value. ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => { if !matches!(expr.kind, ExprKind::Path(_)) { - if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) { + if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) { self.res = true; } else { self.visit_expr(expr); @@ -126,7 +72,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< // e.g. In `(vec![0])[0]` the vector is a temporary value. ExprKind::Index(base, index) => { if !matches!(base.kind, ExprKind::Path(_)) { - if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) { + if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) { self.res = true; } else { self.visit_expr(base); @@ -143,7 +89,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< .typeck_results() .type_dependent_def_id(expr.hir_id) .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref()); - if self_by_ref && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) { + if self_by_ref && needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) { self.res = true; } else { self.visit_expr(self_arg); @@ -243,7 +189,7 @@ fn find_sugg_for_if_let<'tcx>( // scrutinee would be, so they have to be considered as well. // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held // for the duration if body. - let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr); + let needs_drop = needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr); // check that `while_let_on_iterator` lint does not trigger if_chain! { diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index bbcf7e9e3789c..a863a7990ca8c 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -1,10 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_opt; -use clippy_utils::visitors::{expr_visitor, is_local_used}; +use clippy_utils::ty::needs_ordered_drop; +use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used}; use rustc_errors::Applicability; use rustc_hir::intravisit::Visitor; -use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind}; +use rustc_hir::{ + BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, + StmtKind, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -73,6 +77,31 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> seen } +fn contains_let(cond: &Expr<'_>) -> bool { + let mut seen = false; + expr_visitor_no_bodies(|expr| { + if let ExprKind::Let(_) = expr.kind { + seen = true; + } + + !seen + }) + .visit_expr(cond); + + seen +} + +fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { + let StmtKind::Local(local) = stmt.kind else { return false }; + !local.pat.walk_short(|pat| { + if let PatKind::Binding(.., None) = pat.kind { + !needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat)) + } else { + true + } + }) +} + #[derive(Debug)] struct LocalAssign { lhs_id: HirId, @@ -187,11 +216,14 @@ fn first_usage<'tcx>( local_stmt_id: HirId, block: &'tcx Block<'tcx>, ) -> Option> { + let significant_drop = needs_ordered_drop(cx, cx.typeck_results().node_type(binding_id)); + block .stmts .iter() .skip_while(|stmt| stmt.hir_id != local_stmt_id) .skip(1) + .take_while(|stmt| !significant_drop || !stmt_needs_ordered_drop(cx, stmt)) .find(|&stmt| is_local_used(cx, stmt, binding_id)) .and_then(|stmt| match stmt.kind { StmtKind::Expr(expr) => Some(Usage { @@ -258,7 +290,7 @@ fn check<'tcx>( }, ); }, - ExprKind::If(_, then_expr, Some(else_expr)) => { + ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => { let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; span_lint_and_then( @@ -338,7 +370,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { if let Local { init: None, pat: &Pat { - kind: PatKind::Binding(_, binding_id, _, None), + kind: PatKind::Binding(BindingAnnotation::Unannotated, binding_id, _, None), .. }, source: LocalSource::Normal, diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index e3fc76f4e1a26..901e3e5390c5d 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -3,11 +3,11 @@ #![allow(clippy::module_name_repetitions)] use rustc_ast::ast::Mutability; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, TyKind, Unsafety}; +use rustc_hir::{Expr, LangItem, TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; @@ -22,7 +22,7 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::normalize::AtExt; use std::iter; -use crate::{match_def_path, must_use_attr, path_res}; +use crate::{match_def_path, must_use_attr, path_res, paths}; // Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { @@ -83,6 +83,20 @@ pub fn get_associated_type<'tcx>( }) } +/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type +/// implements a trait marked with a diagnostic item use [`implements_trait`]. +/// +/// For a further exploitation what diagnostic items are see [diagnostic items] in +/// rustc-dev-guide. +/// +/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html +pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option { + match ty.kind() { + ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()), + _ => None, + } +} + /// Returns true if ty has `iter` or `iter_mut` methods pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option { // FIXME: instead of this hard-coded list, we should check if `::iter` @@ -319,6 +333,57 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { } } +/// 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. +pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet>) -> bool { + if !seen.insert(ty) { + return false; + } + if !ty.has_significant_drop(cx.tcx, cx.param_env) { + false + } + // Check for std types which implement drop, but only for memory allocation. + else if is_type_lang_item(cx, ty, LangItem::OwnedBox) + || matches!( + get_type_diagnostic_name(cx, ty), + Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type) + ) + || match_type(cx, ty, &paths::WEAK_RC) + || match_type(cx, ty, &paths::WEAK_ARC) + { + // Check all of the generic arguments. + if let ty::Adt(_, subs) = ty.kind() { + subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen)) + } else { + true + } + } else if !cx + .tcx + .lang_items() + .drop_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + // This type doesn't implement drop, so no side effects here. + // Check if any component type has any. + match ty.kind() { + ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)), + ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen), + ty::Adt(adt, subs) => adt + .all_fields() + .map(|f| f.ty(cx.tcx, subs)) + .any(|ty| needs_ordered_drop_inner(cx, ty, seen)), + _ => true, + } + } else { + true + } + } + + needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) +} + /// Peels off all references on the type. Returns the underlying type and the number of references /// removed. pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 3cfbfb6033bd8..54e66b391b8d8 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,5 +1,15 @@ -#![allow(unused)] -#![allow(clippy::let_unit_value)] +#![feature(let_chains)] +#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value)] + +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::rc::Rc; + +struct SignificantDrop; +impl std::ops::Drop for SignificantDrop { + fn drop(&mut self) { + println!("dropped"); + } +} fn main() { let a; @@ -18,13 +28,6 @@ fn main() { b = "five" } - let c; - if let Some(n) = Some(5) { - c = n; - } else { - c = -50; - } - let d; if true { let temp = 5; @@ -37,7 +40,7 @@ fn main() { if true { e = format!("{} {}", a, b); } else { - e = format!("{}", c); + e = format!("{}", n); } let f; @@ -53,7 +56,27 @@ fn main() { panic!(); } - println!("{}", a); + // Drop order only matters if both are significant + let x; + let y = SignificantDrop; + x = 1; + + let x; + let y = 1; + x = SignificantDrop; + + let x; + // types that should be considered insignificant + let y = 1; + let y = "2"; + let y = String::new(); + let y = vec![3.0]; + let y = HashMap::::new(); + let y = BTreeMap::::new(); + let y = HashSet::::new(); + let y = BTreeSet::::new(); + let y = Box::new(4); + x = SignificantDrop; } async fn in_async() -> &'static str { @@ -177,5 +200,32 @@ fn does_not_lint() { } in_macro!(); - println!("{}", x); + // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613 + let x; + if let Some(n) = Some("v") { + x = 1; + } else { + x = 2; + } + + let x; + if true && let Some(n) = Some("let chains too") { + x = 1; + } else { + x = 2; + } + + // ignore mut bindings + // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93 + // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100 + let mut x: usize; + x = 1; + x = 2; + x = 3; + + // should not move the declaration if `x` has a significant drop, and there + // is another binding with a significant drop between it and the first usage + let x; + let y = SignificantDrop; + x = SignificantDrop; } diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index 124fe5592c31c..015c173561ac3 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ error: unneeded late initialization - --> $DIR/needless_late_init.rs:5:5 + --> $DIR/needless_late_init.rs:15:5 | LL | let a; | ^^^^^^ @@ -21,7 +21,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:14:5 + --> $DIR/needless_late_init.rs:24:5 | LL | let b; | ^^^^^^ @@ -42,28 +42,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:21:5 - | -LL | let c; - | ^^^^^^ - | -help: declare `c` here - | -LL | let c = if let Some(n) = Some(5) { - | +++++++ -help: remove the assignments from the branches - | -LL ~ n -LL | } else { -LL ~ -50 - | -help: add a semicolon after the `if` expression - | -LL | }; - | + - -error: unneeded late initialization - --> $DIR/needless_late_init.rs:28:5 + --> $DIR/needless_late_init.rs:31:5 | LL | let d; | ^^^^^^ @@ -84,7 +63,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:36:5 + --> $DIR/needless_late_init.rs:39:5 | LL | let e; | ^^^^^^ @@ -97,7 +76,7 @@ help: remove the assignments from the branches | LL ~ format!("{} {}", a, b) LL | } else { -LL ~ format!("{}", c) +LL ~ format!("{}", n) | help: add a semicolon after the `if` expression | @@ -105,7 +84,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:43:5 + --> $DIR/needless_late_init.rs:46:5 | LL | let f; | ^^^^^^ @@ -121,7 +100,7 @@ LL + 1 => "three", | error: unneeded late initialization - --> $DIR/needless_late_init.rs:49:5 + --> $DIR/needless_late_init.rs:52:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -140,9 +119,42 @@ help: add a semicolon after the `if` expression LL | }; | + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:60:5 + | +LL | let x; + | ^^^^^^ + | +help: declare `x` here + | +LL | let x = 1; + | ~~~~~ + error: unneeded late initialization --> $DIR/needless_late_init.rs:64:5 | +LL | let x; + | ^^^^^^ + | +help: declare `x` here + | +LL | let x = SignificantDrop; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:68:5 + | +LL | let x; + | ^^^^^^ + | +help: declare `x` here + | +LL | let x = SignificantDrop; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:87:5 + | LL | let a; | ^^^^^^ | @@ -162,7 +174,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:81:5 + --> $DIR/needless_late_init.rs:104:5 | LL | let a; | ^^^^^^ @@ -182,5 +194,5 @@ help: add a semicolon after the `match` expression LL | }; | + -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/needless_late_init_fixable.fixed b/tests/ui/needless_late_init_fixable.fixed index b516f9d86b75b..724477e8691df 100644 --- a/tests/ui/needless_late_init_fixable.fixed +++ b/tests/ui/needless_late_init_fixable.fixed @@ -15,11 +15,5 @@ fn main() { let d: usize = 1; - let mut e = 1; - e = 2; - - - let h = format!("{}", e); - - println!("{}", a); + let e = format!("{}", d); } diff --git a/tests/ui/needless_late_init_fixable.rs b/tests/ui/needless_late_init_fixable.rs index 75a4bc916deac..3e6bd36367275 100644 --- a/tests/ui/needless_late_init_fixable.rs +++ b/tests/ui/needless_late_init_fixable.rs @@ -14,12 +14,6 @@ fn main() { let d: usize; d = 1; - let mut e; - e = 1; - e = 2; - - let h; - h = format!("{}", e); - - println!("{}", a); + let e; + e = format!("{}", d); } diff --git a/tests/ui/needless_late_init_fixable.stderr b/tests/ui/needless_late_init_fixable.stderr index 34dccc2cec8ba..c97cd93fd2fe8 100644 --- a/tests/ui/needless_late_init_fixable.stderr +++ b/tests/ui/needless_late_init_fixable.stderr @@ -46,24 +46,13 @@ LL | let d: usize = 1; error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:17:5 | -LL | let mut e; - | ^^^^^^^^^^ - | -help: declare `e` here - | -LL | let mut e = 1; - | ~~~~~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:21:5 - | -LL | let h; +LL | let e; | ^^^^^^ | -help: declare `h` here +help: declare `e` here | -LL | let h = format!("{}", e); +LL | let e = format!("{}", d); | ~~~~~ -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors From 33bf9e9a54697bcdaba30291f2d4e357ee84777f Mon Sep 17 00:00:00 2001 From: dswij Date: Wed, 23 Feb 2022 23:04:06 +0800 Subject: [PATCH 107/129] `redundant_closure` ignore coerced closure --- clippy_lints/src/eta_reduction.rs | 4 +++- tests/ui/eta.fixed | 16 ++++++++++++++-- tests/ui/eta.rs | 12 ++++++++++++ tests/ui/eta.stderr | 14 +------------- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index c2e32f1d9a23e..1b19868e4c70f 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -3,7 +3,7 @@ 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::{higher, path_to_local, path_to_local_id}; +use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -103,6 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { let closure_ty = cx.typeck_results().expr_ty(expr); if_chain!( + if !is_adjusted(cx, &body.value); if let ExprKind::Call(callee, args) = body.value.kind; if let ExprKind::Path(_) = callee.kind; if check_inputs(cx, body.params, args); @@ -144,6 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { ); if_chain!( + if !is_adjusted(cx, &body.value); if let ExprKind::MethodCall(path, args, _) = body.value.kind; if check_inputs(cx, body.params, args); let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap(); diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 873ed689a5be7..6c2272f4dff97 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -37,7 +37,7 @@ fn main() { } // See #815 - let e = Some(1u8).map(divergent); + let e = Some(1u8).map(|a| divergent(a)); let e = Some(1u8).map(generic); let e = Some(1u8).map(generic); // See #515 @@ -233,7 +233,7 @@ fn late_bound_lifetimes() { { } map_str(|s| take_asref_path(s)); - map_str_to_path(std::convert::AsRef::as_ref); + map_str_to_path(|s| s.as_ref()); } mod type_param_bound { @@ -279,3 +279,15 @@ mod bind_by_ref { Some(A).map(|ref a| B::from(a)); } } + +// #7812 False positive on coerced closure +fn coerced_closure() { + fn function_returning_unit(f: F) {} + function_returning_unit(|x| std::process::exit(x)); + + fn arr() -> &'static [u8; 0] { + &[] + } + fn slice_fn(_: impl FnOnce() -> &'static [u8]) {} + slice_fn(|| arr()); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 4cb58eec94cd7..a1a9c0dfbf381 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -279,3 +279,15 @@ mod bind_by_ref { Some(A).map(|ref a| B::from(a)); } } + +// #7812 False positive on coerced closure +fn coerced_closure() { + fn function_returning_unit(f: F) {} + function_returning_unit(|x| std::process::exit(x)); + + fn arr() -> &'static [u8; 0] { + &[] + } + fn slice_fn(_: impl FnOnce() -> &'static [u8]) {} + slice_fn(|| arr()); +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index d1ae889d6f360..bf2e97e744ab3 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -24,12 +24,6 @@ error: redundant closure LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` -error: redundant closure - --> $DIR/eta.rs:40:27 - | -LL | let e = Some(1u8).map(|a| divergent(a)); - | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent` - error: redundant closure --> $DIR/eta.rs:41:27 | @@ -122,11 +116,5 @@ error: redundant closure LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` -error: redundant closure - --> $DIR/eta.rs:236:21 - | -LL | map_str_to_path(|s| s.as_ref()); - | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref` - -error: aborting due to 21 previous errors +error: aborting due to 19 previous errors From 5ffe8a1a9049c58501132a07f1567605acff4541 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 26 Apr 2022 06:17:33 +0100 Subject: [PATCH 108/129] errors: `span_suggestion` takes `impl ToString` Change `span_suggestion` (and variants) to take `impl ToString` rather than `String` for the suggested code, as this simplifies the requirements on the diagnostic derive. Signed-off-by: David Wood --- clippy_lints/src/functions/must_use.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 0709580c8adfd..5462d913fb441 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -108,7 +108,7 @@ fn check_needless_must_use( diag.span_suggestion( attr.span, "remove the attribute", - "".into(), + "", Applicability::MachineApplicable, ); }, diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 9c734221ebcea..4034079a90c0d 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -241,7 +241,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { |x| Cow::from(format!("change `{}` to", x)), ) .as_ref(), - suggestion.into(), + suggestion, Applicability::Unspecified, ); } @@ -271,7 +271,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { |x| Cow::from(format!("change `{}` to", x)) ) .as_ref(), - suggestion.into(), + suggestion, Applicability::Unspecified, ); } From 8f8fc9f71752e8df80152a402b992bdd819be7f3 Mon Sep 17 00:00:00 2001 From: asquared31415 <34665709+asquared31415@users.noreply.github.com> Date: Thu, 28 Apr 2022 21:27:32 -0400 Subject: [PATCH 109/129] use non-panicking snippet, use struct update syntax and add comment --- .../src/casts/cast_slice_different_sizes.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 16c9e49ff11d2..2238668abca71 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -1,4 +1,4 @@ -use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source::snippet_opt}; +use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source}; use if_chain::if_chain; use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, Node}; @@ -39,7 +39,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Opti start_ty.ty, from_size, end_ty.ty, to_size, ), |diag| { - let ptr_snippet = snippet_opt(cx, left_cast.span).unwrap(); + let ptr_snippet = source::snippet(cx, left_cast.span, ".."); let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl { Mutability::Mut => ("_mut", "mut"), @@ -119,16 +119,14 @@ fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Optio if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind { let cast_to = cx.typeck_results().expr_ty(expr); let to_slice_ty = get_raw_slice_ty_mut(cast_to)?; - if let Some(CastChainInfo { - left_cast, - start_ty, - end_ty: _, - }) = expr_cast_chain_tys(cx, cast_expr) - { + + // If the expression that makes up the source of this cast is itself a cast, recursively + // call `expr_cast_chain_tys` and update the end type with the final tartet type. + // Otherwise, this cast is not immediately nested, just construct the info for this cast + if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) { Some(CastChainInfo { - left_cast, - start_ty, end_ty: to_slice_ty, + ..prev_info }) } else { let cast_from = cx.typeck_results().expr_ty(cast_expr); From 8ef4b8d248ba55c52eb6ddc1b8631de98b64564e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 29 Apr 2022 19:28:03 -0700 Subject: [PATCH 110/129] Update GitHub Actions actions/checkout@v2 to v3 The v2 implementation uses Node 12, which is end-of-life on April 30, 2022. See https://nodejs.org/en/about/releases/. Update to v3, which is based on Node 16 whose support lasts until April 30, 2024. --- .github/workflows/clippy.yml | 2 +- .github/workflows/clippy_bors.yml | 8 ++++---- .github/workflows/clippy_dev.yml | 2 +- .github/workflows/deploy.yml | 4 ++-- .github/workflows/remark.yml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 4292949a02dff..0e27cc927acea 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -37,7 +37,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index f571485e6d389..9b3fd3ddfeb38 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -25,7 +25,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 with: ref: ${{ github.ref }} @@ -88,7 +88,7 @@ jobs: if: matrix.host == 'i686-unknown-linux-gnu' - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain @@ -154,7 +154,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain @@ -212,7 +212,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 5dfab1d2ebc40..22051093c9cf9 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -23,7 +23,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 # Run - name: Build diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b8be730be32b0..71d71d10359e7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -21,10 +21,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 56c00544c93a7..a179bfa726172 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -16,7 +16,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Setup Node.js uses: actions/setup-node@v1.4.4 From faadd8fd14852957c3f7252420439b10668fe409 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 5 Feb 2022 15:26:49 +0100 Subject: [PATCH 111/129] Box HIR Generics and Impl. --- clippy_lints/src/new_without_default.rs | 2 +- clippy_lints/src/partialeq_ne_impl.rs | 2 +- clippy_lints/src/serde_api.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 9419056be1432..96c00c205ff2e 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { .. }) = item.kind { - for assoc_item in items { + for assoc_item in *items { if assoc_item.kind == (hir::AssocItemKind::Fn { has_self: false }) { let impl_item = cx.tcx.hir().impl_item(assoc_item.id); if in_external_macro(cx.sess(), impl_item.span) { diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index e827cdaae8728..1469cb434c00c 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -42,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { if let Some(eq_trait) = cx.tcx.lang_items().eq_trait(); if trait_ref.path.res.def_id() == eq_trait; then { - for impl_item in impl_items { + for impl_item in *impl_items { if impl_item.ident.name == sym::ne { span_lint_hir( cx, diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs index 398e2c200de3c..fc1c2af9257bf 100644 --- a/clippy_lints/src/serde_api.rs +++ b/clippy_lints/src/serde_api.rs @@ -36,7 +36,7 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi { if did == visit_did { let mut seen_str = None; let mut seen_string = None; - for item in items { + for item in *items { match item.ident.as_str() { "visit_str" => seen_str = Some(item.span), "visit_string" => seen_string = Some(item.span), From 67241bb03ca83e132efbce8deffe5ca3438dd529 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 5 Feb 2022 15:48:02 +0100 Subject: [PATCH 112/129] Inline WhereClause into Generics. --- clippy_lints/src/lifetimes.rs | 8 ++++---- clippy_lints/src/trait_bounds.rs | 9 ++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index b09c23f31e970..4ec7c2362f087 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -8,7 +8,7 @@ use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, - TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, + TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -130,7 +130,7 @@ fn check_fn_inner<'tcx>( span: Span, report_extra_lifetimes: bool, ) { - if span.from_expansion() || has_where_lifetimes(cx, &generics.where_clause) { + if span.from_expansion() || has_where_lifetimes(cx, generics) { return; } @@ -445,8 +445,8 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { /// Are any lifetimes mentioned in the `where` clause? If so, we don't try to /// reason about elision. -fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereClause<'_>) -> bool { - for predicate in where_clause.predicates { +fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) -> bool { + for predicate in generics.predicates { match *predicate { WherePredicate::RegionPredicate(..) => return true, WherePredicate::BoundPredicate(ref pred) => { diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 43e0132a7ec7b..c388d2854cc2a 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -90,10 +90,9 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) { - let Generics { where_clause, .. } = &item.generics; let mut self_bounds_map = FxHashMap::default(); - for predicate in where_clause.predicates { + for predicate in item.generics.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; if !bound_predicate.span.from_expansion(); @@ -166,7 +165,7 @@ impl TraitBounds { } let mut map: UnhashMap, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; - for bound in gen.where_clause.predicates { + for bound in gen.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref p) = bound; if p.bounds.len() as u64 <= self.max_trait_bounds; @@ -216,7 +215,7 @@ impl TraitBounds { } fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { - if gen.span.from_expansion() || gen.params.is_empty() || gen.where_clause.predicates.is_empty() { + if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() { return; } @@ -232,7 +231,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { } } - for predicate in gen.where_clause.predicates { + for predicate in gen.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; if !bound_predicate.span.from_expansion(); From e2d923ac3b138bb69511bb1f6212633ca14e61ee Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 7 Feb 2022 22:58:30 +0100 Subject: [PATCH 113/129] Store all generic bounds as where predicates. --- clippy_lints/src/lifetimes.rs | 53 ++++++++++++++------------ clippy_lints/src/trait_bounds.rs | 27 +++++-------- clippy_lints/src/types/borrowed_box.rs | 4 +- 3 files changed, 41 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 4ec7c2362f087..662a561f171e9 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -139,28 +139,35 @@ fn check_fn_inner<'tcx>( .iter() .filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); for typ in types { - for bound in typ.bounds { - let mut visitor = RefVisitor::new(cx); - walk_param_bound(&mut visitor, bound); - if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { - return; + for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) { + if pred.in_where_clause { + // has_where_lifetimes checked that this predicate contains no lifetime. + continue; } - if let GenericBound::Trait(ref trait_ref, _) = *bound { - let params = &trait_ref - .trait_ref - .path - .segments - .last() - .expect("a path must have at least one segment") - .args; - if let Some(params) = *params { - let lifetimes = params.args.iter().filter_map(|arg| match arg { - GenericArg::Lifetime(lt) => Some(lt), - _ => None, - }); - for bound in lifetimes { - if bound.name != LifetimeName::Static && !bound.is_elided() { - return; + + for bound in pred.bounds { + let mut visitor = RefVisitor::new(cx); + walk_param_bound(&mut visitor, bound); + if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { + return; + } + if let GenericBound::Trait(ref trait_ref, _) = *bound { + let params = &trait_ref + .trait_ref + .path + .segments + .last() + .expect("a path must have at least one segment") + .args; + if let Some(params) = *params { + let lifetimes = params.args.iter().filter_map(|arg| match arg { + GenericArg::Lifetime(lt) => Some(lt), + _ => None, + }); + for bound in lifetimes { + if bound.name != LifetimeName::Static && !bound.is_elided() { + return; + } } } } @@ -322,9 +329,7 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { let mut allowed_lts = FxHashSet::default(); for par in named_generics.iter() { if let GenericParamKind::Lifetime { .. } = par.kind { - if par.bounds.is_empty() { - allowed_lts.insert(RefLt::Named(par.name.ident().name)); - } + allowed_lts.insert(RefLt::Named(par.name.ident().name)); } } allowed_lts.insert(RefLt::Unnamed); diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index c388d2854cc2a..3d1b2ee925bce 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -8,8 +8,7 @@ use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{ - GenericBound, Generics, Item, ItemKind, Node, ParamName, Path, PathSegment, QPath, TraitItem, Ty, TyKind, - WherePredicate, + GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, QPath, TraitItem, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -219,30 +218,19 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { return; } - let mut map = FxHashMap::default(); - for param in gen.params { - if let ParamName::Plain(ref ident) = param.name { - let res = param - .bounds - .iter() - .filter_map(get_trait_info_from_bound) - .collect::>(); - map.insert(*ident, res); - } - } - + let mut map = FxHashMap::<_, Vec<_>>::default(); for predicate in gen.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(segment) = segments.first(); - if let Some(trait_resolutions_direct) = map.get(&segment.ident); then { - for (res_where, _, _) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) { - if let Some((_, _, span_direct)) = trait_resolutions_direct + for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) { + let trait_resolutions_direct = map.entry(segment.ident).or_default(); + if let Some((_, span_direct)) = trait_resolutions_direct .iter() - .find(|(res_direct, _, _)| *res_direct == res_where) { + .find(|(res_direct, _)| *res_direct == res_where) { span_lint_and_help( cx, TRAIT_DUPLICATION_IN_BOUNDS, @@ -252,6 +240,9 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { "consider removing this trait bound", ); } + else { + trait_resolutions_direct.push((res_where, span_where)) + } } } } diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 7c06906293b16..f35f44eda5679 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -104,8 +104,10 @@ fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did); if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; if synthetic; + if let Some(generics) = cx.tcx.hir().get_generics(id.owner); + if let Some(pred) = generics.bounds_for_param(did.expect_local()).next(); then { - Some(generic_param.bounds) + Some(pred.bounds) } else { None } From e46c782662f1e9fcaa7978810d19d1cb1d342f01 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 14 Mar 2022 15:56:37 +0100 Subject: [PATCH 114/129] Bless tests. --- tests/ui/extra_unused_lifetimes.stderr | 8 +------- tests/ui/needless_lifetimes.stderr | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/tests/ui/extra_unused_lifetimes.stderr b/tests/ui/extra_unused_lifetimes.stderr index ebdb8e749520f..9143fb2c208a0 100644 --- a/tests/ui/extra_unused_lifetimes.stderr +++ b/tests/ui/extra_unused_lifetimes.stderr @@ -6,12 +6,6 @@ LL | fn unused_lt<'a>(x: u8) {} | = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings` -error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:16:25 - | -LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) { - | ^^ - error: this lifetime isn't used in the function definition --> $DIR/extra_unused_lifetimes.rs:41:10 | @@ -24,5 +18,5 @@ error: this lifetime isn't used in the function definition LL | fn unused_lt<'a>(x: u8) {} | ^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index ffa152427a977..a488bc01fffa2 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -108,12 +108,6 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:307:5 - | -LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) --> $DIR/needless_lifetimes.rs:310:5 | @@ -192,5 +186,5 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 32 previous errors +error: aborting due to 31 previous errors From 959ed524cef3dde4bf006d3f363d9f8cf6f0005e Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 30 Apr 2022 19:16:19 +0200 Subject: [PATCH 115/129] Add missing quite in `large_include_file` example --- clippy_lints/src/large_include_file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 5ed275726c530..8bef13c682dbf 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -19,7 +19,7 @@ declare_clippy_lint! { /// ### Example /// ```rust,ignore /// let included_str = include_str!("very_large_file.txt"); - /// let included_bytes = include_bytes!("very_large_file.txt); + /// let included_bytes = include_bytes!("very_large_file.txt"); /// ``` /// /// Instead, you can load the file at runtime: From defc537a2e7dc469d6fc1429854a24ca2914469f Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 24 Apr 2022 19:08:23 -0700 Subject: [PATCH 116/129] Fix the clippy build --- clippy_utils/src/sugg.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 1fc9979f3dd7d..794d2e1026f8c 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -214,6 +214,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Path(..) | ast::ExprKind::Repeat(..) | ast::ExprKind::Ret(..) + | ast::ExprKind::Yeet(..) | ast::ExprKind::Struct(..) | ast::ExprKind::Try(..) | ast::ExprKind::TryBlock(..) From 6ad810f94eec6b49af026d44c70066284120612d Mon Sep 17 00:00:00 2001 From: tamaron Date: Fri, 22 Apr 2022 10:39:38 +0900 Subject: [PATCH 117/129] improve identity_op fix Update identity_op.rs ok update without_loop_counters test chore fix chore remove visitor and leftmost fix add document --- clippy_lints/src/identity_op.rs | 82 ++++++++++++++++++++--------- tests/ui/identity_op.rs | 30 +++++++++++ tests/ui/identity_op.stderr | 92 ++++++++++++++++++++++++++++++++- 3 files changed, 180 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 4d6bef89bea7f..40cc5cd4bcf9d 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,3 +1,4 @@ +use clippy_utils::get_parent_expr; use clippy_utils::source::snippet; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -22,6 +23,11 @@ declare_clippy_lint! { /// # let x = 1; /// x / 1 + 0 * 1 - 0 | 0; /// ``` + /// + /// ### Known problems + /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to + /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code. + /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724) #[clippy::version = "pre 1.29.0"] pub IDENTITY_OP, complexity, @@ -31,36 +37,66 @@ declare_clippy_lint! { declare_lint_pass!(IdentityOp => [IDENTITY_OP]); impl<'tcx> LateLintPass<'tcx> for IdentityOp { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if e.span.from_expansion() { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { return; } - if let ExprKind::Binary(cmp, left, right) = e.kind { - if is_allowed(cx, cmp, left, right) { - return; - } - match cmp.node { - BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - check(cx, left, 0, e.span, right.span); - check(cx, right, 0, e.span, left.span); - }, - BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span), - BinOpKind::Mul => { - check(cx, left, 1, e.span, right.span); - check(cx, right, 1, e.span, left.span); - }, - BinOpKind::Div => check(cx, right, 1, e.span, left.span), - BinOpKind::BitAnd => { - check(cx, left, -1, e.span, right.span); - check(cx, right, -1, e.span, left.span); - }, - BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span), - _ => (), + if let ExprKind::Binary(cmp, left, right) = &expr.kind { + if !is_allowed(cx, *cmp, left, right) { + match cmp.node { + BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { + if reducible_to_right(cx, expr, right) { + check(cx, left, 0, expr.span, right.span); + } + check(cx, right, 0, expr.span, left.span); + }, + BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { + check(cx, right, 0, expr.span, left.span); + }, + BinOpKind::Mul => { + if reducible_to_right(cx, expr, right) { + check(cx, left, 1, expr.span, right.span); + } + check(cx, right, 1, expr.span, left.span); + }, + BinOpKind::Div => check(cx, right, 1, expr.span, left.span), + BinOpKind::BitAnd => { + if reducible_to_right(cx, expr, right) { + check(cx, left, -1, expr.span, right.span); + } + check(cx, right, -1, expr.span, left.span); + }, + BinOpKind::Rem => { + // Don't call reducible_to_right because N % N is always reducible to 1 + check_remainder(cx, left, right, expr.span, left.span); + }, + _ => (), + } } } } } +/// Checks if `left op ..right` can be actually reduced to `right` +/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` +/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` +/// See #8724 +fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool { + if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind { + is_toplevel_binary(cx, binary) + } else { + true + } +} + +fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind { + false + } else { + true + } +} + fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { // This lint applies to integers !cx.typeck_results().expr_ty(left).peel_refs().is_integral() diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index 9d39d7696354c..fec54d00ccb4b 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -77,4 +77,34 @@ fn main() { (x + 1) % 3; // no error 4 % 3; // no error 4 % -3; // no error + + // See #8724 + let a = 0; + let b = true; + 0 + if b { 1 } else { 2 }; + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error + 0 + match a { 0 => 10, _ => 20 }; + 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error + 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error + 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error + + 0 + if b { 0 + 1 } else { 2 }; + 0 + match a { 0 => 0 + 10, _ => 20 }; + 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + + let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; + + 0 + { a } + 3; // no error + 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error + + fn f(_: i32) { + todo!(); + } + f(1 * a + { 8 * 5 }); + f(0 + if b { 1 } else { 2 } + 3); // no error + const _: i32 = { 2 * 4 } + 0 + 3; + const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index e3c156b54291e..d8cb65839cbba 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -108,5 +108,95 @@ error: the operation is ineffective. Consider reducing it to `1` LL | x + 1 % 3; | ^^^^^ -error: aborting due to 18 previous errors +error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }` + --> $DIR/identity_op.rs:84:5 + | +LL | 0 + if b { 1 } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }` + --> $DIR/identity_op.rs:86:5 + | +LL | 0 + match a { 0 => 10, _ => 20 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }` + --> $DIR/identity_op.rs:91:5 + | +LL | 0 + if b { 0 + 1 } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:91:16 + | +LL | 0 + if b { 0 + 1 } else { 2 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `match a { 0 => 0 + 10, _ => 20 }` + --> $DIR/identity_op.rs:92:5 + | +LL | 0 + match a { 0 => 0 + 10, _ => 20 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `10` + --> $DIR/identity_op.rs:92:25 + | +LL | 0 + match a { 0 => 0 + 10, _ => 20 }; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:93:16 + | +LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `30` + --> $DIR/identity_op.rs:93:52 + | +LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:95:20 + | +LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:95:52 + | +LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:96:23 + | +LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:96:58 + | +LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` + --> $DIR/identity_op.rs:98:5 + | +LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `a` + --> $DIR/identity_op.rs:106:7 + | +LL | f(1 * a + { 8 * 5 }); + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }` + --> $DIR/identity_op.rs:108:20 + | +LL | const _: i32 = { 2 * 4 } + 0 + 3; + | ^^^^^^^^^^^^^ + +error: aborting due to 33 previous errors From 8172166f34fe5077b3873a6254d379b2282e1530 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 25 Apr 2022 22:08:45 +0300 Subject: [PATCH 118/129] rustc: Panic by default in `DefIdTree::parent` Only crate root def-ids don't have a parent, and in majority of cases the argument of `DefIdTree::parent` cannot be a crate root. So we now panic by default in `parent` and introduce a new non-panicing function `opt_parent` for cases where the argument can be a crate root. Same applies to `local_parent`/`opt_local_parent`. --- clippy_lints/src/matches/redundant_pattern_match.rs | 2 +- clippy_lints/src/methods/bind_instead_of_map.rs | 8 ++++---- clippy_lints/src/methods/chars_cmp.rs | 2 +- clippy_lints/src/methods/option_map_or_none.rs | 2 +- clippy_lints/src/missing_doc.rs | 2 +- clippy_utils/src/lib.rs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 777ec9b75bc24..aa3552001f469 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -193,7 +193,7 @@ fn find_sugg_for_if_let<'tcx>( PatKind::TupleStruct(ref qpath, [sub_pat], _) => { if let PatKind::Wild = sub_pat.kind { let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id); - let Some(id) = res.opt_def_id().and_then(|ctor_id| cx.tcx.parent(ctor_id)) else { return }; + let Some(id) = res.opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) else { return }; let lang_items = cx.tcx.lang_items(); if Some(id) == lang_items.result_ok_variant() { ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty)) diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index eec232e6d0989..b88ec0963f2b5 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -42,7 +42,7 @@ pub(crate) trait BindInsteadOfMap { fn no_op_msg(cx: &LateContext<'_>) -> Option { let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?; - let item_id = cx.tcx.parent(variant_id)?; + let item_id = cx.tcx.parent(variant_id); Some(format!( "using `{}.{}({})`, which is a no-op", cx.tcx.item_name(item_id), @@ -53,7 +53,7 @@ pub(crate) trait BindInsteadOfMap { fn lint_msg(cx: &LateContext<'_>) -> Option { let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?; - let item_id = cx.tcx.parent(variant_id)?; + let item_id = cx.tcx.parent(variant_id); Some(format!( "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`", cx.tcx.item_name(item_id), @@ -145,7 +145,7 @@ pub(crate) trait BindInsteadOfMap { if_chain! { if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def(); if let Ok(vid) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM); - if Some(adt.did()) == cx.tcx.parent(vid); + if adt.did() == cx.tcx.parent(vid); then {} else { return false; } } @@ -182,7 +182,7 @@ pub(crate) trait BindInsteadOfMap { fn is_variant(cx: &LateContext<'_>, res: Res) -> bool { if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { if let Ok(variant_id) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM) { - return cx.tcx.parent(id) == Some(variant_id); + return cx.tcx.parent(id) == variant_id; } } false diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index 2cf2c5641bf10..f7b79f0839ba8 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -19,7 +19,7 @@ pub(super) fn check( if_chain! { if let Some(args) = method_chain_args(info.chain, chain_methods); if let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind; - if let Some(id) = path_def_id(cx, fun).and_then(|ctor_id| cx.tcx.parent(ctor_id)); + if let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id)); if Some(id) == cx.tcx.lang_items().option_some_variant(); then { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 2a5ab6e625c11..76bc9466ed818 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -75,7 +75,7 @@ pub(super) fn check<'tcx>( let arg_snippet = snippet(cx, span, ".."); let body = cx.tcx.hir().body(id); if let Some((func, [arg_char])) = reduce_unit_expression(&body.value); - if let Some(id) = path_def_id(cx, func).and_then(|ctor_id| cx.tcx.parent(ctor_id)); + if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id)); if Some(id) == cx.tcx.lang_items().option_some_variant(); then { let func_snippet = snippet(cx, arg_char.span, ".."); diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 5816a95dcebff..a20377f320b23 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { hir::ItemKind::Fn(..) => { // ignore main() if it.ident.name == sym::main { - let at_root = cx.tcx.local_parent(it.def_id) == Some(CRATE_DEF_ID); + let at_root = cx.tcx.local_parent(it.def_id) == CRATE_DEF_ID; if at_root { return; } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 74978720424d4..7d46952d9718b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -235,7 +235,7 @@ pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem if let QPath::Resolved(_, path) = qpath { if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res { if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) { - return cx.tcx.parent(ctor_id) == Some(item_id); + return cx.tcx.parent(ctor_id) == item_id; } } } From 34760560ed8b1462b5dd0604fee5bd8dc071a6eb Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 29 Apr 2022 18:48:58 +0200 Subject: [PATCH 119/129] Make rustc_parse_format compile on stable This allows it to be used by lightweight formatting systems and may allow it to be used by rust-analyzer. --- clippy_lints/src/write.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index f3d818cc3485d..54b93a20a057d 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -13,7 +13,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{sym, BytePos, Span, DUMMY_SP}; +use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP}; declare_clippy_lint! { /// ### What it does @@ -454,6 +454,7 @@ impl SimpleFormatArgs { } }, ArgumentNamed(n, _) => { + let n = Symbol::intern(n); if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) { match x.1.as_slice() { // A non-empty format string has been seen already. @@ -495,7 +496,7 @@ impl Write { let span = parser .arg_places .last() - .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(x)); + .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(InnerSpan::new(x.start, x.end))); if !self.in_debug_impl && arg.format.ty == "?" { // FIXME: modify rustc's fmt string parser to give us the current span From f505cc9f7265eee332984f30516852e24d6e8385 Mon Sep 17 00:00:00 2001 From: binggh Date: Tue, 3 May 2022 17:51:23 +0800 Subject: [PATCH 120/129] Easier readability for needless_late_init --- clippy_lints/src/needless_late_init.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index a863a7990ca8c..b70871b38beab 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -3,7 +3,7 @@ use clippy_utils::path_to_local; use clippy_utils::source::snippet_opt; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used}; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::Visitor; use rustc_hir::{ BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, @@ -267,11 +267,14 @@ fn check<'tcx>( match usage.expr.kind { ExprKind::Assign(..) => { let assign = LocalAssign::new(cx, usage.expr, binding_id)?; + let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); + msg_span.push_span_label(local_stmt.span, "created here"); + msg_span.push_span_label(assign.span, "initialised here"); span_lint_and_then( cx, NEEDLESS_LATE_INIT, - local_stmt.span, + msg_span, "unneeded late initialization", |diag| { diag.tool_only_span_suggestion( @@ -365,7 +368,6 @@ fn check<'tcx>( 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); - if_chain! { if let Local { init: None, From 7017eb102d71526205b2feded779ff1f26d11da1 Mon Sep 17 00:00:00 2001 From: binggh Date: Tue, 3 May 2022 17:51:34 +0800 Subject: [PATCH 121/129] cargo dev bless --- tests/ui/needless_late_init.stderr | 15 ++++++++++++--- tests/ui/needless_late_init_fixable.stderr | 22 +++++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index 015c173561ac3..d33a117b288cb 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -123,7 +123,10 @@ error: unneeded late initialization --> $DIR/needless_late_init.rs:60:5 | LL | let x; - | ^^^^^^ + | ^^^^^^ created here +LL | let y = SignificantDrop; +LL | x = 1; + | ^^^^^ initialised here | help: declare `x` here | @@ -134,7 +137,10 @@ error: unneeded late initialization --> $DIR/needless_late_init.rs:64:5 | LL | let x; - | ^^^^^^ + | ^^^^^^ created here +LL | let y = 1; +LL | x = SignificantDrop; + | ^^^^^^^^^^^^^^^^^^^ initialised here | help: declare `x` here | @@ -145,7 +151,10 @@ error: unneeded late initialization --> $DIR/needless_late_init.rs:68:5 | LL | let x; - | ^^^^^^ + | ^^^^^^ created here +... +LL | x = SignificantDrop; + | ^^^^^^^^^^^^^^^^^^^ initialised here | help: declare `x` here | diff --git a/tests/ui/needless_late_init_fixable.stderr b/tests/ui/needless_late_init_fixable.stderr index c97cd93fd2fe8..8c664309e3e83 100644 --- a/tests/ui/needless_late_init_fixable.stderr +++ b/tests/ui/needless_late_init_fixable.stderr @@ -2,7 +2,9 @@ error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:6:5 | LL | let a; - | ^^^^^^ + | ^^^^^^ created here +LL | a = "zero"; + | ^^^^^^^^^^ initialised here | = note: `-D clippy::needless-late-init` implied by `-D warnings` help: declare `a` here @@ -14,7 +16,10 @@ error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:9:5 | LL | let b; - | ^^^^^^ + | ^^^^^^ created here +LL | let c; +LL | b = 1; + | ^^^^^ initialised here | help: declare `b` here | @@ -25,7 +30,10 @@ error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:10:5 | LL | let c; - | ^^^^^^ + | ^^^^^^ created here +LL | b = 1; +LL | c = 2; + | ^^^^^ initialised here | help: declare `c` here | @@ -36,7 +44,9 @@ error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:14:5 | LL | let d: usize; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ created here +LL | d = 1; + | ^^^^^ initialised here | help: declare `d` here | @@ -47,7 +57,9 @@ error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:17:5 | LL | let e; - | ^^^^^^ + | ^^^^^^ created here +LL | e = format!("{}", d); + | ^^^^^^^^^^^^^^^^^^^^ initialised here | help: declare `e` here | From d53293d52ac69d238b86a8ec8690e446e7ec789a Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 3 May 2022 13:39:38 +0100 Subject: [PATCH 122/129] Ignore type aliases in `init_numbered_fields` --- clippy_lints/src/init_numbered_fields.rs | 2 ++ tests/ui/numbered_fields.fixed | 5 +++++ tests/ui/numbered_fields.rs | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs index 9284e00240992..7e1548531f10c 100644 --- a/clippy_lints/src/init_numbered_fields.rs +++ b/clippy_lints/src/init_numbered_fields.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -49,6 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { && fields .iter() .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) + && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..)) { let expr_spans = fields .iter() diff --git a/tests/ui/numbered_fields.fixed b/tests/ui/numbered_fields.fixed index 1da97e9687988..3710b3e9c81e0 100644 --- a/tests/ui/numbered_fields.fixed +++ b/tests/ui/numbered_fields.fixed @@ -30,4 +30,9 @@ fn main() { // Ok because it's in macro let _ = tuple_struct_init!(); + + type Alias = TupleStruct; + + // Aliases can't be tuple constructed #8638 + let _ = Alias { 0: 0, 1: 1, 2: 2 }; } diff --git a/tests/ui/numbered_fields.rs b/tests/ui/numbered_fields.rs index 08ec405a5606e..2af84bc0642a5 100644 --- a/tests/ui/numbered_fields.rs +++ b/tests/ui/numbered_fields.rs @@ -38,4 +38,9 @@ fn main() { // Ok because it's in macro let _ = tuple_struct_init!(); + + type Alias = TupleStruct; + + // Aliases can't be tuple constructed #8638 + let _ = Alias { 0: 0, 1: 1, 2: 2 }; } From 1834f326c4a2b732c7c62bf7b00efca8af3b290a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 3 May 2022 16:41:12 +0100 Subject: [PATCH 123/129] Move only_used_in_recursion to nursery --- clippy_lints/src/lib.register_all.rs | 1 - clippy_lints/src/lib.register_complexity.rs | 1 - clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/only_used_in_recursion.rs | 9 ++++++++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 1cccfdb24c27d..325e74a177889 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -248,7 +248,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index df646ff5c5fe9..6f3c433af31a6 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -68,7 +68,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(no_effect::NO_EFFECT), LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), LintId::of(precedence::PRECEDENCE), diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index c2fc67afba517..18904a9453890 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -20,6 +20,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ 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), + LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index f946fc1119281..beb812793f81c 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lint_allowed; use itertools::{izip, Itertools}; use rustc_ast::{walk_list, Label, Mutability}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -33,6 +34,9 @@ declare_clippy_lint! { /// and the assigned variables are also only in recursion, it is useless. /// /// ### Known problems + /// Too many code paths in the linting code are currently untested and prone to produce false + /// positives or are prone to have performance implications. + /// /// In some cases, this would not catch all useless arguments. /// /// ```rust @@ -85,7 +89,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.60.0"] pub ONLY_USED_IN_RECURSION, - complexity, + nursery, "arguments that is only used in recursion can be removed" } declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]); @@ -100,6 +104,9 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { _: Span, id: HirId, ) { + if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) { + return; + } if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind { let def_id = id.owner.to_def_id(); let data = cx.tcx.def_path(def_id).data; From fea177fafe44c6ea950b71d812102167dfefd6b6 Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Tue, 22 Mar 2022 21:15:09 +0100 Subject: [PATCH 124/129] add trim_split_whitespace --- 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 | 1 + clippy_lints/src/strings.rs | 56 ++++++++++++++++ tests/ui/trim_split_whitespace.fixed | 91 ++++++++++++++++++++++++++ tests/ui/trim_split_whitespace.rs | 91 ++++++++++++++++++++++++++ tests/ui/trim_split_whitespace.stderr | 52 +++++++++++++++ 9 files changed, 295 insertions(+) create mode 100644 tests/ui/trim_split_whitespace.fixed create mode 100644 tests/ui/trim_split_whitespace.rs create mode 100644 tests/ui/trim_split_whitespace.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index c88ead99fa6f7..751f9fccd88d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3736,6 +3736,7 @@ Released 2018-09-13 [`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr [`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts [`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null +[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace [`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex [`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 1cccfdb24c27d..a49f8656ba3da 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -281,6 +281,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), + LintId::of(strings::TRIM_SPLIT_WHITESPACE), LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index e362431602794..5768edc501884 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -482,6 +482,7 @@ store.register_lints(&[ strings::STRING_SLICE, strings::STRING_TO_STRING, strings::STR_TO_STRING, + strings::TRIM_SPLIT_WHITESPACE, strlen_on_c_strings::STRLEN_ON_C_STRINGS, suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 5a85a9e209ebe..d183c0449cd5f 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -105,6 +105,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(returns::NEEDLESS_RETURN), LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(strings::TRIM_SPLIT_WHITESPACE), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), LintId::of(unit_types::LET_UNIT_VALUE), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 57d48e1762705..caf48194ad47d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -887,6 +887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen)); let max_include_file_size = conf.max_include_file_size; store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); + store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 3573f632a3671..7c196ccaa8ccd 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -5,6 +5,7 @@ use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method use clippy_utils::{peel_blocks, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -451,3 +452,58 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { } } } + +declare_clippy_lint! { + /// ### What it does + /// Warns about calling `str::trim` (or variants) before `str::split_whitespace`. + /// + /// ### Why is this bad? + /// `split_whitespace` already ignores leading and trailing whitespace. + /// + /// ### Example + /// ```rust + /// " A B C ".trim().split_whitespace(); + /// ``` + /// Use instead: + /// ```rust + /// " A B C ".split_whitespace(); + /// ``` + #[clippy::version = "1.62.0"] + pub TRIM_SPLIT_WHITESPACE, + style, + "using `str::trim()` or alike before `str::split_whitespace`" +} +declare_lint_pass!(TrimSplitWhitespace => [TRIM_SPLIT_WHITESPACE]); + +impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + let tyckres = cx.typeck_results(); + if_chain! { + if let ExprKind::MethodCall(path, [split_recv], split_ws_span) = expr.kind; + if path.ident.name == sym!(split_whitespace); + if let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id); + if cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id); + if let ExprKind::MethodCall(path, [_trim_recv], trim_span) = split_recv.kind; + if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str(); + if let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id); + if is_one_of_trim_diagnostic_items(cx, trim_def_id); + then { + span_lint_and_sugg( + cx, + TRIM_SPLIT_WHITESPACE, + trim_span.with_hi(split_ws_span.lo()), + &format!("found call to `str::{}` before `str::split_whitespace`", trim_fn_name), + &format!("remove `{}()`", trim_fn_name), + String::new(), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool { + cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id) + || cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id) + || cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id) +} diff --git a/tests/ui/trim_split_whitespace.fixed b/tests/ui/trim_split_whitespace.fixed new file mode 100644 index 0000000000000..e4d352f7367e4 --- /dev/null +++ b/tests/ui/trim_split_whitespace.fixed @@ -0,0 +1,91 @@ +// run-rustfix +#![warn(clippy::trim_split_whitespace)] +#![allow(clippy::let_unit_value)] + +struct Custom; +impl Custom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStr(&'static str); +impl std::ops::Deref for DerefStr { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +struct DerefStrAndCustom(&'static str); +impl std::ops::Deref for DerefStrAndCustom { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomSplit(&'static str); +impl std::ops::Deref for DerefStrAndCustomSplit { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomSplit { + #[allow(dead_code)] + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomTrim(&'static str); +impl std::ops::Deref for DerefStrAndCustomTrim { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomTrim { + fn trim(self) -> Self { + self + } +} + +fn main() { + // &str + let _ = " A B C ".split_whitespace(); // should trigger lint + let _ = " A B C ".split_whitespace(); // should trigger lint + let _ = " A B C ".split_whitespace(); // should trigger lint + + // String + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + + // Custom + let _ = Custom.trim().split_whitespace(); // should not trigger lint + + // Deref + let s = DerefStr(" A B C "); + let _ = s.split_whitespace(); // should trigger lint + + // Deref + custom impl + let s = DerefStrAndCustom(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint + + // Deref + only custom split_ws() impl + let s = DerefStrAndCustomSplit(" A B C "); + let _ = s.split_whitespace(); // should trigger lint + // Expl: trim() is called on str (deref) and returns &str. + // Thus split_ws() is called on str as well and the custom impl on S is unused + + // Deref + only custom trim() impl + let s = DerefStrAndCustomTrim(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint +} diff --git a/tests/ui/trim_split_whitespace.rs b/tests/ui/trim_split_whitespace.rs new file mode 100644 index 0000000000000..f98451a983712 --- /dev/null +++ b/tests/ui/trim_split_whitespace.rs @@ -0,0 +1,91 @@ +// run-rustfix +#![warn(clippy::trim_split_whitespace)] +#![allow(clippy::let_unit_value)] + +struct Custom; +impl Custom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStr(&'static str); +impl std::ops::Deref for DerefStr { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +struct DerefStrAndCustom(&'static str); +impl std::ops::Deref for DerefStrAndCustom { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomSplit(&'static str); +impl std::ops::Deref for DerefStrAndCustomSplit { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomSplit { + #[allow(dead_code)] + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomTrim(&'static str); +impl std::ops::Deref for DerefStrAndCustomTrim { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomTrim { + fn trim(self) -> Self { + self + } +} + +fn main() { + // &str + let _ = " A B C ".trim().split_whitespace(); // should trigger lint + let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint + let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint + + // String + let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint + + // Custom + let _ = Custom.trim().split_whitespace(); // should not trigger lint + + // Deref + let s = DerefStr(" A B C "); + let _ = s.trim().split_whitespace(); // should trigger lint + + // Deref + custom impl + let s = DerefStrAndCustom(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint + + // Deref + only custom split_ws() impl + let s = DerefStrAndCustomSplit(" A B C "); + let _ = s.trim().split_whitespace(); // should trigger lint + // Expl: trim() is called on str (deref) and returns &str. + // Thus split_ws() is called on str as well and the custom impl on S is unused + + // Deref + only custom trim() impl + let s = DerefStrAndCustomTrim(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint +} diff --git a/tests/ui/trim_split_whitespace.stderr b/tests/ui/trim_split_whitespace.stderr new file mode 100644 index 0000000000000..5ae7849e27d2b --- /dev/null +++ b/tests/ui/trim_split_whitespace.stderr @@ -0,0 +1,52 @@ +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:62:23 + | +LL | let _ = " A B C ".trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + | + = note: `-D clippy::trim-split-whitespace` implied by `-D warnings` + +error: found call to `str::trim_start` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:63:23 + | +LL | let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^^^ help: remove `trim_start()` + +error: found call to `str::trim_end` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:64:23 + | +LL | let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^ help: remove `trim_end()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:67:37 + | +LL | let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: found call to `str::trim_start` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:68:37 + | +LL | let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^^^ help: remove `trim_start()` + +error: found call to `str::trim_end` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:69:37 + | +LL | let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^ help: remove `trim_end()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:76:15 + | +LL | let _ = s.trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:84:15 + | +LL | let _ = s.trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: aborting due to 8 previous errors + From c318cf453dafbd8129203c3117d8f7be799f62ef Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 29 Apr 2022 06:52:01 +1000 Subject: [PATCH 125/129] Overhaul `MacArgs::Eq`. The value in `MacArgs::Eq` is currently represented as a `Token`. Because of `TokenKind::Interpolated`, `Token` can be either a token or an arbitrary AST fragment. In practice, a `MacArgs::Eq` starts out as a literal or macro call AST fragment, and then is later lowered to a literal token. But this is very non-obvious. `Token` is a much more general type than what is needed. This commit restricts things, by introducing a new type `MacArgsEqKind` that is either an AST expression (pre-lowering) or an AST literal (post-lowering). The downside is that the code is a bit more verbose in a few places. The benefit is that makes it much clearer what the possibilities are (though also shorter in some other places). Also, it removes one use of `TokenKind::Interpolated`, taking us a step closer to removing that variant, which will let us make `Token` impl `Copy` and remove many "handle Interpolated" code paths in the parser. Things to note: - Error messages have improved. Messages like this: ``` unexpected token: `"bug" + "found"` ``` now say "unexpected expression", which makes more sense. Although arbitrary expressions can exist within tokens thanks to `TokenKind::Interpolated`, that's not obvious to anyone who doesn't know compiler internals. - In `parse_mac_args_common`, we no longer need to collect tokens for the value expression. --- clippy_utils/src/ast_utils.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 3fce4987679ad..7919800483f52 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -688,7 +688,8 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool { match (l, r) { (Empty, Empty) => true, (Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts), - (Eq(_, lt), Eq(_, rt)) => lt.kind == rt.kind, + (Eq(_, MacArgsEq::Ast(le)), Eq(_, MacArgsEq::Ast(re))) => eq_expr(le, re), + (Eq(_, MacArgsEq::Hir(ll)), Eq(_, MacArgsEq::Hir(rl))) => ll.kind == rl.kind, _ => false, } } From 7e017dbe8d53bb8bce36871a53d8cdc47de5bc1a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 13:32:18 +0100 Subject: [PATCH 126/129] Bump nightly version -> 2022-05-05 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index bb29c71e9f455..03acb51306d7a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-04-07" +channel = "nightly-2022-05-05" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From bb01aca86f66954b80798213711e8eadc4b26902 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 13:32:31 +0100 Subject: [PATCH 127/129] HACK: Move buggy lints to nursery Those lints are trait_duplication_in_bounds and type_repetition_in_bounds. I don't think those can be fixed on the Clippy side alone, but need changes in the compiler. So let's move them to nursery to get the sync through and then fix them on the rustc side. Also adds a regression test that has to be fixed before they can be moved back to pedantic. --- clippy_lints/src/lib.register_nursery.rs | 2 ++ clippy_lints/src/lib.register_pedantic.rs | 2 -- clippy_lints/src/trait_bounds.rs | 4 ++-- tests/ui/trait_duplication_in_bounds.rs | 3 +++ tests/ui/trait_duplication_in_bounds.stderr | 10 +++++++++- tests/ui/type_repetition_in_bounds.rs | 3 +++ tests/ui/type_repetition_in_bounds.stderr | 10 +++++++++- 7 files changed, 28 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 18904a9453890..ec187563b3f64 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -28,6 +28,8 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), + LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), + LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 63232fd411305..2ee2c6e3358cd 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -84,8 +84,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_ADD_ASSIGN), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(types::LINKEDLIST), LintId::of(types::OPTION_OPTION), diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index c0aca2d517cfa..78e388a49af1d 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.38.0"] pub TYPE_REPETITION_IN_BOUNDS, - pedantic, + nursery, "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } @@ -65,7 +65,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.47.0"] pub TRAIT_DUPLICATION_IN_BOUNDS, - pedantic, + nursery, "Check if the same trait bounds are specified twice during a function declaration" } diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index f5ca91143af25..a21d4c5d637da 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -95,4 +95,7 @@ trait FooIter: Iterator { } } +// This should not lint +fn impl_trait(_: impl AsRef, _: impl AsRef) {} + fn main() {} diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr index 6f8c8e47dfbf1..d0a4cfb88370e 100644 --- a/tests/ui/trait_duplication_in_bounds.stderr +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -67,5 +67,13 @@ LL | Self: Iterator, | = help: consider removing this trait bound -error: aborting due to 8 previous errors +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:99:23 + | +LL | fn impl_trait(_: impl AsRef, _: impl AsRef) {} + | ^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 9 previous errors diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index fc740ee11d6ab..d11432f904611 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -79,4 +79,7 @@ where u: U, } +// This should not lint +fn impl_trait(_: impl AsRef, _: impl AsRef) {} + fn main() {} diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 148c19c7d0701..abc25e59496bf 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -19,5 +19,13 @@ LL | Self: Copy + Default + Ord, | = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` -error: aborting due to 2 previous errors +error: this type has already been used as a bound predicate + --> $DIR/type_repetition_in_bounds.rs:83:43 + | +LL | fn impl_trait(_: impl AsRef, _: impl AsRef) {} + | ^^^^^^^^^^ + | + = help: consider combining the bounds: `impl AsRef: AsRef + AsRef` + +error: aborting due to 3 previous errors From 006282964f1e741a58c72d8e12f872bf5c227af9 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 14:10:06 +0100 Subject: [PATCH 128/129] Fix ICE in EarlyAttribtues lints --- clippy_lints/src/attrs.rs | 6 +++++- tests/ui/crashes/ice-96721.rs | 10 ++++++++++ tests/ui/crashes/ice-96721.stderr | 8 ++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/ui/crashes/ice-96721.rs create mode 100644 tests/ui/crashes/ice-96721.stderr diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index e880876218ec6..8b0e11cb802ee 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -6,7 +6,7 @@ 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, meets_msrv}; use if_chain::if_chain; -use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; +use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MacArgs, MacArgsEq, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, @@ -593,6 +593,10 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It }; if attr.style == AttrStyle::Outer { + if let MacArgs::Eq(_, MacArgsEq::Ast(expr)) = &attr_item.args + && !matches!(expr.kind, rustc_ast::ExprKind::Lit(..)) { + return; + } if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { return; } diff --git a/tests/ui/crashes/ice-96721.rs b/tests/ui/crashes/ice-96721.rs new file mode 100644 index 0000000000000..4b3fb76401082 --- /dev/null +++ b/tests/ui/crashes/ice-96721.rs @@ -0,0 +1,10 @@ +macro_rules! foo { + () => { + "bar.rs" + }; +} + +#[path = foo!()] //~ ERROR malformed `path` attribute +mod abc {} + +fn main() {} diff --git a/tests/ui/crashes/ice-96721.stderr b/tests/ui/crashes/ice-96721.stderr new file mode 100644 index 0000000000000..78c567b8e772e --- /dev/null +++ b/tests/ui/crashes/ice-96721.stderr @@ -0,0 +1,8 @@ +error: malformed `path` attribute input + --> $DIR/ice-96721.rs:7:1 + | +LL | #[path = foo!()] //~ ERROR malformed `path` attribute + | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]` + +error: aborting due to previous error + From 463c760aa1f86e4d2ab5e0af0e6ca2ea3603623e Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 15:13:10 +0100 Subject: [PATCH 129/129] Update Cargo.lock --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 188db89bc6c78..f6a33a32111c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -655,6 +655,7 @@ dependencies = [ name = "clippy_dev" version = "0.0.1" dependencies = [ + "aho-corasick", "clap 2.34.0", "indoc", "itertools",