From 86811e9b1ba28abd8ba00ece1670c7cfd1284817 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Thu, 7 Oct 2021 00:14:06 +0200 Subject: [PATCH 001/101] make test module detection more strict --- clippy_utils/src/lib.rs | 42 ++++++++++++-------------------- tests/ui/wildcard_imports.fixed | 8 ++++++ tests/ui/wildcard_imports.rs | 8 ++++++ tests/ui/wildcard_imports.stderr | 8 +++++- 4 files changed, 39 insertions(+), 27 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 00cf8a4d077f6..673830eea8a45 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -252,11 +252,7 @@ pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem /// Returns `true` if this `span` was expanded by any macro. #[must_use] pub fn in_macro(span: Span) -> bool { - if span.from_expansion() { - !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) - } else { - false - } + span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) } pub fn is_unit_expr(expr: &Expr<'_>) -> bool { @@ -1286,10 +1282,9 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool } let enclosing_body = cx.tcx.hir().local_def_id(cx.tcx.hir().enclosing_body_owner(e.hir_id)); if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) { - value == v - } else { - false + return value == v; } + false } /// Checks whether the given expression is a constant literal of the given value. @@ -1316,7 +1311,7 @@ pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// Returns the pre-expansion span if is this comes from an expansion of the /// macro `name`. -/// See also `is_direct_expn_of`. +/// See also [`is_direct_expn_of`]. #[must_use] pub fn is_expn_of(mut span: Span, name: &str) -> Option { loop { @@ -1339,13 +1334,13 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option { /// Returns the pre-expansion span if the span directly comes from an expansion /// of the macro `name`. -/// The difference with `is_expn_of` is that in -/// ```rust,ignore +/// The difference with [`is_expn_of`] is that in +/// ```rust +/// # macro_rules! foo { ($e:tt) => { $e } }; macro_rules! bar { ($e:expr) => { $e } } /// foo!(bar!(42)); /// ``` /// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only -/// `bar!` by -/// `is_direct_expn_of`. +/// from `bar!` by `is_direct_expn_of`. #[must_use] pub fn is_direct_expn_of(span: Span, name: &str) -> Option { if span.from_expansion() { @@ -1468,11 +1463,9 @@ pub fn is_self(slf: &Param<'_>) -> bool { } pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool { - if_chain! { - if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind; - if let Res::SelfTy(..) = path.res; - then { - return true + if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind { + if let Res::SelfTy(..) = path.res { + return true; } } false @@ -2064,15 +2057,12 @@ macro_rules! unwrap_cargo_metadata { } pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { - if_chain! { - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind; - if let Res::Def(_, def_id) = path.res; - then { - cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) - } else { - false + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + if let Res::Def(_, def_id) = path.res { + return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr); } } + false } /// Checks whether item either has `test` attribute applied, or @@ -2084,7 +2074,7 @@ pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool { } } - matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") + matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests") } macro_rules! op_utils { diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index ee9c9045fff55..98bc1e80731ff 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -230,4 +230,12 @@ mod super_imports { let _ = foofoo(); } } + + mod attestation_should_be_replaced { + use super::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } } diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index efaa8f9ef6641..4ef61f9245b58 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -231,4 +231,12 @@ mod super_imports { let _ = foofoo(); } } + + mod attestation_should_be_replaced { + use super::*; + + fn with_explicit() { + let _ = foofoo(); + } + } } diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 66267dd27b84f..d7af0c046e886 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -122,5 +122,11 @@ error: usage of wildcard import LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` -error: aborting due to 20 previous errors +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:236:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors From 64f8b9d0eac0b3c25c74462bfbe5086feb4264e8 Mon Sep 17 00:00:00 2001 From: dswij Date: Thu, 7 Oct 2021 16:39:13 +0800 Subject: [PATCH 002/101] Make `shadow_reuse` suggestion less verbose --- clippy_lints/src/shadow.rs | 6 +--- tests/ui/shadow.rs | 5 ++++ tests/ui/shadow.stderr | 60 +++++++++++++++++++++++--------------- 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 2ca7c18800ee2..64841f33cc385 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -162,11 +162,7 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) (SHADOW_SAME, msg) }, Some(expr) if is_local_used(cx, expr, shadowed) => { - let msg = format!( - "`{}` is shadowed by `{}` which reuses the original value", - snippet(cx, pat.span, "_"), - snippet(cx, expr.span, "..") - ); + let msg = format!("`{}` is shadowed", snippet(cx, pat.span, "_")); (SHADOW_REUSE, msg) }, _ => { diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index 02e838456d0b5..55caef59f7f68 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -17,6 +17,11 @@ fn shadow_reuse() -> Option<()> { let x = foo(x); let x = || x; let x = Some(1).map(|_| x)?; + let y = 1; + let y = match y { + 1 => 2, + _ => 3, + }; None } diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index 8b60e072c9342..feed6e1ba8b86 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -47,7 +47,7 @@ note: previous binding is here LL | let x = &mut x; | ^ -error: `x` is shadowed by `x.0` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:13:9 | LL | let x = x.0; @@ -60,7 +60,7 @@ note: previous binding is here LL | let x = ([[0]], ()); | ^ -error: `x` is shadowed by `x[0]` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:14:9 | LL | let x = x[0]; @@ -72,7 +72,7 @@ note: previous binding is here LL | let x = x.0; | ^ -error: `x` is shadowed by `x` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:15:10 | LL | let [x] = x; @@ -84,7 +84,7 @@ note: previous binding is here LL | let x = x[0]; | ^ -error: `x` is shadowed by `Some(x)` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:16:9 | LL | let x = Some(x); @@ -96,7 +96,7 @@ note: previous binding is here LL | let [x] = x; | ^ -error: `x` is shadowed by `foo(x)` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:17:9 | LL | let x = foo(x); @@ -108,7 +108,7 @@ note: previous binding is here LL | let x = Some(x); | ^ -error: `x` is shadowed by `|| x` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:18:9 | LL | let x = || x; @@ -120,7 +120,7 @@ note: previous binding is here LL | let x = foo(x); | ^ -error: `x` is shadowed by `Some(1).map(|_| x)?` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:19:9 | LL | let x = Some(1).map(|_| x)?; @@ -132,102 +132,114 @@ note: previous binding is here LL | let x = || x; | ^ +error: `y` is shadowed + --> $DIR/shadow.rs:21:9 + | +LL | let y = match y { + | ^ + | +note: previous binding is here + --> $DIR/shadow.rs:20:9 + | +LL | let y = 1; + | ^ + error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:25:9 + --> $DIR/shadow.rs:30:9 | LL | let x = 2; | ^ | = note: `-D clippy::shadow-unrelated` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:24:9 + --> $DIR/shadow.rs:29:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:30:13 + --> $DIR/shadow.rs:35:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:29:10 + --> $DIR/shadow.rs:34:10 | LL | fn f(x: u32) { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:35:14 + --> $DIR/shadow.rs:40:14 | LL | Some(x) => { | ^ | note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:37:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:36:17 + --> $DIR/shadow.rs:41:17 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:35:14 + --> $DIR/shadow.rs:40:14 | LL | Some(x) => { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:40:17 + --> $DIR/shadow.rs:45:17 | LL | if let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:37:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:41:20 + --> $DIR/shadow.rs:46:20 | LL | while let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:37:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:42:15 + --> $DIR/shadow.rs:47:15 | LL | let _ = |[x]: [u32; 1]| { | ^ | note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:37:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:43:13 + --> $DIR/shadow.rs:48:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:42:15 + --> $DIR/shadow.rs:47:15 | LL | let _ = |[x]: [u32; 1]| { | ^ -error: aborting due to 19 previous errors +error: aborting due to 20 previous errors From 5cf4984872e1ac047b4e2df52fe0f6faa82716c3 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 7 Oct 2021 11:21:30 +0200 Subject: [PATCH 003/101] Merge commit 'b7f3f7f6082679da2da9a0b3faf1b5adef3afd3b' into clippyup --- CHANGELOG.md | 2 + clippy_dev/src/lib.rs | 523 +----- clippy_dev/src/update_lints.rs | 673 +++++++- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/await_holding_invalid.rs | 2 +- clippy_lints/src/casts/cast_precision_loss.rs | 2 +- clippy_lints/src/derivable_impls.rs | 1 + clippy_lints/src/derive.rs | 4 +- clippy_lints/src/doc.rs | 26 +- clippy_lints/src/equatable_if_let.rs | 100 ++ clippy_lints/src/erasing_op.rs | 2 +- clippy_lints/src/escape.rs | 2 +- clippy_lints/src/eta_reduction.rs | 2 +- clippy_lints/src/eval_order_dependence.rs | 2 +- clippy_lints/src/format.rs | 2 +- clippy_lints/src/identity_op.rs | 14 +- clippy_lints/src/if_then_panic.rs | 14 +- clippy_lints/src/implicit_hasher.rs | 20 +- clippy_lints/src/integer_division.rs | 2 +- clippy_lints/src/large_enum_variant.rs | 161 +- clippy_lints/src/len_zero.rs | 12 +- clippy_lints/src/lib.deprecated.rs | 70 + clippy_lints/src/lib.register_all.rs | 304 ++++ clippy_lints/src/lib.register_cargo.rs | 11 + clippy_lints/src/lib.register_complexity.rs | 94 ++ clippy_lints/src/lib.register_correctness.rs | 73 + clippy_lints/src/lib.register_internal.rs | 18 + clippy_lints/src/lib.register_lints.rs | 510 ++++++ clippy_lints/src/lib.register_nursery.rs | 30 + clippy_lints/src/lib.register_pedantic.rs | 100 ++ clippy_lints/src/lib.register_perf.rs | 27 + clippy_lints/src/lib.register_restriction.rs | 65 + clippy_lints/src/lib.register_style.rs | 114 ++ clippy_lints/src/lib.register_suspicious.rs | 20 + clippy_lints/src/lib.rs | 1411 +---------------- clippy_lints/src/loops/mut_range_bound.rs | 2 +- clippy_lints/src/loops/needless_range_loop.rs | 2 +- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/manual_async_fn.rs | 4 +- clippy_lints/src/map_unit_fn.rs | 15 +- clippy_lints/src/matches.rs | 3 +- clippy_lints/src/methods/mod.rs | 5 +- clippy_lints/src/methods/or_fun_call.rs | 14 +- clippy_lints/src/methods/suspicious_map.rs | 2 +- .../src/methods/unnecessary_filter_map.rs | 3 +- clippy_lints/src/modulo_arithmetic.rs | 2 +- clippy_lints/src/needless_bool.rs | 2 +- clippy_lints/src/neg_multiply.rs | 2 +- clippy_lints/src/new_without_default.rs | 2 +- .../src/non_send_fields_in_send_ty.rs | 238 +++ .../src/overflow_check_conditional.rs | 31 +- clippy_lints/src/redundant_pub_crate.rs | 36 +- clippy_lints/src/repeat_once.rs | 2 +- clippy_lints/src/shadow.rs | 427 ++--- clippy_lints/src/transmuting_null.rs | 4 +- clippy_lints/src/types/option_option.rs | 4 +- clippy_lints/src/utils/conf.rs | 4 + clippy_lints/src/utils/internal_lints.rs | 12 +- clippy_lints/src/zero_div_zero.rs | 2 +- clippy_utils/src/lib.rs | 15 +- clippy_utils/src/numeric_literal.rs | 15 +- clippy_utils/src/paths.rs | 6 + clippy_utils/src/sugg.rs | 2 +- clippy_utils/src/ty.rs | 10 +- clippy_utils/src/usage.rs | 2 +- rust-toolchain | 2 +- tests/compile-test.rs | 4 +- .../clippy.toml | 1 + .../strict_non_send_fields_in_send_ty/test.rs | 43 + .../test.stderr | 91 ++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/approx_const.rs | 2 +- .../branches_sharing_code/shared_at_bottom.rs | 2 +- tests/ui/collapsible_else_if.fixed | 2 +- tests/ui/collapsible_else_if.rs | 2 +- tests/ui/collapsible_if.fixed | 2 +- tests/ui/collapsible_if.rs | 2 +- tests/ui/collapsible_match.rs | 7 +- tests/ui/collapsible_match.stderr | 40 +- tests/ui/crashes/ice-3462.rs | 2 +- tests/ui/def_id_nocore.rs | 1 + tests/ui/def_id_nocore.stderr | 2 +- tests/ui/derivable_impls.rs | 33 + tests/ui/derivable_impls.stderr | 14 +- tests/ui/doc/doc.rs | 5 + tests/ui/doc_unsafe.rs | 21 +- tests/ui/doc_unsafe.stderr | 14 +- tests/ui/equatable_if_let.fixed | 69 + tests/ui/equatable_if_let.rs | 69 + tests/ui/equatable_if_let.stderr | 64 + tests/ui/excessive_precision.fixed | 6 + tests/ui/excessive_precision.rs | 6 + tests/ui/excessive_precision.stderr | 14 +- tests/ui/for_loop_fixable.fixed | 7 +- tests/ui/for_loop_fixable.rs | 7 +- tests/ui/for_loop_fixable.stderr | 30 +- tests/ui/for_loop_unfixable.rs | 9 +- tests/ui/for_loop_unfixable.stderr | 2 +- tests/ui/if_same_then_else2.rs | 1 + tests/ui/if_same_then_else2.stderr | 24 +- tests/ui/if_then_panic.fixed | 6 + tests/ui/if_then_panic.rs | 16 + tests/ui/if_then_panic.stderr | 42 +- tests/ui/implicit_hasher.rs | 4 + tests/ui/implicit_hasher.stderr | 35 +- tests/ui/integer_arithmetic.rs | 9 +- tests/ui/integer_arithmetic.stderr | 54 +- tests/ui/large_enum_variant.rs | 18 + tests/ui/large_enum_variant.stderr | 63 +- tests/ui/map_clone.fixed | 14 +- tests/ui/map_clone.rs | 14 +- tests/ui/match_expr_like_matches_macro.fixed | 2 +- tests/ui/match_expr_like_matches_macro.rs | 2 +- tests/ui/match_overlapping_arm.rs | 2 +- tests/ui/match_ref_pats.rs | 1 + tests/ui/match_ref_pats.stderr | 16 +- tests/ui/modulo_arithmetic_float.rs | 9 +- tests/ui/modulo_arithmetic_float.stderr | 20 +- tests/ui/modulo_arithmetic_integral.rs | 9 +- tests/ui/modulo_arithmetic_integral.stderr | 34 +- tests/ui/modulo_arithmetic_integral_const.rs | 9 +- .../modulo_arithmetic_integral_const.stderr | 34 +- tests/ui/needless_bool/fixable.fixed | 1 + tests/ui/needless_bool/fixable.rs | 1 + tests/ui/needless_bool/fixable.stderr | 24 +- tests/ui/needless_return.fixed | 7 +- tests/ui/needless_return.rs | 7 +- tests/ui/needless_return.stderr | 64 +- tests/ui/non_send_fields_in_send_ty.rs | 127 ++ tests/ui/non_send_fields_in_send_ty.stderr | 171 ++ tests/ui/option_if_let_else.fixed | 2 +- tests/ui/option_if_let_else.rs | 2 +- ...edundant_pattern_matching_drop_order.fixed | 2 +- .../redundant_pattern_matching_drop_order.rs | 2 +- .../redundant_pattern_matching_option.fixed | 1 + tests/ui/redundant_pattern_matching_option.rs | 1 + .../redundant_pattern_matching_option.stderr | 38 +- .../ui/redundant_pattern_matching_poll.fixed | 1 + tests/ui/redundant_pattern_matching_poll.rs | 1 + .../ui/redundant_pattern_matching_poll.stderr | 36 +- tests/ui/shadow.rs | 113 +- tests/ui/shadow.stderr | 235 ++- tests/ui/suspicious_map.stderr | 4 +- tests/ui/while_let_on_iterator.fixed | 8 +- tests/ui/while_let_on_iterator.rs | 8 +- tests/ui/while_let_on_iterator.stderr | 42 +- util/etc/pre-commit.sh | 1 + 147 files changed, 4259 insertions(+), 2940 deletions(-) create mode 100644 clippy_lints/src/equatable_if_let.rs create mode 100644 clippy_lints/src/lib.deprecated.rs create mode 100644 clippy_lints/src/lib.register_all.rs create mode 100644 clippy_lints/src/lib.register_cargo.rs create mode 100644 clippy_lints/src/lib.register_complexity.rs create mode 100644 clippy_lints/src/lib.register_correctness.rs create mode 100644 clippy_lints/src/lib.register_internal.rs create mode 100644 clippy_lints/src/lib.register_lints.rs create mode 100644 clippy_lints/src/lib.register_nursery.rs create mode 100644 clippy_lints/src/lib.register_pedantic.rs create mode 100644 clippy_lints/src/lib.register_perf.rs create mode 100644 clippy_lints/src/lib.register_restriction.rs create mode 100644 clippy_lints/src/lib.register_style.rs create mode 100644 clippy_lints/src/lib.register_suspicious.rs create mode 100644 clippy_lints/src/non_send_fields_in_send_ty.rs create mode 100644 tests/ui-toml/strict_non_send_fields_in_send_ty/clippy.toml create mode 100644 tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs create mode 100644 tests/ui-toml/strict_non_send_fields_in_send_ty/test.stderr create mode 100644 tests/ui/equatable_if_let.fixed create mode 100644 tests/ui/equatable_if_let.rs create mode 100644 tests/ui/equatable_if_let.stderr create mode 100644 tests/ui/non_send_fields_in_send_ty.rs create mode 100644 tests/ui/non_send_fields_in_send_ty.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 58ea0f9ab9d28..7fdb300c97741 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2695,6 +2695,7 @@ Released 2018-09-13 [`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 [`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 @@ -2898,6 +2899,7 @@ Released 2018-09-13 [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions +[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options [`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index e05db7af58677..5538f62c8e786 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -3,14 +3,7 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use itertools::Itertools; -use regex::Regex; -use std::collections::HashMap; -use std::ffi::OsStr; -use std::fs; -use std::lazy::SyncLazy; -use std::path::{Path, PathBuf}; -use walkdir::WalkDir; +use std::path::PathBuf; pub mod bless; pub mod fmt; @@ -19,323 +12,6 @@ pub mod serve; pub mod setup; pub mod update_lints; -static DEC_CLIPPY_LINT_RE: SyncLazy = SyncLazy::new(|| { - Regex::new( - r#"(?x) - declare_clippy_lint!\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+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()); - -pub static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; - -/// Lint data parsed from the Clippy source code. -#[derive(Clone, PartialEq, Debug)] -pub struct Lint { - pub name: String, - pub group: String, - pub desc: String, - pub deprecation: Option, - pub module: String, -} - -impl Lint { - #[must_use] - pub fn new(name: &str, group: &str, desc: &str, deprecation: Option<&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(), - } - } - - /// Returns all non-deprecated lints and non-internal lints - #[must_use] - pub fn usable_lints(lints: &[Self]) -> Vec { - lints - .iter() - .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal")) - .cloned() - .collect() - } - - /// Returns all internal lints (not `internal_warn` lints) - #[must_use] - pub fn internal_lints(lints: &[Self]) -> Vec { - lints.iter().filter(|l| l.group == "internal").cloned().collect() - } - - /// Returns all deprecated lints - #[must_use] - pub 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] - pub fn by_lint_group(lints: impl Iterator) -> HashMap> { - lints.map(|lint| (lint.group.to_string(), lint)).into_group_map() - } -} - -/// Generates the Vec items for `register_lint_group` calls in `clippy_lints/src/lib.rs`. -#[must_use] -pub fn gen_lint_group_list<'a>(lints: impl Iterator) -> Vec { - lints - .map(|l| format!(" LintId::of({}::{}),", l.module, l.name.to_uppercase())) - .sorted() - .collect::>() -} - -/// Generates the `pub mod module_name` list in `clippy_lints/src/lib.rs`. -#[must_use] -pub fn gen_modules_list<'a>(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 README -#[must_use] -pub 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 in `./clippy_lints/src/lib.rs`. -#[must_use] -pub fn gen_deprecated<'a>(lints: impl Iterator) -> Vec { - lints - .flat_map(|l| { - l.deprecation - .clone() - .map(|depr_text| { - vec![ - " store.register_removed(".to_string(), - format!(" \"clippy::{}\",", l.name), - format!(" \"{}\",", depr_text), - " );".to_string(), - ] - }) - .expect("only deprecated lints should be passed") - }) - .collect::>() -} - -#[must_use] -pub fn gen_register_lint_list<'a>( - internal_lints: impl Iterator, - usable_lints: impl Iterator, -) -> Vec { - let header = " store.register_lints(&[".to_string(); - let footer = " ]);".to_string(); - let internal_lints = internal_lints - .sorted_by_key(|l| format!(" {}::{},", l.module, l.name.to_uppercase())) - .map(|l| { - format!( - " #[cfg(feature = \"internal-lints\")]\n {}::{},", - l.module, - l.name.to_uppercase() - ) - }); - let other_lints = usable_lints - .sorted_by_key(|l| format!(" {}::{},", l.module, l.name.to_uppercase())) - .map(|l| format!(" {}::{},", l.module, l.name.to_uppercase())) - .sorted(); - let mut lint_list = vec![header]; - lint_list.extend(internal_lints); - lint_list.extend(other_lints); - lint_list.push(footer); - lint_list -} - -/// Gathers all files in `src/clippy_lints` and gathers all lints inside -pub fn gather_all() -> impl Iterator { - lint_files().flat_map(|f| gather_from_file(&f)) -} - -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(); - } - - let module = rel_path - .components() - .map(|c| c.as_os_str().to_str().unwrap()) - .collect::>() - .join("::"); - - parse_contents(&content, &module) -} - -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() -} - -/// 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"))) -} - -/// Whether a file has had its text changed or not -#[derive(PartialEq, Debug)] -pub struct FileChange { - pub changed: bool, - pub new_lines: String, -} - -/// Replaces a region in a file delimited by two lines matching regexes. -/// -/// `path` is the relative path to the file on which you want to perform the replacement. -/// -/// See `replace_region_in_text` for documentation of the other options. -/// -/// # Panics -/// -/// Panics if the path could not read or then written -pub fn replace_region_in_file( - 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 -/// -/// ``` -/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end"; -/// let result = -/// clippy_dev::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 -pub 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()); - } - 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 } -} - /// Returns the path to the Clippy project directory /// /// # Panics @@ -360,200 +36,3 @@ pub fn clippy_project_root() -> PathBuf { } panic!("error: Can't determine root of project. Please run inside a Clippy working dir."); } - -#[test] -fn test_parse_contents() { - let result: Vec = parse_contents( - r#" -declare_clippy_lint! { - pub PTR_ARG, - style, - "really long \ - text" -} - -declare_clippy_lint!{ - pub DOC_MARKDOWN, - pedantic, - "single line" -} - -/// some doc comment -declare_deprecated_lint! { - 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); -} - -#[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); -} - -#[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()] - }); - 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); - 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"), - ]; - let expected = vec![Lint::new( - "should_assert_eq2", - "Not Deprecated", - "abc", - None, - "module_name", - )]; - assert_eq!(expected, Lint::usable_lints(&lints)); -} - -#[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"), - ]; - 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"), - ], - ); - expected.insert( - "group2".to_string(), - vec![Lint::new("should_assert_eq2", "group2", "abc", None, "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.to_string()), - format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string()), - ]; - 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", - ), - ]; - let expected: Vec = vec![ - " store.register_removed(", - " \"clippy::should_assert_eq\",", - " \"has been superseded by should_assert_eq2\",", - " );", - " store.register_removed(", - " \"clippy::another_deprecated\",", - " \"will be removed\",", - " );", - ] - .into_iter() - .map(String::from) - .collect(); - 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())); -} - -#[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"), - ]; - let expected = vec![ - " LintId::of(module_name::ABC),".to_string(), - " LintId::of(module_name::INTERNAL),".to_string(), - " LintId::of(module_name::SHOULD_ASSERT_EQ),".to_string(), - ]; - assert_eq!(expected, gen_lint_group_list(lints.iter())); -} diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index db467c26f1546..10d859988f6f2 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,8 +1,45 @@ -use crate::{ - gather_all, gen_changelog_lint_list, gen_deprecated, gen_lint_group_list, gen_modules_list, gen_register_lint_list, - replace_region_in_file, Lint, DOCS_LINK, -}; +use itertools::Itertools; +use regex::Regex; +use std::collections::HashMap; +use std::ffi::OsStr; +use std::fs; +use std::lazy::SyncLazy; use std::path::Path; +use walkdir::WalkDir; + +use crate::clippy_project_root; + +const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\ + // 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+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+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"; #[derive(Clone, Copy, PartialEq)] pub enum UpdateMode { @@ -10,6 +47,15 @@ pub enum UpdateMode { Change, } +/// Runs the `update_lints` command. +/// +/// This updates various generated values from the lint source code. +/// +/// `update_mode` indicates if the files should be updated or if updates should be checked for. +/// +/// # 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) { let lint_list: Vec = gather_all().collect(); @@ -52,26 +98,7 @@ pub fn run(update_mode: UpdateMode) { ) .changed; - file_change |= replace_region_in_file( - Path::new("clippy_lints/src/lib.rs"), - "begin deprecated lints", - "end deprecated lints", - false, - update_mode == UpdateMode::Change, - || gen_deprecated(deprecated_lints.iter()), - ) - .changed; - - file_change |= replace_region_in_file( - Path::new("clippy_lints/src/lib.rs"), - "begin register lints", - "end register lints", - false, - update_mode == UpdateMode::Change, - || gen_register_lint_list(internal_lints.iter(), usable_lints.iter()), - ) - .changed; - + // This has to be in lib.rs, otherwise rustfmt doesn't work file_change |= replace_region_in_file( Path::new("clippy_lints/src/lib.rs"), "begin lints modules", @@ -82,46 +109,37 @@ pub fn run(update_mode: UpdateMode) { ) .changed; - // Generate lists of lints in the clippy::all lint group - file_change |= replace_region_in_file( - Path::new("clippy_lints/src/lib.rs"), - r#"store.register_group\(true, "clippy::all""#, - r#"\]\);"#, - false, - update_mode == UpdateMode::Change, - || { - // clippy::all should only include the following lint groups: - let all_group_lints = usable_lints.iter().filter(|l| { - matches!( - &*l.group, - "correctness" | "suspicious" | "style" | "complexity" | "perf" - ) - }); - - gen_lint_group_list(all_group_lints) - }, - ) - .changed; + if file_change && update_mode == UpdateMode::Check { + exit_with_failure(); + } - // Generate the list of lints for all other lint groups - for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { - file_change |= replace_region_in_file( - Path::new("clippy_lints/src/lib.rs"), - &format!("store.register_group\\(true, \"clippy::{}\"", lint_group), - r#"\]\);"#, - false, - update_mode == UpdateMode::Change, - || gen_lint_group_list(lints.iter()), + process_file( + "clippy_lints/src/lib.register_lints.rs", + update_mode, + &gen_register_lint_list(internal_lints.iter(), usable_lints.iter()), + ); + process_file( + "clippy_lints/src/lib.deprecated.rs", + update_mode, + &gen_deprecated(deprecated_lints.iter()), + ); + + let all_group_lints = usable_lints.iter().filter(|l| { + matches!( + &*l.group, + "correctness" | "suspicious" | "style" | "complexity" | "perf" ) - .changed; - } + }); + let content = gen_lint_group_list("all", all_group_lints); + process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content); - if update_mode == UpdateMode::Check && file_change { - println!( - "Not all lints defined properly. \ - Please run `cargo dev update_lints` to make sure all lints are defined properly." + for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { + let content = gen_lint_group_list(&lint_group, lints.iter()); + process_file( + &format!("clippy_lints/src/lib.register_{}.rs", lint_group), + update_mode, + &content, ); - std::process::exit(1); } } @@ -150,3 +168,538 @@ pub fn print_lints() { fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } + +fn process_file(path: impl AsRef, update_mode: UpdateMode, content: &str) { + if update_mode == UpdateMode::Check { + let old_content = + fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.as_ref().display(), e)); + if content != old_content { + exit_with_failure(); + } + } else { + fs::write(&path, content.as_bytes()) + .unwrap_or_else(|e| panic!("Cannot write to {}: {}", path.as_ref().display(), e)); + } +} + +fn exit_with_failure() { + println!( + "Not all lints defined properly. \ + Please run `cargo dev update_lints` to make sure all lints are defined properly." + ); + std::process::exit(1); +} + +/// Lint data parsed from the Clippy source code. +#[derive(Clone, PartialEq, Debug)] +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 { + 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(), + } + } + + /// Returns all non-deprecated lints and non-internal lints + #[must_use] + fn usable_lints(lints: &[Self]) -> Vec { + lints + .iter() + .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal")) + .cloned() + .collect() + } + + /// Returns all internal lints (not `internal_warn` lints) + #[must_use] + fn internal_lints(lints: &[Self]) -> Vec { + 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> { + lints.map(|lint| (lint.group.to_string(), lint)).into_group_map() + } +} + +/// 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(); + details.sort_unstable(); + + let mut output = GENERATED_FILE_COMMENT.to_string(); + + output.push_str(&format!( + "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![\n", + group_name + )); + for (module, name) in details { + output.push_str(&format!(" LintId::of({}::{}),\n", module, name)); + } + output.push_str("])\n"); + + output +} + +/// Generates the module declarations for `lints` +#[must_use] +fn gen_modules_list<'a>(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 { + let mut output = GENERATED_FILE_COMMENT.to_string(); + output.push_str("{\n"); + for Lint { name, deprecation, .. } in lints { + output.push_str(&format!( + concat!( + " store.register_removed(\n", + " \"clippy::{}\",\n", + " \"{}\",\n", + " );\n" + ), + name, + deprecation.as_ref().expect("`lints` are deprecated") + )); + } + output.push_str("}\n"); + + output +} + +/// Generates the code for registering lints +#[must_use] +fn gen_register_lint_list<'a>( + internal_lints: impl Iterator, + usable_lints: impl Iterator, +) -> String { + let mut details: Vec<_> = internal_lints + .map(|l| (false, &l.module, l.name.to_uppercase())) + .chain(usable_lints.map(|l| (true, &l.module, l.name.to_uppercase()))) + .collect(); + details.sort_unstable(); + + let mut output = GENERATED_FILE_COMMENT.to_string(); + output.push_str("store.register_lints(&[\n"); + + for (is_public, module_name, lint_name) in details { + if !is_public { + output.push_str(" #[cfg(feature = \"internal-lints\")]\n"); + } + output.push_str(&format!(" {}::{},\n", module_name, lint_name)); + } + output.push_str("])\n"); + + 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)) +} + +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(); + } + + let module = rel_path + .components() + .map(|c| c.as_os_str().to_str().unwrap()) + .collect::>() + .join("::"); + + parse_contents(&content, &module) +} + +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() +} + +/// 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"))) +} + +/// Whether a file has had its text changed or not +#[derive(PartialEq, Debug)] +struct FileChange { + changed: bool, + new_lines: String, +} + +/// Replaces a region in a file delimited by two lines matching regexes. +/// +/// `path` is the relative path to the file on which you want to perform the replacement. +/// +/// See `replace_region_in_text` for documentation of the other options. +/// +/// # Panics +/// +/// Panics if the path could not read or then written +fn replace_region_in_file( + 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()); + } + 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! { + pub PTR_ARG, + style, + "really long \ + text" +} + +declare_clippy_lint!{ + pub DOC_MARKDOWN, + pedantic, + "single line" +} + +/// some doc comment +declare_deprecated_lint! { + 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); +} + +#[cfg(test)] +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); + } + + #[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()] + }); + 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); + 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"), + ]; + let expected = vec![Lint::new( + "should_assert_eq2", + "Not Deprecated", + "abc", + None, + "module_name", + )]; + assert_eq!(expected, Lint::usable_lints(&lints)); + } + + #[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"), + ]; + 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"), + ], + ); + expected.insert( + "group2".to_string(), + vec![Lint::new("should_assert_eq2", "group2", "abc", None, "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.to_string()), + format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string()), + ]; + 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", + ), + ]; + + let expected = GENERATED_FILE_COMMENT.to_string() + + &[ + "{", + " store.register_removed(", + " \"clippy::should_assert_eq\",", + " \"has been superseded by should_assert_eq2\",", + " );", + " store.register_removed(", + " \"clippy::another_deprecated\",", + " \"will be removed\",", + " );", + "}", + ] + .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())); + } + + #[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"), + ]; + let expected = GENERATED_FILE_COMMENT.to_string() + + &[ + "store.register_group(true, \"clippy::group1\", Some(\"clippy_group1\"), vec![", + " LintId::of(module_name::ABC),", + " LintId::of(module_name::INTERNAL),", + " LintId::of(module_name::SHOULD_ASSERT_EQ),", + "])", + ] + .join("\n") + + "\n"; + + let result = gen_lint_group_list("group1", lints.iter()); + + assert_eq!(expected, result); + } +} diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 2ef7dcc1775a6..6f8b645dd70d1 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -563,7 +563,7 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) { skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip; // Only lint outer attributes, because custom inner attributes are unstable // Tracking issue: https://github.com/rust-lang/rust/issues/54726 - if let AttrStyle::Outer = attr.style; + if attr.style == AttrStyle::Outer; then { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 0cc79c8b6e8cb..28615b9217cd3 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -16,7 +16,7 @@ declare_clippy_lint! { /// The Mutex types found in std::sync and parking_lot /// are not designed to operate in an async context across await points. /// - /// There are two potential solutions. One is to use an asynx-aware Mutex + /// There are two potential solutions. One is to use an async-aware Mutex /// type. Many asynchronous foundation crates provide such a Mutex type. The /// other solution is to ensure the mutex is unlocked before calling await, /// either by introducing a scope or an explicit call to Drop::drop. diff --git a/clippy_lints/src/casts/cast_precision_loss.rs b/clippy_lints/src/casts/cast_precision_loss.rs index 63ac8fd2dd269..334e1646cd4fc 100644 --- a/clippy_lints/src/casts/cast_precision_loss.rs +++ b/clippy_lints/src/casts/cast_precision_loss.rs @@ -12,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca } let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); - let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() { + let to_nbits = if cast_to.kind() == &ty::Float(FloatTy::F32) { 32 } else { 64 diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 15252ef96cd1d..fdef0abe9708e 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -83,6 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { if !attrs.iter().any(|attr| attr.doc_str().is_some()); if let child_attrs = cx.tcx.hir().attrs(impl_item_hir); if !child_attrs.iter().any(|attr| attr.doc_str().is_some()); + if adt_def.is_struct(); then { if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() { diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 8416b8440dfbe..24ac5917dcb05 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -393,7 +393,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { if_chain! { if let Some(header) = kind.header(); - if let Unsafety::Unsafe = header.unsafety; + if header.unsafety == Unsafety::Unsafe; then { self.has_unsafe = true; } @@ -408,7 +408,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { } if let ExprKind::Block(block, _) = expr.kind { - if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules { + if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { self.has_unsafe = true; } } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 33ed6273ad2c5..9840affbf6fd8 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -236,7 +236,17 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { hir::ItemKind::Impl(ref impl_) => { self.in_trait_impl = impl_.of_trait.is_some(); }, - _ => {}, + hir::ItemKind::Trait(_, unsafety, ..) => { + if !headers.safety && unsafety == hir::Unsafety::Unsafe { + span_lint( + cx, + MISSING_SAFETY_DOC, + item.span, + "docs for unsafe trait missing `# Safety` section", + ); + } + }, + _ => (), } } @@ -396,6 +406,15 @@ struct DocHeaders { } fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &'a [Attribute]) -> DocHeaders { + use pulldown_cmark::{BrokenLink, CowStr, Options}; + /// We don't want the parser to choke on intra doc links. Since we don't + /// actually care about rendering them, just pretend that all broken links are + /// point to a fake address. + #[allow(clippy::unnecessary_wraps)] // we're following a type signature + fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> { + Some(("fake".into(), "fake".into())) + } + let mut doc = String::new(); let mut spans = vec![]; @@ -430,7 +449,10 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs }; } - let parser = pulldown_cmark::Parser::new(&doc).into_offset_iter(); + let mut cb = fake_broken_link_callback; + + let parser = + pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter(); // Iterate over all `Events` and combine consecutive events into one let events = parser.coalesce(|previous, current| { use pulldown_cmark::Event::Text; diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs new file mode 100644 index 0000000000000..0c6ba91c9430b --- /dev/null +++ b/clippy_lints/src/equatable_if_let.rs @@ -0,0 +1,100 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::implements_trait; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for pattern matchings that can be expressed using equality. + /// + /// ### Why is this bad? + /// + /// * It reads better and has less cognitive load because equality won't cause binding. + /// * It is a [Yoda condition](https://en.wikipedia.org/wiki/Yoda_conditions). Yoda conditions are widely + /// criticized for increasing the cognitive load of reading the code. + /// * Equality is a simple bool expression and can be merged with `&&` and `||` and + /// reuse if blocks + /// + /// ### Example + /// ```rust,ignore + /// if let Some(2) = x { + /// do_thing(); + /// } + /// ``` + /// Should be written + /// ```rust,ignore + /// if x == Some(2) { + /// do_thing(); + /// } + /// ``` + pub EQUATABLE_IF_LET, + nursery, + "using pattern matching instead of equality" +} + +declare_lint_pass!(PatternEquality => [EQUATABLE_IF_LET]); + +/// detects if pattern matches just one thing +fn unary_pattern(pat: &Pat<'_>) -> bool { + fn array_rec(pats: &[Pat<'_>]) -> bool { + pats.iter().all(unary_pattern) + } + match &pat.kind { + PatKind::Slice(_, _, _) | PatKind::Range(_, _, _) | PatKind::Binding(..) | PatKind::Wild | PatKind::Or(_) => { + false + }, + PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), + PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => !etc.is_some() && array_rec(a), + PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x), + PatKind::Path(_) | PatKind::Lit(_) => true, + } +} + +fn is_structural_partial_eq(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool { + if let Some(def_id) = cx.tcx.lang_items().eq_trait() { + implements_trait(cx, ty, def_id, &[other.into()]) + } else { + false + } +} + +impl<'tcx> LateLintPass<'tcx> for PatternEquality { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if_chain! { + if let ExprKind::Let(pat, exp, _) = expr.kind; + if unary_pattern(pat); + let exp_ty = cx.typeck_results().expr_ty(exp); + let pat_ty = cx.typeck_results().pat_ty(pat); + if is_structural_partial_eq(cx, exp_ty, pat_ty); + then { + + let mut applicability = Applicability::MachineApplicable; + let pat_str = match pat.kind { + PatKind::Struct(..) => format!( + "({})", + snippet_with_applicability(cx, pat.span, "..", &mut applicability), + ), + _ => snippet_with_applicability(cx, pat.span, "..", &mut applicability).to_string(), + }; + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using equality", + "try", + format!( + "{} == {}", + snippet_with_applicability(cx, exp.span, "..", &mut applicability), + pat_str, + ), + applicability, + ); + } + } + } +} diff --git a/clippy_lints/src/erasing_op.rs b/clippy_lints/src/erasing_op.rs index 026d14d0ea265..d0944c37cf5f7 100644 --- a/clippy_lints/src/erasing_op.rs +++ b/clippy_lints/src/erasing_op.rs @@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for ErasingOp { } fn check(cx: &LateContext<'_>, e: &Expr<'_>, span: Span) { - if let Some(Constant::Int(0)) = constant_simple(cx, cx.typeck_results(), e) { + if constant_simple(cx, cx.typeck_results(), e) == Some(Constant::Int(0)) { span_lint( cx, ERASING_OP, diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 090be73af3b7a..75b1c882c2336 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { for trait_item in items { if trait_item.id.hir_id() == hir_id { // be sure we have `self` parameter in this function - if let AssocItemKind::Fn { has_self: true } = trait_item.kind { + if trait_item.kind == (AssocItemKind::Fn { has_self: true }) { trait_self_ty = Some( TraitRef::identity(cx.tcx, trait_item.id.def_id.to_def_id()) .self_ty() diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 9df92cc5b6406..765a6c7585a20 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if let Some(mut snippet) = snippet_opt(cx, callee.span) { if_chain! { if let ty::Closure(_, substs) = callee_ty.peel_refs().kind(); - if let ClosureKind::FnMut = substs.as_closure().kind(); + if substs.as_closure().kind() == ClosureKind::FnMut; if get_enclosing_loop_or_closure(cx.tcx, expr).is_some() || UsedAfterExprVisitor::is_found(cx, callee); diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 8714ce90164c8..1b56dd4b08177 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -141,7 +141,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { match typ.kind() { ty::FnDef(..) | ty::FnPtr(_) => { let sig = typ.fn_sig(self.cx.tcx); - if let ty::Never = self.cx.tcx.erase_late_bound_regions(sig).output().kind() { + if self.cx.tcx.erase_late_bound_regions(sig).output().kind() == &ty::Never { self.report_diverging_sub_expr(e); } }, diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 129a8475e1c23..8df7f91ce59f5 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -112,7 +112,7 @@ fn is_display_arg(expr: &Expr<'_>) -> bool { if let ExprKind::Call(_, [_, fmt]) = expr.kind; if let ExprKind::Path(QPath::Resolved(_, path)) = fmt.kind; if let [.., t, _] = path.segments; - if t.ident.name.as_str() == "Display"; + if t.ident.name == sym::Display; then { true } else { false } } } diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 5feb0ce8dece7..73bdd67ff5d25 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,5 +1,4 @@ use clippy_utils::source::snippet; -use if_chain::if_chain; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -62,16 +61,9 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { // `1 << 0` is a common pattern in bit manipulation code - if_chain! { - if let BinOpKind::Shl = cmp.node; - if let Some(Constant::Int(0)) = constant_simple(cx, cx.typeck_results(), right); - if let Some(Constant::Int(1)) = constant_simple(cx, cx.typeck_results(), left); - then { - return true; - } - } - - false + cmp.node == BinOpKind::Shl + && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0)) + && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)) } #[allow(clippy::cast_possible_wrap)] diff --git a/clippy_lints/src/if_then_panic.rs b/clippy_lints/src/if_then_panic.rs index ee575c81a8b0b..10bca59e6d06a 100644 --- a/clippy_lints/src/if_then_panic.rs +++ b/clippy_lints/src/if_then_panic.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::PanicExpn; -use clippy_utils::is_expn_of; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{is_expn_of, sugg}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -74,12 +74,14 @@ impl LateLintPass<'_> for IfThenPanic { }; let mut applicability = Applicability::MachineApplicable; let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); - - let cond_sugg = - if let ExprKind::DropTemps(Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..}) = cond.kind { - snippet_with_applicability(cx, not_expr.span, "..", &mut applicability).to_string() + let cond_sugg = if let ExprKind::DropTemps(e, ..) = cond.kind { + if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e { + sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string() + } else { + format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par().to_string()) + } } else { - format!("!{}", snippet_with_applicability(cx, cond.span, "..", &mut applicability)) + format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par().to_string()) }; span_lint_and_sugg( diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 9da06d1418e2d..81eb51e6f7cee 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -167,12 +167,20 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { continue; } let generics_suggestion_span = generics.span.substitute_dummy({ - let pos = snippet_opt(cx, item.span.until(body.params[0].pat.span)) - .and_then(|snip| { - let i = snip.find("fn")?; - Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32)) - }) - .expect("failed to create span for type parameters"); + let pos = snippet_opt( + cx, + Span::new( + item.span.lo(), + body.params[0].pat.span.lo(), + item.span.ctxt(), + item.span.parent(), + ), + ) + .and_then(|snip| { + let i = snip.find("fn")?; + Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32)) + }) + .expect("failed to create span for type parameters"); Span::new(pos, pos, item.span.ctxt(), item.span.parent()) }); diff --git a/clippy_lints/src/integer_division.rs b/clippy_lints/src/integer_division.rs index a0e6f12b8122e..c962e814fa5c2 100644 --- a/clippy_lints/src/integer_division.rs +++ b/clippy_lints/src/integer_division.rs @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for IntegerDivision { fn is_integer_division<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool { if_chain! { if let hir::ExprKind::Binary(binop, left, right) = &expr.kind; - if let hir::BinOpKind::Div = &binop.node; + if binop.node == hir::BinOpKind::Div; then { let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right)); return left_ty.is_integral() && right_ty.is_integral(); diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index e4b8e7546283b..392166237be50 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,13 +1,14 @@ //! lint when there is a large size difference between variants on an enum use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; -use rustc_hir::{Item, ItemKind, VariantData}; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::LayoutOf; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Span; declare_clippy_lint! { /// ### What it does @@ -58,6 +59,17 @@ impl LargeEnumVariant { } } +struct FieldInfo { + ind: usize, + size: u64, +} + +struct VariantInfo { + ind: usize, + size: u64, + fields_size: Vec, +} + impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { @@ -68,72 +80,95 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { if let ItemKind::Enum(ref def, _) = item.kind { let ty = cx.tcx.type_of(item.def_id); let adt = ty.ty_adt_def().expect("already checked whether this is an enum"); - - let mut largest_variant: Option<(_, _)> = None; - let mut second_variant: Option<(_, _)> = None; - - for (i, variant) in adt.variants.iter().enumerate() { - let size: u64 = variant - .fields - .iter() - .filter_map(|f| { - let ty = cx.tcx.type_of(f.did); - // don't count generics by filtering out everything - // that does not have a layout - cx.layout_of(ty).ok().map(|l| l.size.bytes()) - }) - .sum(); - - let grouped = (size, (i, variant)); - - if grouped.0 >= largest_variant.map_or(0, |x| x.0) { - second_variant = largest_variant; - largest_variant = Some(grouped); - } + if adt.variants.len() <= 1 { + return; } + let mut variants_size: Vec = adt + .variants + .iter() + .enumerate() + .map(|(i, variant)| { + let mut fields_size = Vec::new(); + let size: u64 = variant + .fields + .iter() + .enumerate() + .filter_map(|(i, f)| { + let ty = cx.tcx.type_of(f.did); + // don't count generics by filtering out everything + // that does not have a layout + cx.layout_of(ty).ok().map(|l| { + let size = l.size.bytes(); + fields_size.push(FieldInfo { ind: i, size }); + size + }) + }) + .sum(); + VariantInfo { + ind: i, + size, + fields_size, + } + }) + .collect(); - if let (Some(largest), Some(second)) = (largest_variant, second_variant) { - let difference = largest.0 - second.0; + variants_size.sort_by(|a, b| (b.size.cmp(&a.size))); - if difference > self.maximum_size_difference_allowed { - let (i, variant) = largest.1; + let mut difference = variants_size[0].size - variants_size[1].size; + if difference > self.maximum_size_difference_allowed { + let help_text = "consider boxing the large fields to reduce the total size of the enum"; + span_lint_and_then( + cx, + LARGE_ENUM_VARIANT, + def.variants[variants_size[0].ind].span, + "large size difference between variants", + |diag| { + diag.span_label( + def.variants[variants_size[0].ind].span, + &format!("this variant is {} bytes", variants_size[0].size), + ); + diag.span_note( + def.variants[variants_size[1].ind].span, + &format!("and the second-largest variant is {} bytes:", variants_size[1].size), + ); - let help_text = "consider boxing the large fields to reduce the total size of the enum"; - span_lint_and_then( - cx, - LARGE_ENUM_VARIANT, - def.variants[i].span, - "large size difference between variants", - |diag| { - diag.span_label( - def.variants[(largest.1).0].span, - &format!("this variant is {} bytes", largest.0), - ); - diag.span_note( - def.variants[(second.1).0].span, - &format!("and the second-largest variant is {} bytes:", second.0), - ); - if variant.fields.len() == 1 { - let span = match def.variants[i].data { - VariantData::Struct(fields, ..) | VariantData::Tuple(fields, ..) => { - fields[0].ty.span - }, - VariantData::Unit(..) => unreachable!(), - }; - if let Some(snip) = snippet_opt(cx, span) { - diag.span_suggestion( - span, - help_text, - format!("Box<{}>", snip), - Applicability::MaybeIncorrect, - ); - return; + let fields = def.variants[variants_size[0].ind].data.fields(); + variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size))); + let mut applicability = Applicability::MaybeIncorrect; + let sugg: Vec<(Span, String)> = variants_size[0] + .fields_size + .iter() + .rev() + .map_while(|val| { + if difference > self.maximum_size_difference_allowed { + difference = difference.saturating_sub(val.size); + Some(( + fields[val.ind].ty.span, + format!( + "Box<{}>", + snippet_with_applicability( + cx, + fields[val.ind].ty.span, + "..", + &mut applicability + ) + .into_owned() + ), + )) + } else { + None } - } - diag.span_help(def.variants[i].span, help_text); - }, - ); - } + }) + .collect(); + + if !sugg.is_empty() { + diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect); + return; + } + + diag.span_help(def.variants[variants_size[0].ind].span, help_text); + }, + ); } } } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index de46e50a68a13..f336fb9d42f88 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -455,14 +455,10 @@ fn is_empty_array(expr: &Expr<'_>) -> bool { fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool { - if let ty::AssocKind::Fn = item.kind { - if item.ident.name.as_str() == "is_empty" { - let sig = cx.tcx.fn_sig(item.def_id); - let ty = sig.skip_binder(); - ty.inputs().len() == 1 - } else { - false - } + if item.kind == ty::AssocKind::Fn && item.ident.name.as_str() == "is_empty" { + let sig = cx.tcx.fn_sig(item.def_id); + let ty = sig.skip_binder(); + ty.inputs().len() == 1 } else { false } diff --git a/clippy_lints/src/lib.deprecated.rs b/clippy_lints/src/lib.deprecated.rs new file mode 100644 index 0000000000000..80bde1b11384b --- /dev/null +++ b/clippy_lints/src/lib.deprecated.rs @@ -0,0 +1,70 @@ +// 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. + +{ + store.register_removed( + "clippy::should_assert_eq", + "`assert!()` will be more flexible with RFC 2011", + ); + store.register_removed( + "clippy::extend_from_slice", + "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice", + ); + store.register_removed( + "clippy::range_step_by_zero", + "`iterator.step_by(0)` panics nowadays", + ); + store.register_removed( + "clippy::unstable_as_slice", + "`Vec::as_slice` has been stabilized in 1.7", + ); + store.register_removed( + "clippy::unstable_as_mut_slice", + "`Vec::as_mut_slice` has been stabilized in 1.7", + ); + store.register_removed( + "clippy::misaligned_transmute", + "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", + ); + store.register_removed( + "clippy::assign_ops", + "using compound assignment operators (e.g., `+=`) is harmless", + ); + store.register_removed( + "clippy::if_let_redundant_pattern_matching", + "this lint has been changed to redundant_pattern_matching", + ); + store.register_removed( + "clippy::unsafe_vector_initialization", + "the replacement suggested by this lint had substantially different behavior", + ); + store.register_removed( + "clippy::unused_collect", + "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint", + ); + store.register_removed( + "clippy::replace_consts", + "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants", + ); + store.register_removed( + "clippy::regex_macro", + "the regex! macro has been removed from the regex crate in 2018", + ); + store.register_removed( + "clippy::find_map", + "this lint has been replaced by `manual_find_map`, a more specific lint", + ); + store.register_removed( + "clippy::filter_map", + "this lint has been replaced by `manual_filter_map`, a more specific lint", + ); + store.register_removed( + "clippy::pub_enum_variant_names", + "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items", + ); + store.register_removed( + "clippy::wrong_pub_self_convention", + "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items", + ); +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs new file mode 100644 index 0000000000000..3e6e0244754fb --- /dev/null +++ b/clippy_lints/src/lib.register_all.rs @@ -0,0 +1,304 @@ +// 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. + +store.register_group(true, "clippy::all", Some("clippy_all"), vec![ + LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), + LintId::of(approx_const::APPROX_CONSTANT), + LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), + LintId::of(assign_ops::ASSIGN_OP_PATTERN), + LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), + LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), + LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), + LintId::of(attrs::DEPRECATED_CFG_ATTR), + LintId::of(attrs::DEPRECATED_SEMVER), + LintId::of(attrs::MISMATCHED_TARGET_OS), + LintId::of(attrs::USELESS_ATTRIBUTE), + LintId::of(bit_mask::BAD_BIT_MASK), + LintId::of(bit_mask::INEFFECTIVE_BIT_MASK), + LintId::of(blacklisted_name::BLACKLISTED_NAME), + LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), + LintId::of(booleans::LOGIC_BUG), + LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(casts::CAST_REF_TO_MUT), + LintId::of(casts::CHAR_LIT_AS_U8), + LintId::of(casts::FN_TO_NUMERIC_CAST), + LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), + LintId::of(casts::UNNECESSARY_CAST), + LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), + LintId::of(collapsible_if::COLLAPSIBLE_IF), + LintId::of(collapsible_match::COLLAPSIBLE_MATCH), + LintId::of(comparison_chain::COMPARISON_CHAIN), + LintId::of(copies::IFS_SAME_COND), + LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), + LintId::of(derivable_impls::DERIVABLE_IMPLS), + LintId::of(derive::DERIVE_HASH_XOR_EQ), + LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(doc::MISSING_SAFETY_DOC), + LintId::of(doc::NEEDLESS_DOCTEST_MAIN), + 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_REF), + LintId::of(drop_forget_ref::FORGET_COPY), + LintId::of(drop_forget_ref::FORGET_REF), + LintId::of(duration_subsec::DURATION_SUBSEC), + LintId::of(entry::MAP_ENTRY), + LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), + LintId::of(enum_variants::ENUM_VARIANT_NAMES), + LintId::of(enum_variants::MODULE_INCEPTION), + LintId::of(eq_op::EQ_OP), + LintId::of(eq_op::OP_REF), + LintId::of(erasing_op::ERASING_OP), + LintId::of(escape::BOXED_LOCAL), + LintId::of(eta_reduction::REDUNDANT_CLOSURE), + LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), + LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), + LintId::of(explicit_write::EXPLICIT_WRITE), + LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), + LintId::of(float_literal::EXCESSIVE_PRECISION), + LintId::of(format::USELESS_FORMAT), + LintId::of(formatting::POSSIBLE_MISSING_COMMA), + LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), + LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), + LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(from_over_into::FROM_OVER_INTO), + LintId::of(from_str_radix_10::FROM_STR_RADIX_10), + LintId::of(functions::DOUBLE_MUST_USE), + LintId::of(functions::MUST_USE_UNIT), + LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), + LintId::of(functions::RESULT_UNIT_ERR), + LintId::of(functions::TOO_MANY_ARGUMENTS), + LintId::of(get_last_with_len::GET_LAST_WITH_LEN), + LintId::of(identity_op::IDENTITY_OP), + LintId::of(if_let_mutex::IF_LET_MUTEX), + LintId::of(if_then_panic::IF_THEN_PANIC), + LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), + LintId::of(infinite_iter::INFINITE_ITER), + LintId::of(inherent_to_string::INHERENT_TO_STRING), + LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), + LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), + LintId::of(int_plus_one::INT_PLUS_ONE), + LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), + LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), + LintId::of(len_zero::COMPARISON_TO_EMPTY), + LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), + LintId::of(len_zero::LEN_ZERO), + LintId::of(let_underscore::LET_UNDERSCORE_LOCK), + LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), + LintId::of(lifetimes::NEEDLESS_LIFETIMES), + LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), + LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), + LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), + LintId::of(loops::EMPTY_LOOP), + LintId::of(loops::EXPLICIT_COUNTER_LOOP), + LintId::of(loops::FOR_KV_MAP), + LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), + LintId::of(loops::ITER_NEXT_LOOP), + LintId::of(loops::MANUAL_FLATTEN), + LintId::of(loops::MANUAL_MEMCPY), + LintId::of(loops::MUT_RANGE_BOUND), + LintId::of(loops::NEEDLESS_COLLECT), + LintId::of(loops::NEEDLESS_RANGE_LOOP), + LintId::of(loops::NEVER_LOOP), + LintId::of(loops::SAME_ITEM_PUSH), + LintId::of(loops::SINGLE_ELEMENT_LOOP), + LintId::of(loops::WHILE_IMMUTABLE_CONDITION), + LintId::of(loops::WHILE_LET_LOOP), + LintId::of(loops::WHILE_LET_ON_ITERATOR), + LintId::of(main_recursion::MAIN_RECURSION), + LintId::of(manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(manual_map::MANUAL_MAP), + LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), + LintId::of(manual_strip::MANUAL_STRIP), + LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), + LintId::of(map_clone::MAP_CLONE), + LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), + LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(match_result_ok::MATCH_RESULT_OK), + LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MATCH_AS_REF), + LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), + LintId::of(matches::MATCH_OVERLAPPING_ARM), + LintId::of(matches::MATCH_REF_PATS), + LintId::of(matches::MATCH_SINGLE_BINDING), + LintId::of(matches::REDUNDANT_PATTERN_MATCHING), + LintId::of(matches::SINGLE_MATCH), + LintId::of(matches::WILDCARD_IN_OR_PATTERNS), + LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), + LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), + LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), + LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), + LintId::of(methods::BIND_INSTEAD_OF_MAP), + LintId::of(methods::BYTES_NTH), + LintId::of(methods::CHARS_LAST_CMP), + LintId::of(methods::CHARS_NEXT_CMP), + LintId::of(methods::CLONE_DOUBLE_REF), + LintId::of(methods::CLONE_ON_COPY), + LintId::of(methods::EXPECT_FUN_CALL), + LintId::of(methods::EXTEND_WITH_DRAIN), + LintId::of(methods::FILTER_MAP_IDENTITY), + LintId::of(methods::FILTER_NEXT), + LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::INSPECT_FOR_EACH), + LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::ITERATOR_STEP_BY_ZERO), + LintId::of(methods::ITER_CLONED_COLLECT), + LintId::of(methods::ITER_COUNT), + LintId::of(methods::ITER_NEXT_SLICE), + LintId::of(methods::ITER_NTH), + LintId::of(methods::ITER_NTH_ZERO), + LintId::of(methods::ITER_SKIP_NEXT), + LintId::of(methods::MANUAL_FILTER_MAP), + LintId::of(methods::MANUAL_FIND_MAP), + LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(methods::MANUAL_SPLIT_ONCE), + LintId::of(methods::MANUAL_STR_REPEAT), + LintId::of(methods::MAP_COLLECT_RESULT_UNIT), + LintId::of(methods::MAP_IDENTITY), + LintId::of(methods::NEW_RET_NO_SELF), + LintId::of(methods::OK_EXPECT), + LintId::of(methods::OPTION_AS_REF_DEREF), + LintId::of(methods::OPTION_FILTER_MAP), + LintId::of(methods::OPTION_MAP_OR_NONE), + LintId::of(methods::OR_FUN_CALL), + LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), + LintId::of(methods::SEARCH_IS_SOME), + LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(methods::SINGLE_CHAR_ADD_STR), + LintId::of(methods::SINGLE_CHAR_PATTERN), + LintId::of(methods::SKIP_WHILE_NEXT), + LintId::of(methods::STRING_EXTEND_CHARS), + LintId::of(methods::SUSPICIOUS_MAP), + LintId::of(methods::SUSPICIOUS_SPLITN), + LintId::of(methods::UNINIT_ASSUMED_INIT), + LintId::of(methods::UNNECESSARY_FILTER_MAP), + LintId::of(methods::UNNECESSARY_FOLD), + LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), + LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), + LintId::of(methods::USELESS_ASREF), + LintId::of(methods::WRONG_SELF_CONVENTION), + LintId::of(methods::ZST_OFFSET), + LintId::of(minmax::MIN_MAX), + LintId::of(misc::CMP_NAN), + LintId::of(misc::CMP_OWNED), + LintId::of(misc::MODULO_ONE), + LintId::of(misc::SHORT_CIRCUIT_STATEMENT), + LintId::of(misc::TOPLEVEL_REF_ARG), + LintId::of(misc::ZERO_PTR), + LintId::of(misc_early::BUILTIN_TYPE_SHADOW), + LintId::of(misc_early::DOUBLE_NEG), + LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), + LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), + LintId::of(misc_early::REDUNDANT_PATTERN), + LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), + LintId::of(misc_early::ZERO_PREFIXED_LITERAL), + LintId::of(mut_key::MUTABLE_KEY_TYPE), + LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), + LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), + LintId::of(mutex_atomic::MUTEX_ATOMIC), + LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), + LintId::of(needless_bool::BOOL_COMPARISON), + LintId::of(needless_bool::NEEDLESS_BOOL), + LintId::of(needless_borrow::NEEDLESS_BORROW), + 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), + LintId::of(neg_multiply::NEG_MULTIPLY), + LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), + LintId::of(no_effect::NO_EFFECT), + LintId::of(no_effect::UNNECESSARY_OPERATION), + LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), + LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), + LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), + LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), + LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), + LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), + LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), + LintId::of(precedence::PRECEDENCE), + LintId::of(ptr::CMP_NULL), + LintId::of(ptr::INVALID_NULL_PTR_USAGE), + LintId::of(ptr::MUT_FROM_REF), + LintId::of(ptr::PTR_ARG), + LintId::of(ptr_eq::PTR_EQ), + LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), + LintId::of(question_mark::QUESTION_MARK), + LintId::of(ranges::MANUAL_RANGE_CONTAINS), + LintId::of(ranges::RANGE_ZIP_WITH_LEN), + LintId::of(ranges::REVERSED_EMPTY_RANGES), + LintId::of(redundant_clone::REDUNDANT_CLONE), + LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), + LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), + LintId::of(redundant_slicing::REDUNDANT_SLICING), + LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(reference::DEREF_ADDROF), + LintId::of(reference::REF_IN_DEREF), + LintId::of(regex::INVALID_REGEX), + LintId::of(repeat_once::REPEAT_ONCE), + LintId::of(returns::LET_AND_RETURN), + LintId::of(returns::NEEDLESS_RETURN), + LintId::of(self_assignment::SELF_ASSIGNMENT), + LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), + LintId::of(serde_api::SERDE_API_MISUSE), + 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), + LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), + LintId::of(swap::ALMOST_SWAPPED), + LintId::of(swap::MANUAL_SWAP), + LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), + LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), + LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY), + LintId::of(transmute::CROSSPOINTER_TRANSMUTE), + LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), + LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), + LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), + LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), + LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), + LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), + LintId::of(transmute::TRANSMUTE_PTR_TO_REF), + LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), + LintId::of(transmute::WRONG_TRANSMUTE), + LintId::of(transmuting_null::TRANSMUTING_NULL), + LintId::of(try_err::TRY_ERR), + LintId::of(types::BORROWED_BOX), + LintId::of(types::BOX_COLLECTION), + 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(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), + LintId::of(unit_types::UNIT_ARG), + LintId::of(unit_types::UNIT_CMP), + LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), + LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), + 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), + LintId::of(unused_unit::UNUSED_UNIT), + LintId::of(unwrap::PANICKING_UNWRAP), + LintId::of(unwrap::UNNECESSARY_UNWRAP), + LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), + LintId::of(useless_conversion::USELESS_CONVERSION), + LintId::of(vec::USELESS_VEC), + LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), + LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO), + LintId::of(write::PRINTLN_EMPTY_STRING), + LintId::of(write::PRINT_LITERAL), + LintId::of(write::PRINT_WITH_NEWLINE), + LintId::of(write::WRITELN_EMPTY_STRING), + LintId::of(write::WRITE_LITERAL), + LintId::of(write::WRITE_WITH_NEWLINE), + LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), +]) diff --git a/clippy_lints/src/lib.register_cargo.rs b/clippy_lints/src/lib.register_cargo.rs new file mode 100644 index 0000000000000..1809f2cc7d462 --- /dev/null +++ b/clippy_lints/src/lib.register_cargo.rs @@ -0,0 +1,11 @@ +// 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. + +store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ + LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA), + LintId::of(feature_name::NEGATIVE_FEATURE_NAMES), + LintId::of(feature_name::REDUNDANT_FEATURE_NAMES), + LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), + LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES), +]) diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs new file mode 100644 index 0000000000000..64b82fc0faac8 --- /dev/null +++ b/clippy_lints/src/lib.register_complexity.rs @@ -0,0 +1,94 @@ +// 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. + +store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ + LintId::of(attrs::DEPRECATED_CFG_ATTR), + LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(casts::CHAR_LIT_AS_U8), + LintId::of(casts::UNNECESSARY_CAST), + LintId::of(derivable_impls::DERIVABLE_IMPLS), + LintId::of(double_comparison::DOUBLE_COMPARISONS), + LintId::of(double_parens::DOUBLE_PARENS), + LintId::of(duration_subsec::DURATION_SUBSEC), + LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), + LintId::of(explicit_write::EXPLICIT_WRITE), + LintId::of(format::USELESS_FORMAT), + LintId::of(functions::TOO_MANY_ARGUMENTS), + LintId::of(get_last_with_len::GET_LAST_WITH_LEN), + LintId::of(identity_op::IDENTITY_OP), + LintId::of(int_plus_one::INT_PLUS_ONE), + LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), + LintId::of(lifetimes::NEEDLESS_LIFETIMES), + LintId::of(loops::EXPLICIT_COUNTER_LOOP), + LintId::of(loops::MANUAL_FLATTEN), + LintId::of(loops::SINGLE_ELEMENT_LOOP), + LintId::of(loops::WHILE_LET_LOOP), + LintId::of(manual_strip::MANUAL_STRIP), + LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), + LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), + LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(matches::MATCH_AS_REF), + LintId::of(matches::MATCH_SINGLE_BINDING), + LintId::of(matches::WILDCARD_IN_OR_PATTERNS), + LintId::of(methods::BIND_INSTEAD_OF_MAP), + LintId::of(methods::CLONE_ON_COPY), + LintId::of(methods::FILTER_MAP_IDENTITY), + LintId::of(methods::FILTER_NEXT), + LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::INSPECT_FOR_EACH), + LintId::of(methods::ITER_COUNT), + LintId::of(methods::MANUAL_FILTER_MAP), + LintId::of(methods::MANUAL_FIND_MAP), + LintId::of(methods::MANUAL_SPLIT_ONCE), + LintId::of(methods::MAP_IDENTITY), + LintId::of(methods::OPTION_AS_REF_DEREF), + LintId::of(methods::OPTION_FILTER_MAP), + LintId::of(methods::SEARCH_IS_SOME), + LintId::of(methods::SKIP_WHILE_NEXT), + LintId::of(methods::UNNECESSARY_FILTER_MAP), + LintId::of(methods::USELESS_ASREF), + LintId::of(misc::SHORT_CIRCUIT_STATEMENT), + LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), + LintId::of(misc_early::ZERO_PREFIXED_LITERAL), + LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), + 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), + LintId::of(no_effect::NO_EFFECT), + LintId::of(no_effect::UNNECESSARY_OPERATION), + LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), + LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), + LintId::of(precedence::PRECEDENCE), + LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), + LintId::of(ranges::RANGE_ZIP_WITH_LEN), + LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), + LintId::of(redundant_slicing::REDUNDANT_SLICING), + LintId::of(reference::DEREF_ADDROF), + LintId::of(reference::REF_IN_DEREF), + LintId::of(repeat_once::REPEAT_ONCE), + LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), + LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), + LintId::of(swap::MANUAL_SWAP), + LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), + LintId::of(transmute::CROSSPOINTER_TRANSMUTE), + LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), + LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), + LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), + LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), + LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), + LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), + LintId::of(transmute::TRANSMUTE_PTR_TO_REF), + LintId::of(types::BORROWED_BOX), + LintId::of(types::TYPE_COMPLEXITY), + LintId::of(types::VEC_BOX), + LintId::of(unit_types::UNIT_ARG), + LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(unwrap::UNNECESSARY_UNWRAP), + LintId::of(useless_conversion::USELESS_CONVERSION), + LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), +]) diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs new file mode 100644 index 0000000000000..e0ef7b3b8af9f --- /dev/null +++ b/clippy_lints/src/lib.register_correctness.rs @@ -0,0 +1,73 @@ +// 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. + +store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ + LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), + LintId::of(approx_const::APPROX_CONSTANT), + LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), + LintId::of(attrs::DEPRECATED_SEMVER), + LintId::of(attrs::MISMATCHED_TARGET_OS), + LintId::of(attrs::USELESS_ATTRIBUTE), + LintId::of(bit_mask::BAD_BIT_MASK), + LintId::of(bit_mask::INEFFECTIVE_BIT_MASK), + LintId::of(booleans::LOGIC_BUG), + LintId::of(casts::CAST_REF_TO_MUT), + LintId::of(copies::IFS_SAME_COND), + LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(derive::DERIVE_HASH_XOR_EQ), + LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(drop_forget_ref::DROP_COPY), + LintId::of(drop_forget_ref::DROP_REF), + LintId::of(drop_forget_ref::FORGET_COPY), + LintId::of(drop_forget_ref::FORGET_REF), + LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), + LintId::of(eq_op::EQ_OP), + LintId::of(erasing_op::ERASING_OP), + LintId::of(formatting::POSSIBLE_MISSING_COMMA), + LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), + LintId::of(if_let_mutex::IF_LET_MUTEX), + LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), + LintId::of(infinite_iter::INFINITE_ITER), + LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), + LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), + LintId::of(let_underscore::LET_UNDERSCORE_LOCK), + LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), + LintId::of(loops::ITER_NEXT_LOOP), + LintId::of(loops::NEVER_LOOP), + LintId::of(loops::WHILE_IMMUTABLE_CONDITION), + LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), + LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), + LintId::of(methods::CLONE_DOUBLE_REF), + LintId::of(methods::ITERATOR_STEP_BY_ZERO), + LintId::of(methods::SUSPICIOUS_SPLITN), + LintId::of(methods::UNINIT_ASSUMED_INIT), + LintId::of(methods::ZST_OFFSET), + LintId::of(minmax::MIN_MAX), + LintId::of(misc::CMP_NAN), + LintId::of(misc::MODULO_ONE), + LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), + LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), + LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), + LintId::of(ptr::INVALID_NULL_PTR_USAGE), + LintId::of(ptr::MUT_FROM_REF), + LintId::of(ranges::REVERSED_EMPTY_RANGES), + LintId::of(regex::INVALID_REGEX), + LintId::of(self_assignment::SELF_ASSIGNMENT), + LintId::of(serde_api::SERDE_API_MISUSE), + LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), + LintId::of(swap::ALMOST_SWAPPED), + LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY), + 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(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), + LintId::of(unit_types::UNIT_CMP), + LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), + LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), + LintId::of(unwrap::PANICKING_UNWRAP), + LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO), +]) diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs new file mode 100644 index 0000000000000..c8c1e0262abae --- /dev/null +++ b/clippy_lints/src/lib.register_internal.rs @@ -0,0 +1,18 @@ +// 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. + +store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ + LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL), + LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), + LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS), + LintId::of(utils::internal_lints::DEFAULT_LINT), + LintId::of(utils::internal_lints::IF_CHAIN_STYLE), + LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL), + LintId::of(utils::internal_lints::INVALID_PATHS), + LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), + LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), + LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), + LintId::of(utils::internal_lints::PRODUCE_ICE), + LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), +]) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs new file mode 100644 index 0000000000000..2ba2b3da55cd1 --- /dev/null +++ b/clippy_lints/src/lib.register_lints.rs @@ -0,0 +1,510 @@ +// 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. + +store.register_lints(&[ + #[cfg(feature = "internal-lints")] + utils::internal_lints::CLIPPY_LINTS_INTERNAL, + #[cfg(feature = "internal-lints")] + utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + #[cfg(feature = "internal-lints")] + utils::internal_lints::COMPILER_LINT_FUNCTIONS, + #[cfg(feature = "internal-lints")] + utils::internal_lints::DEFAULT_LINT, + #[cfg(feature = "internal-lints")] + utils::internal_lints::IF_CHAIN_STYLE, + #[cfg(feature = "internal-lints")] + utils::internal_lints::INTERNING_DEFINED_SYMBOL, + #[cfg(feature = "internal-lints")] + utils::internal_lints::INVALID_PATHS, + #[cfg(feature = "internal-lints")] + utils::internal_lints::LINT_WITHOUT_LINT_PASS, + #[cfg(feature = "internal-lints")] + utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + #[cfg(feature = "internal-lints")] + utils::internal_lints::OUTER_EXPN_EXPN_DATA, + #[cfg(feature = "internal-lints")] + utils::internal_lints::PRODUCE_ICE, + #[cfg(feature = "internal-lints")] + utils::internal_lints::UNNECESSARY_SYMBOL_STR, + absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, + approx_const::APPROX_CONSTANT, + arithmetic::FLOAT_ARITHMETIC, + arithmetic::INTEGER_ARITHMETIC, + as_conversions::AS_CONVERSIONS, + asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, + asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, + assertions_on_constants::ASSERTIONS_ON_CONSTANTS, + assign_ops::ASSIGN_OP_PATTERN, + assign_ops::MISREFACTORED_ASSIGN_OP, + async_yields_async::ASYNC_YIELDS_ASYNC, + attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, + attrs::DEPRECATED_CFG_ATTR, + attrs::DEPRECATED_SEMVER, + attrs::EMPTY_LINE_AFTER_OUTER_ATTR, + attrs::INLINE_ALWAYS, + attrs::MISMATCHED_TARGET_OS, + attrs::USELESS_ATTRIBUTE, + await_holding_invalid::AWAIT_HOLDING_LOCK, + await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, + bit_mask::BAD_BIT_MASK, + bit_mask::INEFFECTIVE_BIT_MASK, + bit_mask::VERBOSE_BIT_MASK, + blacklisted_name::BLACKLISTED_NAME, + blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, + bool_assert_comparison::BOOL_ASSERT_COMPARISON, + booleans::LOGIC_BUG, + booleans::NONMINIMAL_BOOL, + bytecount::NAIVE_BYTECOUNT, + cargo_common_metadata::CARGO_COMMON_METADATA, + case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + casts::CAST_LOSSLESS, + casts::CAST_POSSIBLE_TRUNCATION, + casts::CAST_POSSIBLE_WRAP, + casts::CAST_PRECISION_LOSS, + casts::CAST_PTR_ALIGNMENT, + casts::CAST_REF_TO_MUT, + casts::CAST_SIGN_LOSS, + casts::CHAR_LIT_AS_U8, + casts::FN_TO_NUMERIC_CAST, + casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + casts::PTR_AS_PTR, + casts::UNNECESSARY_CAST, + checked_conversions::CHECKED_CONVERSIONS, + cognitive_complexity::COGNITIVE_COMPLEXITY, + collapsible_if::COLLAPSIBLE_ELSE_IF, + collapsible_if::COLLAPSIBLE_IF, + collapsible_match::COLLAPSIBLE_MATCH, + comparison_chain::COMPARISON_CHAIN, + copies::BRANCHES_SHARING_CODE, + copies::IFS_SAME_COND, + copies::IF_SAME_THEN_ELSE, + copies::SAME_FUNCTIONS_IN_IF_CONDITION, + copy_iterator::COPY_ITERATOR, + create_dir::CREATE_DIR, + dbg_macro::DBG_MACRO, + default::DEFAULT_TRAIT_ACCESS, + default::FIELD_REASSIGN_WITH_DEFAULT, + default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK, + dereference::EXPLICIT_DEREF_METHODS, + derivable_impls::DERIVABLE_IMPLS, + derive::DERIVE_HASH_XOR_EQ, + derive::DERIVE_ORD_XOR_PARTIAL_ORD, + derive::EXPL_IMPL_CLONE_ON_COPY, + derive::UNSAFE_DERIVE_DESERIALIZE, + disallowed_method::DISALLOWED_METHOD, + disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS, + disallowed_type::DISALLOWED_TYPE, + doc::DOC_MARKDOWN, + doc::MISSING_ERRORS_DOC, + doc::MISSING_PANICS_DOC, + doc::MISSING_SAFETY_DOC, + doc::NEEDLESS_DOCTEST_MAIN, + double_comparison::DOUBLE_COMPARISONS, + double_parens::DOUBLE_PARENS, + drop_forget_ref::DROP_COPY, + drop_forget_ref::DROP_REF, + drop_forget_ref::FORGET_COPY, + drop_forget_ref::FORGET_REF, + duration_subsec::DURATION_SUBSEC, + else_if_without_else::ELSE_IF_WITHOUT_ELSE, + empty_enum::EMPTY_ENUM, + entry::MAP_ENTRY, + enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, + enum_variants::ENUM_VARIANT_NAMES, + enum_variants::MODULE_INCEPTION, + enum_variants::MODULE_NAME_REPETITIONS, + eq_op::EQ_OP, + eq_op::OP_REF, + equatable_if_let::EQUATABLE_IF_LET, + erasing_op::ERASING_OP, + escape::BOXED_LOCAL, + eta_reduction::REDUNDANT_CLOSURE, + eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS, + eval_order_dependence::DIVERGING_SUB_EXPRESSION, + eval_order_dependence::EVAL_ORDER_DEPENDENCE, + excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, + excessive_bools::STRUCT_EXCESSIVE_BOOLS, + exhaustive_items::EXHAUSTIVE_ENUMS, + exhaustive_items::EXHAUSTIVE_STRUCTS, + exit::EXIT, + explicit_write::EXPLICIT_WRITE, + fallible_impl_from::FALLIBLE_IMPL_FROM, + feature_name::NEGATIVE_FEATURE_NAMES, + feature_name::REDUNDANT_FEATURE_NAMES, + float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS, + float_literal::EXCESSIVE_PRECISION, + float_literal::LOSSY_FLOAT_LITERAL, + floating_point_arithmetic::IMPRECISE_FLOPS, + floating_point_arithmetic::SUBOPTIMAL_FLOPS, + format::USELESS_FORMAT, + formatting::POSSIBLE_MISSING_COMMA, + formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, + formatting::SUSPICIOUS_ELSE_FORMATTING, + formatting::SUSPICIOUS_UNARY_OP_FORMATTING, + from_over_into::FROM_OVER_INTO, + from_str_radix_10::FROM_STR_RADIX_10, + functions::DOUBLE_MUST_USE, + functions::MUST_USE_CANDIDATE, + functions::MUST_USE_UNIT, + functions::NOT_UNSAFE_PTR_ARG_DEREF, + functions::RESULT_UNIT_ERR, + functions::TOO_MANY_ARGUMENTS, + functions::TOO_MANY_LINES, + future_not_send::FUTURE_NOT_SEND, + get_last_with_len::GET_LAST_WITH_LEN, + identity_op::IDENTITY_OP, + if_let_mutex::IF_LET_MUTEX, + if_not_else::IF_NOT_ELSE, + if_then_panic::IF_THEN_PANIC, + if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, + implicit_hasher::IMPLICIT_HASHER, + implicit_return::IMPLICIT_RETURN, + implicit_saturating_sub::IMPLICIT_SATURATING_SUB, + inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, + indexing_slicing::INDEXING_SLICING, + indexing_slicing::OUT_OF_BOUNDS_INDEXING, + infinite_iter::INFINITE_ITER, + infinite_iter::MAYBE_INFINITE_ITER, + inherent_impl::MULTIPLE_INHERENT_IMPL, + inherent_to_string::INHERENT_TO_STRING, + inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY, + inline_fn_without_body::INLINE_FN_WITHOUT_BODY, + int_plus_one::INT_PLUS_ONE, + integer_division::INTEGER_DIVISION, + invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS, + items_after_statements::ITEMS_AFTER_STATEMENTS, + iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR, + large_const_arrays::LARGE_CONST_ARRAYS, + large_enum_variant::LARGE_ENUM_VARIANT, + large_stack_arrays::LARGE_STACK_ARRAYS, + len_zero::COMPARISON_TO_EMPTY, + len_zero::LEN_WITHOUT_IS_EMPTY, + len_zero::LEN_ZERO, + let_if_seq::USELESS_LET_IF_SEQ, + let_underscore::LET_UNDERSCORE_DROP, + let_underscore::LET_UNDERSCORE_LOCK, + let_underscore::LET_UNDERSCORE_MUST_USE, + lifetimes::EXTRA_UNUSED_LIFETIMES, + lifetimes::NEEDLESS_LIFETIMES, + literal_representation::DECIMAL_LITERAL_REPRESENTATION, + literal_representation::INCONSISTENT_DIGIT_GROUPING, + literal_representation::LARGE_DIGIT_GROUPS, + literal_representation::MISTYPED_LITERAL_SUFFIXES, + literal_representation::UNREADABLE_LITERAL, + literal_representation::UNUSUAL_BYTE_GROUPINGS, + loops::EMPTY_LOOP, + loops::EXPLICIT_COUNTER_LOOP, + loops::EXPLICIT_INTO_ITER_LOOP, + loops::EXPLICIT_ITER_LOOP, + loops::FOR_KV_MAP, + loops::FOR_LOOPS_OVER_FALLIBLES, + loops::ITER_NEXT_LOOP, + loops::MANUAL_FLATTEN, + loops::MANUAL_MEMCPY, + loops::MUT_RANGE_BOUND, + loops::NEEDLESS_COLLECT, + loops::NEEDLESS_RANGE_LOOP, + loops::NEVER_LOOP, + loops::SAME_ITEM_PUSH, + loops::SINGLE_ELEMENT_LOOP, + loops::WHILE_IMMUTABLE_CONDITION, + loops::WHILE_LET_LOOP, + loops::WHILE_LET_ON_ITERATOR, + macro_use::MACRO_USE_IMPORTS, + main_recursion::MAIN_RECURSION, + manual_async_fn::MANUAL_ASYNC_FN, + manual_map::MANUAL_MAP, + manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, + manual_ok_or::MANUAL_OK_OR, + manual_strip::MANUAL_STRIP, + manual_unwrap_or::MANUAL_UNWRAP_OR, + map_clone::MAP_CLONE, + map_err_ignore::MAP_ERR_IGNORE, + map_unit_fn::OPTION_MAP_UNIT_FN, + map_unit_fn::RESULT_MAP_UNIT_FN, + match_on_vec_items::MATCH_ON_VEC_ITEMS, + match_result_ok::MATCH_RESULT_OK, + matches::INFALLIBLE_DESTRUCTURING_MATCH, + matches::MATCH_AS_REF, + matches::MATCH_BOOL, + matches::MATCH_LIKE_MATCHES_MACRO, + matches::MATCH_OVERLAPPING_ARM, + matches::MATCH_REF_PATS, + matches::MATCH_SAME_ARMS, + matches::MATCH_SINGLE_BINDING, + matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + matches::MATCH_WILD_ERR_ARM, + matches::REDUNDANT_PATTERN_MATCHING, + matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, + matches::SINGLE_MATCH, + matches::SINGLE_MATCH_ELSE, + matches::WILDCARD_ENUM_MATCH_ARM, + matches::WILDCARD_IN_OR_PATTERNS, + mem_discriminant::MEM_DISCRIMINANT_NON_ENUM, + mem_forget::MEM_FORGET, + mem_replace::MEM_REPLACE_OPTION_WITH_NONE, + mem_replace::MEM_REPLACE_WITH_DEFAULT, + mem_replace::MEM_REPLACE_WITH_UNINIT, + methods::BIND_INSTEAD_OF_MAP, + methods::BYTES_NTH, + methods::CHARS_LAST_CMP, + methods::CHARS_NEXT_CMP, + methods::CLONED_INSTEAD_OF_COPIED, + methods::CLONE_DOUBLE_REF, + methods::CLONE_ON_COPY, + methods::CLONE_ON_REF_PTR, + methods::EXPECT_FUN_CALL, + methods::EXPECT_USED, + methods::EXTEND_WITH_DRAIN, + methods::FILETYPE_IS_FILE, + methods::FILTER_MAP_IDENTITY, + methods::FILTER_MAP_NEXT, + methods::FILTER_NEXT, + methods::FLAT_MAP_IDENTITY, + methods::FLAT_MAP_OPTION, + methods::FROM_ITER_INSTEAD_OF_COLLECT, + methods::GET_UNWRAP, + methods::IMPLICIT_CLONE, + methods::INEFFICIENT_TO_STRING, + methods::INSPECT_FOR_EACH, + methods::INTO_ITER_ON_REF, + methods::ITERATOR_STEP_BY_ZERO, + methods::ITER_CLONED_COLLECT, + methods::ITER_COUNT, + methods::ITER_NEXT_SLICE, + methods::ITER_NTH, + methods::ITER_NTH_ZERO, + methods::ITER_SKIP_NEXT, + methods::MANUAL_FILTER_MAP, + methods::MANUAL_FIND_MAP, + methods::MANUAL_SATURATING_ARITHMETIC, + methods::MANUAL_SPLIT_ONCE, + methods::MANUAL_STR_REPEAT, + methods::MAP_COLLECT_RESULT_UNIT, + methods::MAP_FLATTEN, + methods::MAP_IDENTITY, + methods::MAP_UNWRAP_OR, + methods::NEW_RET_NO_SELF, + methods::OK_EXPECT, + methods::OPTION_AS_REF_DEREF, + methods::OPTION_FILTER_MAP, + methods::OPTION_MAP_OR_NONE, + methods::OR_FUN_CALL, + methods::RESULT_MAP_OR_INTO_OPTION, + methods::SEARCH_IS_SOME, + methods::SHOULD_IMPLEMENT_TRAIT, + methods::SINGLE_CHAR_ADD_STR, + methods::SINGLE_CHAR_PATTERN, + methods::SKIP_WHILE_NEXT, + methods::STRING_EXTEND_CHARS, + methods::SUSPICIOUS_MAP, + methods::SUSPICIOUS_SPLITN, + methods::UNINIT_ASSUMED_INIT, + methods::UNNECESSARY_FILTER_MAP, + methods::UNNECESSARY_FOLD, + methods::UNNECESSARY_LAZY_EVALUATIONS, + methods::UNWRAP_OR_ELSE_DEFAULT, + methods::UNWRAP_USED, + methods::USELESS_ASREF, + methods::WRONG_SELF_CONVENTION, + methods::ZST_OFFSET, + minmax::MIN_MAX, + misc::CMP_NAN, + misc::CMP_OWNED, + misc::FLOAT_CMP, + misc::FLOAT_CMP_CONST, + misc::MODULO_ONE, + misc::SHORT_CIRCUIT_STATEMENT, + misc::TOPLEVEL_REF_ARG, + misc::USED_UNDERSCORE_BINDING, + misc::ZERO_PTR, + misc_early::BUILTIN_TYPE_SHADOW, + misc_early::DOUBLE_NEG, + misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, + misc_early::MIXED_CASE_HEX_LITERALS, + misc_early::REDUNDANT_PATTERN, + misc_early::UNNEEDED_FIELD_PATTERN, + misc_early::UNNEEDED_WILDCARD_PATTERN, + misc_early::UNSEPARATED_LITERAL_SUFFIX, + misc_early::ZERO_PREFIXED_LITERAL, + missing_const_for_fn::MISSING_CONST_FOR_FN, + missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, + missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, + missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, + module_style::MOD_MODULE_FILES, + module_style::SELF_NAMED_MODULE_FILES, + modulo_arithmetic::MODULO_ARITHMETIC, + multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, + mut_key::MUTABLE_KEY_TYPE, + mut_mut::MUT_MUT, + mut_mutex_lock::MUT_MUTEX_LOCK, + mut_reference::UNNECESSARY_MUT_PASSED, + mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, + mutex_atomic::MUTEX_ATOMIC, + mutex_atomic::MUTEX_INTEGER, + needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE, + needless_bitwise_bool::NEEDLESS_BITWISE_BOOL, + needless_bool::BOOL_COMPARISON, + needless_bool::NEEDLESS_BOOL, + needless_borrow::NEEDLESS_BORROW, + needless_borrow::REF_BINDING_TO_REFERENCE, + needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, + needless_continue::NEEDLESS_CONTINUE, + needless_for_each::NEEDLESS_FOR_EACH, + 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, + neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, + neg_multiply::NEG_MULTIPLY, + new_without_default::NEW_WITHOUT_DEFAULT, + no_effect::NO_EFFECT, + no_effect::UNNECESSARY_OPERATION, + non_copy_const::BORROW_INTERIOR_MUTABLE_CONST, + non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST, + non_expressive_names::JUST_UNDERSCORES_AND_DIGITS, + non_expressive_names::MANY_SINGLE_CHAR_NAMES, + non_expressive_names::SIMILAR_NAMES, + non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, + non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY, + nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, + open_options::NONSENSICAL_OPEN_OPTIONS, + option_env_unwrap::OPTION_ENV_UNWRAP, + option_if_let_else::OPTION_IF_LET_ELSE, + overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, + panic_in_result_fn::PANIC_IN_RESULT_FN, + panic_unimplemented::PANIC, + panic_unimplemented::TODO, + panic_unimplemented::UNIMPLEMENTED, + panic_unimplemented::UNREACHABLE, + partialeq_ne_impl::PARTIALEQ_NE_IMPL, + pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, + pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF, + path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, + pattern_type_mismatch::PATTERN_TYPE_MISMATCH, + precedence::PRECEDENCE, + ptr::CMP_NULL, + ptr::INVALID_NULL_PTR_USAGE, + ptr::MUT_FROM_REF, + ptr::PTR_ARG, + ptr_eq::PTR_EQ, + ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, + question_mark::QUESTION_MARK, + ranges::MANUAL_RANGE_CONTAINS, + ranges::RANGE_MINUS_ONE, + ranges::RANGE_PLUS_ONE, + ranges::RANGE_ZIP_WITH_LEN, + ranges::REVERSED_EMPTY_RANGES, + redundant_clone::REDUNDANT_CLONE, + redundant_closure_call::REDUNDANT_CLOSURE_CALL, + redundant_else::REDUNDANT_ELSE, + redundant_field_names::REDUNDANT_FIELD_NAMES, + redundant_pub_crate::REDUNDANT_PUB_CRATE, + redundant_slicing::REDUNDANT_SLICING, + redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, + ref_option_ref::REF_OPTION_REF, + reference::DEREF_ADDROF, + reference::REF_IN_DEREF, + regex::INVALID_REGEX, + regex::TRIVIAL_REGEX, + repeat_once::REPEAT_ONCE, + returns::LET_AND_RETURN, + returns::NEEDLESS_RETURN, + same_name_method::SAME_NAME_METHOD, + self_assignment::SELF_ASSIGNMENT, + self_named_constructors::SELF_NAMED_CONSTRUCTORS, + semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED, + serde_api::SERDE_API_MISUSE, + shadow::SHADOW_REUSE, + shadow::SHADOW_SAME, + shadow::SHADOW_UNRELATED, + single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, + size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, + slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, + stable_sort_primitive::STABLE_SORT_PRIMITIVE, + strings::STRING_ADD, + strings::STRING_ADD_ASSIGN, + strings::STRING_FROM_UTF8_AS_BYTES, + strings::STRING_LIT_AS_BYTES, + strings::STRING_TO_STRING, + strings::STR_TO_STRING, + strlen_on_c_strings::STRLEN_ON_C_STRINGS, + suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, + suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, + suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, + swap::ALMOST_SWAPPED, + swap::MANUAL_SWAP, + tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, + temporary_assignment::TEMPORARY_ASSIGNMENT, + to_digit_is_some::TO_DIGIT_IS_SOME, + to_string_in_display::TO_STRING_IN_DISPLAY, + trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, + trait_bounds::TYPE_REPETITION_IN_BOUNDS, + transmute::CROSSPOINTER_TRANSMUTE, + transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, + transmute::TRANSMUTE_BYTES_TO_STR, + transmute::TRANSMUTE_FLOAT_TO_INT, + transmute::TRANSMUTE_INT_TO_BOOL, + transmute::TRANSMUTE_INT_TO_CHAR, + transmute::TRANSMUTE_INT_TO_FLOAT, + transmute::TRANSMUTE_PTR_TO_PTR, + transmute::TRANSMUTE_PTR_TO_REF, + transmute::UNSOUND_COLLECTION_TRANSMUTE, + transmute::USELESS_TRANSMUTE, + transmute::WRONG_TRANSMUTE, + transmuting_null::TRANSMUTING_NULL, + try_err::TRY_ERR, + types::BORROWED_BOX, + types::BOX_COLLECTION, + types::LINKEDLIST, + types::OPTION_OPTION, + types::RC_BUFFER, + types::RC_MUTEX, + types::REDUNDANT_ALLOCATION, + types::TYPE_COMPLEXITY, + types::VEC_BOX, + undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, + unicode::INVISIBLE_CHARACTERS, + unicode::NON_ASCII_LITERAL, + unicode::UNICODE_NOT_NFC, + unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, + unit_types::LET_UNIT_VALUE, + unit_types::UNIT_ARG, + unit_types::UNIT_CMP, + unnamed_address::FN_ADDRESS_COMPARISONS, + unnamed_address::VTABLE_ADDRESS_COMPARISONS, + unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, + unnecessary_sort_by::UNNECESSARY_SORT_BY, + unnecessary_wraps::UNNECESSARY_WRAPS, + unnested_or_patterns::UNNESTED_OR_PATTERNS, + unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, + unused_async::UNUSED_ASYNC, + unused_io_amount::UNUSED_IO_AMOUNT, + unused_self::UNUSED_SELF, + unused_unit::UNUSED_UNIT, + unwrap::PANICKING_UNWRAP, + unwrap::UNNECESSARY_UNWRAP, + unwrap_in_result::UNWRAP_IN_RESULT, + upper_case_acronyms::UPPER_CASE_ACRONYMS, + use_self::USE_SELF, + useless_conversion::USELESS_CONVERSION, + vec::USELESS_VEC, + vec_init_then_push::VEC_INIT_THEN_PUSH, + vec_resize_to_zero::VEC_RESIZE_TO_ZERO, + verbose_file_reads::VERBOSE_FILE_READS, + wildcard_dependencies::WILDCARD_DEPENDENCIES, + wildcard_imports::ENUM_GLOB_USE, + wildcard_imports::WILDCARD_IMPORTS, + write::PRINTLN_EMPTY_STRING, + write::PRINT_LITERAL, + write::PRINT_STDERR, + write::PRINT_STDOUT, + write::PRINT_WITH_NEWLINE, + write::USE_DEBUG, + write::WRITELN_EMPTY_STRING, + write::WRITE_LITERAL, + write::WRITE_WITH_NEWLINE, + zero_div_zero::ZERO_DIVIDED_BY_ZERO, + zero_sized_map_values::ZERO_SIZED_MAP_VALUES, +]) diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs new file mode 100644 index 0000000000000..96e0b421094d6 --- /dev/null +++ b/clippy_lints/src/lib.register_nursery.rs @@ -0,0 +1,30 @@ +// 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. + +store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ + LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), + LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), + LintId::of(copies::BRANCHES_SHARING_CODE), + LintId::of(disallowed_method::DISALLOWED_METHOD), + LintId::of(disallowed_type::DISALLOWED_TYPE), + LintId::of(equatable_if_let::EQUATABLE_IF_LET), + LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), + LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), + LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS), + LintId::of(future_not_send::FUTURE_NOT_SEND), + LintId::of(let_if_seq::USELESS_LET_IF_SEQ), + LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), + LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), + LintId::of(mutex_atomic::MUTEX_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(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), + LintId::of(regex::TRIVIAL_REGEX), + LintId::of(strings::STRING_LIT_AS_BYTES), + LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), + 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 new file mode 100644 index 0000000000000..6533b94e82bd5 --- /dev/null +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -0,0 +1,100 @@ +// 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. + +store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ + LintId::of(attrs::INLINE_ALWAYS), + LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), + LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), + LintId::of(bit_mask::VERBOSE_BIT_MASK), + LintId::of(bytecount::NAIVE_BYTECOUNT), + LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), + LintId::of(casts::CAST_LOSSLESS), + LintId::of(casts::CAST_POSSIBLE_TRUNCATION), + LintId::of(casts::CAST_POSSIBLE_WRAP), + LintId::of(casts::CAST_PRECISION_LOSS), + LintId::of(casts::CAST_PTR_ALIGNMENT), + LintId::of(casts::CAST_SIGN_LOSS), + LintId::of(casts::PTR_AS_PTR), + LintId::of(checked_conversions::CHECKED_CONVERSIONS), + LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION), + LintId::of(copy_iterator::COPY_ITERATOR), + LintId::of(default::DEFAULT_TRAIT_ACCESS), + LintId::of(dereference::EXPLICIT_DEREF_METHODS), + LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), + LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), + LintId::of(doc::DOC_MARKDOWN), + LintId::of(doc::MISSING_ERRORS_DOC), + LintId::of(doc::MISSING_PANICS_DOC), + LintId::of(empty_enum::EMPTY_ENUM), + LintId::of(enum_variants::MODULE_NAME_REPETITIONS), + LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), + LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), + LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), + LintId::of(functions::MUST_USE_CANDIDATE), + LintId::of(functions::TOO_MANY_LINES), + LintId::of(if_not_else::IF_NOT_ELSE), + LintId::of(implicit_hasher::IMPLICIT_HASHER), + LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), + LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), + LintId::of(infinite_iter::MAYBE_INFINITE_ITER), + LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), + LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), + LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR), + LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS), + LintId::of(let_underscore::LET_UNDERSCORE_DROP), + LintId::of(literal_representation::LARGE_DIGIT_GROUPS), + LintId::of(literal_representation::UNREADABLE_LITERAL), + LintId::of(loops::EXPLICIT_INTO_ITER_LOOP), + LintId::of(loops::EXPLICIT_ITER_LOOP), + LintId::of(macro_use::MACRO_USE_IMPORTS), + LintId::of(manual_ok_or::MANUAL_OK_OR), + LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS), + LintId::of(matches::MATCH_BOOL), + LintId::of(matches::MATCH_SAME_ARMS), + LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), + LintId::of(matches::MATCH_WILD_ERR_ARM), + LintId::of(matches::SINGLE_MATCH_ELSE), + LintId::of(methods::CLONED_INSTEAD_OF_COPIED), + LintId::of(methods::FILTER_MAP_NEXT), + LintId::of(methods::FLAT_MAP_OPTION), + LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), + LintId::of(methods::IMPLICIT_CLONE), + LintId::of(methods::INEFFICIENT_TO_STRING), + LintId::of(methods::MAP_FLATTEN), + LintId::of(methods::MAP_UNWRAP_OR), + LintId::of(misc::FLOAT_CMP), + LintId::of(misc::USED_UNDERSCORE_BINDING), + LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX), + LintId::of(mut_mut::MUT_MUT), + LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL), + LintId::of(needless_borrow::REF_BINDING_TO_REFERENCE), + LintId::of(needless_continue::NEEDLESS_CONTINUE), + LintId::of(needless_for_each::NEEDLESS_FOR_EACH), + LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), + LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), + LintId::of(non_expressive_names::SIMILAR_NAMES), + LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), + LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), + LintId::of(ranges::RANGE_MINUS_ONE), + LintId::of(ranges::RANGE_PLUS_ONE), + LintId::of(redundant_else::REDUNDANT_ELSE), + LintId::of(ref_option_ref::REF_OPTION_REF), + LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), + 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), + LintId::of(unicode::NON_ASCII_LITERAL), + 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), + LintId::of(unused_self::UNUSED_SELF), + LintId::of(wildcard_imports::ENUM_GLOB_USE), + LintId::of(wildcard_imports::WILDCARD_IMPORTS), + LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES), +]) diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs new file mode 100644 index 0000000000000..5432345760bc3 --- /dev/null +++ b/clippy_lints/src/lib.register_perf.rs @@ -0,0 +1,27 @@ +// 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. + +store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ + LintId::of(entry::MAP_ENTRY), + LintId::of(escape::BOXED_LOCAL), + LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), + LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), + LintId::of(loops::MANUAL_MEMCPY), + LintId::of(loops::NEEDLESS_COLLECT), + LintId::of(methods::EXPECT_FUN_CALL), + LintId::of(methods::EXTEND_WITH_DRAIN), + LintId::of(methods::ITER_NTH), + LintId::of(methods::MANUAL_STR_REPEAT), + LintId::of(methods::OR_FUN_CALL), + LintId::of(methods::SINGLE_CHAR_PATTERN), + LintId::of(misc::CMP_OWNED), + LintId::of(mutex_atomic::MUTEX_ATOMIC), + LintId::of(redundant_clone::REDUNDANT_CLONE), + LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), + LintId::of(types::BOX_COLLECTION), + LintId::of(types::REDUNDANT_ALLOCATION), + LintId::of(vec::USELESS_VEC), + LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), +]) diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs new file mode 100644 index 0000000000000..4463dea5fcb84 --- /dev/null +++ b/clippy_lints/src/lib.register_restriction.rs @@ -0,0 +1,65 @@ +// 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. + +store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ + LintId::of(arithmetic::FLOAT_ARITHMETIC), + LintId::of(arithmetic::INTEGER_ARITHMETIC), + LintId::of(as_conversions::AS_CONVERSIONS), + LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), + LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), + LintId::of(create_dir::CREATE_DIR), + LintId::of(dbg_macro::DBG_MACRO), + LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), + LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS), + LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), + LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), + LintId::of(exit::EXIT), + LintId::of(float_literal::LOSSY_FLOAT_LITERAL), + LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE), + LintId::of(implicit_return::IMPLICIT_RETURN), + LintId::of(indexing_slicing::INDEXING_SLICING), + LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), + LintId::of(integer_division::INTEGER_DIVISION), + LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), + LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), + LintId::of(map_err_ignore::MAP_ERR_IGNORE), + LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), + LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), + LintId::of(mem_forget::MEM_FORGET), + LintId::of(methods::CLONE_ON_REF_PTR), + LintId::of(methods::EXPECT_USED), + LintId::of(methods::FILETYPE_IS_FILE), + LintId::of(methods::GET_UNWRAP), + LintId::of(methods::UNWRAP_USED), + LintId::of(misc::FLOAT_CMP_CONST), + LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), + LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), + LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES), + LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), + LintId::of(module_style::MOD_MODULE_FILES), + LintId::of(module_style::SELF_NAMED_MODULE_FILES), + LintId::of(modulo_arithmetic::MODULO_ARITHMETIC), + LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), + LintId::of(panic_unimplemented::PANIC), + LintId::of(panic_unimplemented::TODO), + LintId::of(panic_unimplemented::UNIMPLEMENTED), + LintId::of(panic_unimplemented::UNREACHABLE), + LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), + LintId::of(same_name_method::SAME_NAME_METHOD), + LintId::of(shadow::SHADOW_REUSE), + LintId::of(shadow::SHADOW_SAME), + LintId::of(shadow::SHADOW_UNRELATED), + LintId::of(strings::STRING_ADD), + LintId::of(strings::STRING_TO_STRING), + LintId::of(strings::STR_TO_STRING), + LintId::of(types::RC_BUFFER), + LintId::of(types::RC_MUTEX), + LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), + LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), + LintId::of(verbose_file_reads::VERBOSE_FILE_READS), + LintId::of(write::PRINT_STDERR), + LintId::of(write::PRINT_STDOUT), + LintId::of(write::USE_DEBUG), +]) diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs new file mode 100644 index 0000000000000..a39c111c57423 --- /dev/null +++ b/clippy_lints/src/lib.register_style.rs @@ -0,0 +1,114 @@ +// 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. + +store.register_group(true, "clippy::style", Some("clippy_style"), vec![ + LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), + LintId::of(assign_ops::ASSIGN_OP_PATTERN), + LintId::of(blacklisted_name::BLACKLISTED_NAME), + LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), + LintId::of(casts::FN_TO_NUMERIC_CAST), + LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), + LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), + LintId::of(collapsible_if::COLLAPSIBLE_IF), + LintId::of(collapsible_match::COLLAPSIBLE_MATCH), + LintId::of(comparison_chain::COMPARISON_CHAIN), + LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), + LintId::of(doc::MISSING_SAFETY_DOC), + LintId::of(doc::NEEDLESS_DOCTEST_MAIN), + LintId::of(enum_variants::ENUM_VARIANT_NAMES), + LintId::of(enum_variants::MODULE_INCEPTION), + LintId::of(eq_op::OP_REF), + LintId::of(eta_reduction::REDUNDANT_CLOSURE), + LintId::of(float_literal::EXCESSIVE_PRECISION), + LintId::of(from_over_into::FROM_OVER_INTO), + LintId::of(from_str_radix_10::FROM_STR_RADIX_10), + LintId::of(functions::DOUBLE_MUST_USE), + LintId::of(functions::MUST_USE_UNIT), + LintId::of(functions::RESULT_UNIT_ERR), + LintId::of(if_then_panic::IF_THEN_PANIC), + LintId::of(inherent_to_string::INHERENT_TO_STRING), + LintId::of(len_zero::COMPARISON_TO_EMPTY), + LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), + LintId::of(len_zero::LEN_ZERO), + LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), + LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), + LintId::of(loops::FOR_KV_MAP), + LintId::of(loops::NEEDLESS_RANGE_LOOP), + LintId::of(loops::SAME_ITEM_PUSH), + LintId::of(loops::WHILE_LET_ON_ITERATOR), + LintId::of(main_recursion::MAIN_RECURSION), + LintId::of(manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(manual_map::MANUAL_MAP), + LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), + LintId::of(map_clone::MAP_CLONE), + LintId::of(match_result_ok::MATCH_RESULT_OK), + LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), + LintId::of(matches::MATCH_OVERLAPPING_ARM), + LintId::of(matches::MATCH_REF_PATS), + LintId::of(matches::REDUNDANT_PATTERN_MATCHING), + LintId::of(matches::SINGLE_MATCH), + LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), + LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), + LintId::of(methods::BYTES_NTH), + LintId::of(methods::CHARS_LAST_CMP), + LintId::of(methods::CHARS_NEXT_CMP), + LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::ITER_CLONED_COLLECT), + LintId::of(methods::ITER_NEXT_SLICE), + LintId::of(methods::ITER_NTH_ZERO), + LintId::of(methods::ITER_SKIP_NEXT), + LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(methods::MAP_COLLECT_RESULT_UNIT), + LintId::of(methods::NEW_RET_NO_SELF), + LintId::of(methods::OK_EXPECT), + LintId::of(methods::OPTION_MAP_OR_NONE), + LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), + LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(methods::SINGLE_CHAR_ADD_STR), + LintId::of(methods::STRING_EXTEND_CHARS), + LintId::of(methods::UNNECESSARY_FOLD), + LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), + LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), + LintId::of(methods::WRONG_SELF_CONVENTION), + LintId::of(misc::TOPLEVEL_REF_ARG), + LintId::of(misc::ZERO_PTR), + LintId::of(misc_early::BUILTIN_TYPE_SHADOW), + LintId::of(misc_early::DOUBLE_NEG), + LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), + LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), + LintId::of(misc_early::REDUNDANT_PATTERN), + LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), + LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), + LintId::of(needless_borrow::NEEDLESS_BORROW), + LintId::of(neg_multiply::NEG_MULTIPLY), + LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), + LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), + LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), + LintId::of(ptr::CMP_NULL), + LintId::of(ptr::PTR_ARG), + LintId::of(ptr_eq::PTR_EQ), + LintId::of(question_mark::QUESTION_MARK), + LintId::of(ranges::MANUAL_RANGE_CONTAINS), + LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), + LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(returns::LET_AND_RETURN), + 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(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), + LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(try_err::TRY_ERR), + LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(unused_unit::UNUSED_UNIT), + LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), + LintId::of(write::PRINTLN_EMPTY_STRING), + LintId::of(write::PRINT_LITERAL), + LintId::of(write::PRINT_WITH_NEWLINE), + LintId::of(write::WRITELN_EMPTY_STRING), + LintId::of(write::WRITE_LITERAL), + LintId::of(write::WRITE_WITH_NEWLINE), +]) diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs new file mode 100644 index 0000000000000..8859787fbc830 --- /dev/null +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -0,0 +1,20 @@ +// 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. + +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(eval_order_dependence::EVAL_ORDER_DEPENDENCE), + LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), + LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), + LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), + LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(loops::EMPTY_LOOP), + LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), + LintId::of(loops::MUT_RANGE_BOUND), + LintId::of(methods::SUSPICIOUS_MAP), + LintId::of(mut_key::MUTABLE_KEY_TYPE), + LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), + LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), +]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 897249174822c..9fc6a9e0ccca0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -203,6 +203,7 @@ mod entry; mod enum_clike; mod enum_variants; mod eq_op; +mod equatable_if_let; mod erasing_op; mod escape; mod eta_reduction; @@ -303,6 +304,7 @@ mod no_effect; mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; +mod non_send_fields_in_send_ty; mod nonstandard_macro_braces; mod open_options; mod option_env_unwrap; @@ -438,1401 +440,23 @@ pub fn read_conf(sess: &Session) -> Conf { pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { register_removed_non_tool_lints(store); - // begin deprecated lints, do not remove this comment, it’s used in `update_lints` - store.register_removed( - "clippy::should_assert_eq", - "`assert!()` will be more flexible with RFC 2011", - ); - store.register_removed( - "clippy::extend_from_slice", - "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice", - ); - store.register_removed( - "clippy::range_step_by_zero", - "`iterator.step_by(0)` panics nowadays", - ); - store.register_removed( - "clippy::unstable_as_slice", - "`Vec::as_slice` has been stabilized in 1.7", - ); - store.register_removed( - "clippy::unstable_as_mut_slice", - "`Vec::as_mut_slice` has been stabilized in 1.7", - ); - store.register_removed( - "clippy::misaligned_transmute", - "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", - ); - store.register_removed( - "clippy::assign_ops", - "using compound assignment operators (e.g., `+=`) is harmless", - ); - store.register_removed( - "clippy::if_let_redundant_pattern_matching", - "this lint has been changed to redundant_pattern_matching", - ); - store.register_removed( - "clippy::unsafe_vector_initialization", - "the replacement suggested by this lint had substantially different behavior", - ); - store.register_removed( - "clippy::unused_collect", - "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint", - ); - store.register_removed( - "clippy::replace_consts", - "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants", - ); - store.register_removed( - "clippy::regex_macro", - "the regex! macro has been removed from the regex crate in 2018", - ); - store.register_removed( - "clippy::find_map", - "this lint has been replaced by `manual_find_map`, a more specific lint", - ); - store.register_removed( - "clippy::filter_map", - "this lint has been replaced by `manual_filter_map`, a more specific lint", - ); - store.register_removed( - "clippy::pub_enum_variant_names", - "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items", - ); - store.register_removed( - "clippy::wrong_pub_self_convention", - "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items", - ); - // end deprecated lints, do not remove this comment, it’s used in `update_lints` + include!("lib.deprecated.rs"); - // begin register lints, do not remove this comment, it’s used in `update_lints` - store.register_lints(&[ - #[cfg(feature = "internal-lints")] - utils::internal_lints::CLIPPY_LINTS_INTERNAL, - #[cfg(feature = "internal-lints")] - utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, - #[cfg(feature = "internal-lints")] - utils::internal_lints::COMPILER_LINT_FUNCTIONS, - #[cfg(feature = "internal-lints")] - utils::internal_lints::DEFAULT_LINT, - #[cfg(feature = "internal-lints")] - utils::internal_lints::IF_CHAIN_STYLE, - #[cfg(feature = "internal-lints")] - utils::internal_lints::INTERNING_DEFINED_SYMBOL, - #[cfg(feature = "internal-lints")] - utils::internal_lints::INVALID_PATHS, - #[cfg(feature = "internal-lints")] - utils::internal_lints::LINT_WITHOUT_LINT_PASS, - #[cfg(feature = "internal-lints")] - utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, - #[cfg(feature = "internal-lints")] - utils::internal_lints::OUTER_EXPN_EXPN_DATA, - #[cfg(feature = "internal-lints")] - utils::internal_lints::PRODUCE_ICE, - #[cfg(feature = "internal-lints")] - utils::internal_lints::UNNECESSARY_SYMBOL_STR, - absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, - approx_const::APPROX_CONSTANT, - arithmetic::FLOAT_ARITHMETIC, - arithmetic::INTEGER_ARITHMETIC, - as_conversions::AS_CONVERSIONS, - asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, - asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, - assertions_on_constants::ASSERTIONS_ON_CONSTANTS, - assign_ops::ASSIGN_OP_PATTERN, - assign_ops::MISREFACTORED_ASSIGN_OP, - async_yields_async::ASYNC_YIELDS_ASYNC, - attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, - attrs::DEPRECATED_CFG_ATTR, - attrs::DEPRECATED_SEMVER, - attrs::EMPTY_LINE_AFTER_OUTER_ATTR, - attrs::INLINE_ALWAYS, - attrs::MISMATCHED_TARGET_OS, - attrs::USELESS_ATTRIBUTE, - await_holding_invalid::AWAIT_HOLDING_LOCK, - await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, - bit_mask::BAD_BIT_MASK, - bit_mask::INEFFECTIVE_BIT_MASK, - bit_mask::VERBOSE_BIT_MASK, - blacklisted_name::BLACKLISTED_NAME, - blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, - bool_assert_comparison::BOOL_ASSERT_COMPARISON, - booleans::LOGIC_BUG, - booleans::NONMINIMAL_BOOL, - bytecount::NAIVE_BYTECOUNT, - cargo_common_metadata::CARGO_COMMON_METADATA, - case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - casts::CAST_LOSSLESS, - casts::CAST_POSSIBLE_TRUNCATION, - casts::CAST_POSSIBLE_WRAP, - casts::CAST_PRECISION_LOSS, - casts::CAST_PTR_ALIGNMENT, - casts::CAST_REF_TO_MUT, - casts::CAST_SIGN_LOSS, - casts::CHAR_LIT_AS_U8, - casts::FN_TO_NUMERIC_CAST, - casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, - casts::PTR_AS_PTR, - casts::UNNECESSARY_CAST, - checked_conversions::CHECKED_CONVERSIONS, - cognitive_complexity::COGNITIVE_COMPLEXITY, - collapsible_if::COLLAPSIBLE_ELSE_IF, - collapsible_if::COLLAPSIBLE_IF, - collapsible_match::COLLAPSIBLE_MATCH, - comparison_chain::COMPARISON_CHAIN, - copies::BRANCHES_SHARING_CODE, - copies::IFS_SAME_COND, - copies::IF_SAME_THEN_ELSE, - copies::SAME_FUNCTIONS_IN_IF_CONDITION, - copy_iterator::COPY_ITERATOR, - create_dir::CREATE_DIR, - dbg_macro::DBG_MACRO, - default::DEFAULT_TRAIT_ACCESS, - default::FIELD_REASSIGN_WITH_DEFAULT, - default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK, - dereference::EXPLICIT_DEREF_METHODS, - derivable_impls::DERIVABLE_IMPLS, - derive::DERIVE_HASH_XOR_EQ, - derive::DERIVE_ORD_XOR_PARTIAL_ORD, - derive::EXPL_IMPL_CLONE_ON_COPY, - derive::UNSAFE_DERIVE_DESERIALIZE, - disallowed_method::DISALLOWED_METHOD, - disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS, - disallowed_type::DISALLOWED_TYPE, - doc::DOC_MARKDOWN, - doc::MISSING_ERRORS_DOC, - doc::MISSING_PANICS_DOC, - doc::MISSING_SAFETY_DOC, - doc::NEEDLESS_DOCTEST_MAIN, - double_comparison::DOUBLE_COMPARISONS, - double_parens::DOUBLE_PARENS, - drop_forget_ref::DROP_COPY, - drop_forget_ref::DROP_REF, - drop_forget_ref::FORGET_COPY, - drop_forget_ref::FORGET_REF, - duration_subsec::DURATION_SUBSEC, - else_if_without_else::ELSE_IF_WITHOUT_ELSE, - empty_enum::EMPTY_ENUM, - entry::MAP_ENTRY, - enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, - enum_variants::ENUM_VARIANT_NAMES, - enum_variants::MODULE_INCEPTION, - enum_variants::MODULE_NAME_REPETITIONS, - eq_op::EQ_OP, - eq_op::OP_REF, - erasing_op::ERASING_OP, - escape::BOXED_LOCAL, - eta_reduction::REDUNDANT_CLOSURE, - eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS, - eval_order_dependence::DIVERGING_SUB_EXPRESSION, - eval_order_dependence::EVAL_ORDER_DEPENDENCE, - excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, - excessive_bools::STRUCT_EXCESSIVE_BOOLS, - exhaustive_items::EXHAUSTIVE_ENUMS, - exhaustive_items::EXHAUSTIVE_STRUCTS, - exit::EXIT, - explicit_write::EXPLICIT_WRITE, - fallible_impl_from::FALLIBLE_IMPL_FROM, - feature_name::NEGATIVE_FEATURE_NAMES, - feature_name::REDUNDANT_FEATURE_NAMES, - float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS, - float_literal::EXCESSIVE_PRECISION, - float_literal::LOSSY_FLOAT_LITERAL, - floating_point_arithmetic::IMPRECISE_FLOPS, - floating_point_arithmetic::SUBOPTIMAL_FLOPS, - format::USELESS_FORMAT, - formatting::POSSIBLE_MISSING_COMMA, - formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, - formatting::SUSPICIOUS_ELSE_FORMATTING, - formatting::SUSPICIOUS_UNARY_OP_FORMATTING, - from_over_into::FROM_OVER_INTO, - from_str_radix_10::FROM_STR_RADIX_10, - functions::DOUBLE_MUST_USE, - functions::MUST_USE_CANDIDATE, - functions::MUST_USE_UNIT, - functions::NOT_UNSAFE_PTR_ARG_DEREF, - functions::RESULT_UNIT_ERR, - functions::TOO_MANY_ARGUMENTS, - functions::TOO_MANY_LINES, - future_not_send::FUTURE_NOT_SEND, - get_last_with_len::GET_LAST_WITH_LEN, - identity_op::IDENTITY_OP, - if_let_mutex::IF_LET_MUTEX, - if_not_else::IF_NOT_ELSE, - if_then_panic::IF_THEN_PANIC, - if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, - implicit_hasher::IMPLICIT_HASHER, - implicit_return::IMPLICIT_RETURN, - implicit_saturating_sub::IMPLICIT_SATURATING_SUB, - inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, - indexing_slicing::INDEXING_SLICING, - indexing_slicing::OUT_OF_BOUNDS_INDEXING, - infinite_iter::INFINITE_ITER, - infinite_iter::MAYBE_INFINITE_ITER, - inherent_impl::MULTIPLE_INHERENT_IMPL, - inherent_to_string::INHERENT_TO_STRING, - inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY, - inline_fn_without_body::INLINE_FN_WITHOUT_BODY, - int_plus_one::INT_PLUS_ONE, - integer_division::INTEGER_DIVISION, - invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS, - items_after_statements::ITEMS_AFTER_STATEMENTS, - iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR, - large_const_arrays::LARGE_CONST_ARRAYS, - large_enum_variant::LARGE_ENUM_VARIANT, - large_stack_arrays::LARGE_STACK_ARRAYS, - len_zero::COMPARISON_TO_EMPTY, - len_zero::LEN_WITHOUT_IS_EMPTY, - len_zero::LEN_ZERO, - let_if_seq::USELESS_LET_IF_SEQ, - let_underscore::LET_UNDERSCORE_DROP, - let_underscore::LET_UNDERSCORE_LOCK, - let_underscore::LET_UNDERSCORE_MUST_USE, - lifetimes::EXTRA_UNUSED_LIFETIMES, - lifetimes::NEEDLESS_LIFETIMES, - literal_representation::DECIMAL_LITERAL_REPRESENTATION, - literal_representation::INCONSISTENT_DIGIT_GROUPING, - literal_representation::LARGE_DIGIT_GROUPS, - literal_representation::MISTYPED_LITERAL_SUFFIXES, - literal_representation::UNREADABLE_LITERAL, - literal_representation::UNUSUAL_BYTE_GROUPINGS, - loops::EMPTY_LOOP, - loops::EXPLICIT_COUNTER_LOOP, - loops::EXPLICIT_INTO_ITER_LOOP, - loops::EXPLICIT_ITER_LOOP, - loops::FOR_KV_MAP, - loops::FOR_LOOPS_OVER_FALLIBLES, - loops::ITER_NEXT_LOOP, - loops::MANUAL_FLATTEN, - loops::MANUAL_MEMCPY, - loops::MUT_RANGE_BOUND, - loops::NEEDLESS_COLLECT, - loops::NEEDLESS_RANGE_LOOP, - loops::NEVER_LOOP, - loops::SAME_ITEM_PUSH, - loops::SINGLE_ELEMENT_LOOP, - loops::WHILE_IMMUTABLE_CONDITION, - loops::WHILE_LET_LOOP, - loops::WHILE_LET_ON_ITERATOR, - macro_use::MACRO_USE_IMPORTS, - main_recursion::MAIN_RECURSION, - manual_async_fn::MANUAL_ASYNC_FN, - manual_map::MANUAL_MAP, - manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, - manual_ok_or::MANUAL_OK_OR, - manual_strip::MANUAL_STRIP, - manual_unwrap_or::MANUAL_UNWRAP_OR, - map_clone::MAP_CLONE, - map_err_ignore::MAP_ERR_IGNORE, - map_unit_fn::OPTION_MAP_UNIT_FN, - map_unit_fn::RESULT_MAP_UNIT_FN, - match_on_vec_items::MATCH_ON_VEC_ITEMS, - match_result_ok::MATCH_RESULT_OK, - matches::INFALLIBLE_DESTRUCTURING_MATCH, - matches::MATCH_AS_REF, - matches::MATCH_BOOL, - matches::MATCH_LIKE_MATCHES_MACRO, - matches::MATCH_OVERLAPPING_ARM, - matches::MATCH_REF_PATS, - matches::MATCH_SAME_ARMS, - matches::MATCH_SINGLE_BINDING, - matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, - matches::MATCH_WILD_ERR_ARM, - matches::REDUNDANT_PATTERN_MATCHING, - matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, - matches::SINGLE_MATCH, - matches::SINGLE_MATCH_ELSE, - matches::WILDCARD_ENUM_MATCH_ARM, - matches::WILDCARD_IN_OR_PATTERNS, - mem_discriminant::MEM_DISCRIMINANT_NON_ENUM, - mem_forget::MEM_FORGET, - mem_replace::MEM_REPLACE_OPTION_WITH_NONE, - mem_replace::MEM_REPLACE_WITH_DEFAULT, - mem_replace::MEM_REPLACE_WITH_UNINIT, - methods::BIND_INSTEAD_OF_MAP, - methods::BYTES_NTH, - methods::CHARS_LAST_CMP, - methods::CHARS_NEXT_CMP, - methods::CLONED_INSTEAD_OF_COPIED, - methods::CLONE_DOUBLE_REF, - methods::CLONE_ON_COPY, - methods::CLONE_ON_REF_PTR, - methods::EXPECT_FUN_CALL, - methods::EXPECT_USED, - methods::EXTEND_WITH_DRAIN, - methods::FILETYPE_IS_FILE, - methods::FILTER_MAP_IDENTITY, - methods::FILTER_MAP_NEXT, - methods::FILTER_NEXT, - methods::FLAT_MAP_IDENTITY, - methods::FLAT_MAP_OPTION, - methods::FROM_ITER_INSTEAD_OF_COLLECT, - methods::GET_UNWRAP, - methods::IMPLICIT_CLONE, - methods::INEFFICIENT_TO_STRING, - methods::INSPECT_FOR_EACH, - methods::INTO_ITER_ON_REF, - methods::ITERATOR_STEP_BY_ZERO, - methods::ITER_CLONED_COLLECT, - methods::ITER_COUNT, - methods::ITER_NEXT_SLICE, - methods::ITER_NTH, - methods::ITER_NTH_ZERO, - methods::ITER_SKIP_NEXT, - methods::MANUAL_FILTER_MAP, - methods::MANUAL_FIND_MAP, - methods::MANUAL_SATURATING_ARITHMETIC, - methods::MANUAL_SPLIT_ONCE, - methods::MANUAL_STR_REPEAT, - methods::MAP_COLLECT_RESULT_UNIT, - methods::MAP_FLATTEN, - methods::MAP_IDENTITY, - methods::MAP_UNWRAP_OR, - methods::NEW_RET_NO_SELF, - methods::OK_EXPECT, - methods::OPTION_AS_REF_DEREF, - methods::OPTION_FILTER_MAP, - methods::OPTION_MAP_OR_NONE, - methods::OR_FUN_CALL, - methods::RESULT_MAP_OR_INTO_OPTION, - methods::SEARCH_IS_SOME, - methods::SHOULD_IMPLEMENT_TRAIT, - methods::SINGLE_CHAR_ADD_STR, - methods::SINGLE_CHAR_PATTERN, - methods::SKIP_WHILE_NEXT, - methods::STRING_EXTEND_CHARS, - methods::SUSPICIOUS_MAP, - methods::SUSPICIOUS_SPLITN, - methods::UNINIT_ASSUMED_INIT, - methods::UNNECESSARY_FILTER_MAP, - methods::UNNECESSARY_FOLD, - methods::UNNECESSARY_LAZY_EVALUATIONS, - methods::UNWRAP_OR_ELSE_DEFAULT, - methods::UNWRAP_USED, - methods::USELESS_ASREF, - methods::WRONG_SELF_CONVENTION, - methods::ZST_OFFSET, - minmax::MIN_MAX, - misc::CMP_NAN, - misc::CMP_OWNED, - misc::FLOAT_CMP, - misc::FLOAT_CMP_CONST, - misc::MODULO_ONE, - misc::SHORT_CIRCUIT_STATEMENT, - misc::TOPLEVEL_REF_ARG, - misc::USED_UNDERSCORE_BINDING, - misc::ZERO_PTR, - misc_early::BUILTIN_TYPE_SHADOW, - misc_early::DOUBLE_NEG, - misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, - misc_early::MIXED_CASE_HEX_LITERALS, - misc_early::REDUNDANT_PATTERN, - misc_early::UNNEEDED_FIELD_PATTERN, - misc_early::UNNEEDED_WILDCARD_PATTERN, - misc_early::UNSEPARATED_LITERAL_SUFFIX, - misc_early::ZERO_PREFIXED_LITERAL, - missing_const_for_fn::MISSING_CONST_FOR_FN, - missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, - missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, - missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, - module_style::MOD_MODULE_FILES, - module_style::SELF_NAMED_MODULE_FILES, - modulo_arithmetic::MODULO_ARITHMETIC, - multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, - mut_key::MUTABLE_KEY_TYPE, - mut_mut::MUT_MUT, - mut_mutex_lock::MUT_MUTEX_LOCK, - mut_reference::UNNECESSARY_MUT_PASSED, - mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, - mutex_atomic::MUTEX_ATOMIC, - mutex_atomic::MUTEX_INTEGER, - needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE, - needless_bitwise_bool::NEEDLESS_BITWISE_BOOL, - needless_bool::BOOL_COMPARISON, - needless_bool::NEEDLESS_BOOL, - needless_borrow::NEEDLESS_BORROW, - needless_borrow::REF_BINDING_TO_REFERENCE, - needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, - needless_continue::NEEDLESS_CONTINUE, - needless_for_each::NEEDLESS_FOR_EACH, - 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, - neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, - neg_multiply::NEG_MULTIPLY, - new_without_default::NEW_WITHOUT_DEFAULT, - no_effect::NO_EFFECT, - no_effect::UNNECESSARY_OPERATION, - non_copy_const::BORROW_INTERIOR_MUTABLE_CONST, - non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST, - non_expressive_names::JUST_UNDERSCORES_AND_DIGITS, - non_expressive_names::MANY_SINGLE_CHAR_NAMES, - non_expressive_names::SIMILAR_NAMES, - non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, - nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, - open_options::NONSENSICAL_OPEN_OPTIONS, - option_env_unwrap::OPTION_ENV_UNWRAP, - option_if_let_else::OPTION_IF_LET_ELSE, - overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, - panic_in_result_fn::PANIC_IN_RESULT_FN, - panic_unimplemented::PANIC, - panic_unimplemented::TODO, - panic_unimplemented::UNIMPLEMENTED, - panic_unimplemented::UNREACHABLE, - partialeq_ne_impl::PARTIALEQ_NE_IMPL, - pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, - pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF, - path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, - pattern_type_mismatch::PATTERN_TYPE_MISMATCH, - precedence::PRECEDENCE, - ptr::CMP_NULL, - ptr::INVALID_NULL_PTR_USAGE, - ptr::MUT_FROM_REF, - ptr::PTR_ARG, - ptr_eq::PTR_EQ, - ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, - question_mark::QUESTION_MARK, - ranges::MANUAL_RANGE_CONTAINS, - ranges::RANGE_MINUS_ONE, - ranges::RANGE_PLUS_ONE, - ranges::RANGE_ZIP_WITH_LEN, - ranges::REVERSED_EMPTY_RANGES, - redundant_clone::REDUNDANT_CLONE, - redundant_closure_call::REDUNDANT_CLOSURE_CALL, - redundant_else::REDUNDANT_ELSE, - redundant_field_names::REDUNDANT_FIELD_NAMES, - redundant_pub_crate::REDUNDANT_PUB_CRATE, - redundant_slicing::REDUNDANT_SLICING, - redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, - ref_option_ref::REF_OPTION_REF, - reference::DEREF_ADDROF, - reference::REF_IN_DEREF, - regex::INVALID_REGEX, - regex::TRIVIAL_REGEX, - repeat_once::REPEAT_ONCE, - returns::LET_AND_RETURN, - returns::NEEDLESS_RETURN, - same_name_method::SAME_NAME_METHOD, - self_assignment::SELF_ASSIGNMENT, - self_named_constructors::SELF_NAMED_CONSTRUCTORS, - semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED, - serde_api::SERDE_API_MISUSE, - shadow::SHADOW_REUSE, - shadow::SHADOW_SAME, - shadow::SHADOW_UNRELATED, - single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, - size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, - slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - stable_sort_primitive::STABLE_SORT_PRIMITIVE, - strings::STRING_ADD, - strings::STRING_ADD_ASSIGN, - strings::STRING_FROM_UTF8_AS_BYTES, - strings::STRING_LIT_AS_BYTES, - strings::STRING_TO_STRING, - strings::STR_TO_STRING, - strlen_on_c_strings::STRLEN_ON_C_STRINGS, - suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, - suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, - suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, - swap::ALMOST_SWAPPED, - swap::MANUAL_SWAP, - tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, - temporary_assignment::TEMPORARY_ASSIGNMENT, - to_digit_is_some::TO_DIGIT_IS_SOME, - to_string_in_display::TO_STRING_IN_DISPLAY, - trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, - trait_bounds::TYPE_REPETITION_IN_BOUNDS, - transmute::CROSSPOINTER_TRANSMUTE, - transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, - transmute::TRANSMUTE_BYTES_TO_STR, - transmute::TRANSMUTE_FLOAT_TO_INT, - transmute::TRANSMUTE_INT_TO_BOOL, - transmute::TRANSMUTE_INT_TO_CHAR, - transmute::TRANSMUTE_INT_TO_FLOAT, - transmute::TRANSMUTE_PTR_TO_PTR, - transmute::TRANSMUTE_PTR_TO_REF, - transmute::UNSOUND_COLLECTION_TRANSMUTE, - transmute::USELESS_TRANSMUTE, - transmute::WRONG_TRANSMUTE, - transmuting_null::TRANSMUTING_NULL, - try_err::TRY_ERR, - types::BORROWED_BOX, - types::BOX_COLLECTION, - types::LINKEDLIST, - types::OPTION_OPTION, - types::RC_BUFFER, - types::RC_MUTEX, - types::REDUNDANT_ALLOCATION, - types::TYPE_COMPLEXITY, - types::VEC_BOX, - undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, - unicode::INVISIBLE_CHARACTERS, - unicode::NON_ASCII_LITERAL, - unicode::UNICODE_NOT_NFC, - unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, - unit_types::LET_UNIT_VALUE, - unit_types::UNIT_ARG, - unit_types::UNIT_CMP, - unnamed_address::FN_ADDRESS_COMPARISONS, - unnamed_address::VTABLE_ADDRESS_COMPARISONS, - unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, - unnecessary_sort_by::UNNECESSARY_SORT_BY, - unnecessary_wraps::UNNECESSARY_WRAPS, - unnested_or_patterns::UNNESTED_OR_PATTERNS, - unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, - unused_async::UNUSED_ASYNC, - unused_io_amount::UNUSED_IO_AMOUNT, - unused_self::UNUSED_SELF, - unused_unit::UNUSED_UNIT, - unwrap::PANICKING_UNWRAP, - unwrap::UNNECESSARY_UNWRAP, - unwrap_in_result::UNWRAP_IN_RESULT, - upper_case_acronyms::UPPER_CASE_ACRONYMS, - use_self::USE_SELF, - useless_conversion::USELESS_CONVERSION, - vec::USELESS_VEC, - vec_init_then_push::VEC_INIT_THEN_PUSH, - vec_resize_to_zero::VEC_RESIZE_TO_ZERO, - verbose_file_reads::VERBOSE_FILE_READS, - wildcard_dependencies::WILDCARD_DEPENDENCIES, - wildcard_imports::ENUM_GLOB_USE, - wildcard_imports::WILDCARD_IMPORTS, - write::PRINTLN_EMPTY_STRING, - write::PRINT_LITERAL, - write::PRINT_STDERR, - write::PRINT_STDOUT, - write::PRINT_WITH_NEWLINE, - write::USE_DEBUG, - write::WRITELN_EMPTY_STRING, - write::WRITE_LITERAL, - write::WRITE_WITH_NEWLINE, - zero_div_zero::ZERO_DIVIDED_BY_ZERO, - zero_sized_map_values::ZERO_SIZED_MAP_VALUES, - ]); - // end register lints, do not remove this comment, it’s used in `update_lints` - - store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ - LintId::of(arithmetic::FLOAT_ARITHMETIC), - LintId::of(arithmetic::INTEGER_ARITHMETIC), - LintId::of(as_conversions::AS_CONVERSIONS), - LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), - LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), - LintId::of(create_dir::CREATE_DIR), - LintId::of(dbg_macro::DBG_MACRO), - LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), - LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS), - LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), - LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), - LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), - LintId::of(exit::EXIT), - LintId::of(float_literal::LOSSY_FLOAT_LITERAL), - LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE), - LintId::of(implicit_return::IMPLICIT_RETURN), - LintId::of(indexing_slicing::INDEXING_SLICING), - LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), - LintId::of(integer_division::INTEGER_DIVISION), - LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), - LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), - LintId::of(map_err_ignore::MAP_ERR_IGNORE), - LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), - LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), - LintId::of(mem_forget::MEM_FORGET), - LintId::of(methods::CLONE_ON_REF_PTR), - LintId::of(methods::EXPECT_USED), - LintId::of(methods::FILETYPE_IS_FILE), - LintId::of(methods::GET_UNWRAP), - LintId::of(methods::UNWRAP_USED), - LintId::of(misc::FLOAT_CMP_CONST), - LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), - LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), - LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES), - LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), - LintId::of(module_style::MOD_MODULE_FILES), - LintId::of(module_style::SELF_NAMED_MODULE_FILES), - LintId::of(modulo_arithmetic::MODULO_ARITHMETIC), - LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), - LintId::of(panic_unimplemented::PANIC), - LintId::of(panic_unimplemented::TODO), - LintId::of(panic_unimplemented::UNIMPLEMENTED), - LintId::of(panic_unimplemented::UNREACHABLE), - LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), - LintId::of(same_name_method::SAME_NAME_METHOD), - LintId::of(shadow::SHADOW_REUSE), - LintId::of(shadow::SHADOW_SAME), - LintId::of(strings::STRING_ADD), - LintId::of(strings::STRING_TO_STRING), - LintId::of(strings::STR_TO_STRING), - LintId::of(types::RC_BUFFER), - LintId::of(types::RC_MUTEX), - LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), - LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), - LintId::of(verbose_file_reads::VERBOSE_FILE_READS), - LintId::of(write::PRINT_STDERR), - LintId::of(write::PRINT_STDOUT), - LintId::of(write::USE_DEBUG), - ]); - - store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ - LintId::of(attrs::INLINE_ALWAYS), - LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), - LintId::of(bit_mask::VERBOSE_BIT_MASK), - LintId::of(bytecount::NAIVE_BYTECOUNT), - LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), - LintId::of(casts::CAST_LOSSLESS), - LintId::of(casts::CAST_POSSIBLE_TRUNCATION), - LintId::of(casts::CAST_POSSIBLE_WRAP), - LintId::of(casts::CAST_PRECISION_LOSS), - LintId::of(casts::CAST_PTR_ALIGNMENT), - LintId::of(casts::CAST_SIGN_LOSS), - LintId::of(casts::PTR_AS_PTR), - LintId::of(checked_conversions::CHECKED_CONVERSIONS), - LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION), - LintId::of(copy_iterator::COPY_ITERATOR), - LintId::of(default::DEFAULT_TRAIT_ACCESS), - LintId::of(dereference::EXPLICIT_DEREF_METHODS), - LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), - LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), - LintId::of(doc::DOC_MARKDOWN), - LintId::of(doc::MISSING_ERRORS_DOC), - LintId::of(doc::MISSING_PANICS_DOC), - LintId::of(empty_enum::EMPTY_ENUM), - LintId::of(enum_variants::MODULE_NAME_REPETITIONS), - LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), - LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), - LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), - LintId::of(functions::MUST_USE_CANDIDATE), - LintId::of(functions::TOO_MANY_LINES), - LintId::of(if_not_else::IF_NOT_ELSE), - LintId::of(implicit_hasher::IMPLICIT_HASHER), - LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), - LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), - LintId::of(infinite_iter::MAYBE_INFINITE_ITER), - LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), - LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), - LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR), - LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS), - LintId::of(let_underscore::LET_UNDERSCORE_DROP), - LintId::of(literal_representation::LARGE_DIGIT_GROUPS), - LintId::of(literal_representation::UNREADABLE_LITERAL), - LintId::of(loops::EXPLICIT_INTO_ITER_LOOP), - LintId::of(loops::EXPLICIT_ITER_LOOP), - LintId::of(macro_use::MACRO_USE_IMPORTS), - LintId::of(manual_ok_or::MANUAL_OK_OR), - LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS), - LintId::of(matches::MATCH_BOOL), - LintId::of(matches::MATCH_SAME_ARMS), - LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), - LintId::of(matches::MATCH_WILD_ERR_ARM), - LintId::of(matches::SINGLE_MATCH_ELSE), - LintId::of(methods::CLONED_INSTEAD_OF_COPIED), - LintId::of(methods::FILTER_MAP_NEXT), - LintId::of(methods::FLAT_MAP_OPTION), - LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), - LintId::of(methods::IMPLICIT_CLONE), - LintId::of(methods::INEFFICIENT_TO_STRING), - LintId::of(methods::MAP_FLATTEN), - LintId::of(methods::MAP_UNWRAP_OR), - LintId::of(misc::FLOAT_CMP), - LintId::of(misc::USED_UNDERSCORE_BINDING), - LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX), - LintId::of(mut_mut::MUT_MUT), - LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL), - LintId::of(needless_borrow::REF_BINDING_TO_REFERENCE), - LintId::of(needless_continue::NEEDLESS_CONTINUE), - LintId::of(needless_for_each::NEEDLESS_FOR_EACH), - LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), - LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), - LintId::of(non_expressive_names::SIMILAR_NAMES), - LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), - LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), - LintId::of(ranges::RANGE_MINUS_ONE), - LintId::of(ranges::RANGE_PLUS_ONE), - LintId::of(redundant_else::REDUNDANT_ELSE), - LintId::of(ref_option_ref::REF_OPTION_REF), - LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), - LintId::of(shadow::SHADOW_UNRELATED), - 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), - LintId::of(unicode::NON_ASCII_LITERAL), - 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), - LintId::of(unused_self::UNUSED_SELF), - LintId::of(wildcard_imports::ENUM_GLOB_USE), - LintId::of(wildcard_imports::WILDCARD_IMPORTS), - LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES), - ]); + include!("lib.register_lints.rs"); + include!("lib.register_restriction.rs"); + include!("lib.register_pedantic.rs"); #[cfg(feature = "internal-lints")] - store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ - LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL), - LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), - LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS), - LintId::of(utils::internal_lints::DEFAULT_LINT), - LintId::of(utils::internal_lints::IF_CHAIN_STYLE), - LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL), - LintId::of(utils::internal_lints::INVALID_PATHS), - LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), - LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), - LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), - LintId::of(utils::internal_lints::PRODUCE_ICE), - LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), - ]); - - store.register_group(true, "clippy::all", Some("clippy"), vec![ - LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), - LintId::of(approx_const::APPROX_CONSTANT), - LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), - LintId::of(assign_ops::ASSIGN_OP_PATTERN), - LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), - LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), - LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), - LintId::of(attrs::DEPRECATED_CFG_ATTR), - LintId::of(attrs::DEPRECATED_SEMVER), - LintId::of(attrs::MISMATCHED_TARGET_OS), - LintId::of(attrs::USELESS_ATTRIBUTE), - LintId::of(bit_mask::BAD_BIT_MASK), - LintId::of(bit_mask::INEFFECTIVE_BIT_MASK), - LintId::of(blacklisted_name::BLACKLISTED_NAME), - LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), - LintId::of(booleans::LOGIC_BUG), - LintId::of(booleans::NONMINIMAL_BOOL), - LintId::of(casts::CAST_REF_TO_MUT), - LintId::of(casts::CHAR_LIT_AS_U8), - LintId::of(casts::FN_TO_NUMERIC_CAST), - LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(casts::UNNECESSARY_CAST), - LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), - LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(collapsible_match::COLLAPSIBLE_MATCH), - LintId::of(comparison_chain::COMPARISON_CHAIN), - LintId::of(copies::IFS_SAME_COND), - LintId::of(copies::IF_SAME_THEN_ELSE), - LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), - LintId::of(derivable_impls::DERIVABLE_IMPLS), - LintId::of(derive::DERIVE_HASH_XOR_EQ), - LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(doc::MISSING_SAFETY_DOC), - LintId::of(doc::NEEDLESS_DOCTEST_MAIN), - 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_REF), - LintId::of(drop_forget_ref::FORGET_COPY), - LintId::of(drop_forget_ref::FORGET_REF), - LintId::of(duration_subsec::DURATION_SUBSEC), - LintId::of(entry::MAP_ENTRY), - LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), - LintId::of(enum_variants::ENUM_VARIANT_NAMES), - LintId::of(enum_variants::MODULE_INCEPTION), - LintId::of(eq_op::EQ_OP), - LintId::of(eq_op::OP_REF), - LintId::of(erasing_op::ERASING_OP), - LintId::of(escape::BOXED_LOCAL), - LintId::of(eta_reduction::REDUNDANT_CLOSURE), - LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), - LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), - LintId::of(explicit_write::EXPLICIT_WRITE), - LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), - LintId::of(float_literal::EXCESSIVE_PRECISION), - LintId::of(format::USELESS_FORMAT), - LintId::of(formatting::POSSIBLE_MISSING_COMMA), - LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), - LintId::of(from_over_into::FROM_OVER_INTO), - LintId::of(from_str_radix_10::FROM_STR_RADIX_10), - LintId::of(functions::DOUBLE_MUST_USE), - LintId::of(functions::MUST_USE_UNIT), - LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), - LintId::of(functions::RESULT_UNIT_ERR), - LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(get_last_with_len::GET_LAST_WITH_LEN), - LintId::of(identity_op::IDENTITY_OP), - LintId::of(if_let_mutex::IF_LET_MUTEX), - LintId::of(if_then_panic::IF_THEN_PANIC), - LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), - LintId::of(infinite_iter::INFINITE_ITER), - LintId::of(inherent_to_string::INHERENT_TO_STRING), - LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), - LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(int_plus_one::INT_PLUS_ONE), - LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), - LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), - LintId::of(len_zero::COMPARISON_TO_EMPTY), - LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), - LintId::of(len_zero::LEN_ZERO), - LintId::of(let_underscore::LET_UNDERSCORE_LOCK), - LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), - LintId::of(lifetimes::NEEDLESS_LIFETIMES), - LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(loops::EMPTY_LOOP), - LintId::of(loops::EXPLICIT_COUNTER_LOOP), - LintId::of(loops::FOR_KV_MAP), - LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), - LintId::of(loops::ITER_NEXT_LOOP), - LintId::of(loops::MANUAL_FLATTEN), - LintId::of(loops::MANUAL_MEMCPY), - LintId::of(loops::MUT_RANGE_BOUND), - LintId::of(loops::NEEDLESS_COLLECT), - LintId::of(loops::NEEDLESS_RANGE_LOOP), - LintId::of(loops::NEVER_LOOP), - LintId::of(loops::SAME_ITEM_PUSH), - LintId::of(loops::SINGLE_ELEMENT_LOOP), - LintId::of(loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(loops::WHILE_LET_LOOP), - LintId::of(loops::WHILE_LET_ON_ITERATOR), - LintId::of(main_recursion::MAIN_RECURSION), - LintId::of(manual_async_fn::MANUAL_ASYNC_FN), - LintId::of(manual_map::MANUAL_MAP), - LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), - LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), - LintId::of(map_clone::MAP_CLONE), - LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), - LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), - LintId::of(match_result_ok::MATCH_RESULT_OK), - LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), - LintId::of(matches::MATCH_AS_REF), - LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), - LintId::of(matches::MATCH_OVERLAPPING_ARM), - LintId::of(matches::MATCH_REF_PATS), - LintId::of(matches::MATCH_SINGLE_BINDING), - LintId::of(matches::REDUNDANT_PATTERN_MATCHING), - LintId::of(matches::SINGLE_MATCH), - LintId::of(matches::WILDCARD_IN_OR_PATTERNS), - LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), - LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), - LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), - LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), - LintId::of(methods::BIND_INSTEAD_OF_MAP), - LintId::of(methods::BYTES_NTH), - LintId::of(methods::CHARS_LAST_CMP), - LintId::of(methods::CHARS_NEXT_CMP), - LintId::of(methods::CLONE_DOUBLE_REF), - LintId::of(methods::CLONE_ON_COPY), - LintId::of(methods::EXPECT_FUN_CALL), - LintId::of(methods::EXTEND_WITH_DRAIN), - LintId::of(methods::FILTER_MAP_IDENTITY), - LintId::of(methods::FILTER_NEXT), - LintId::of(methods::FLAT_MAP_IDENTITY), - LintId::of(methods::INSPECT_FOR_EACH), - LintId::of(methods::INTO_ITER_ON_REF), - LintId::of(methods::ITERATOR_STEP_BY_ZERO), - LintId::of(methods::ITER_CLONED_COLLECT), - LintId::of(methods::ITER_COUNT), - LintId::of(methods::ITER_NEXT_SLICE), - LintId::of(methods::ITER_NTH), - LintId::of(methods::ITER_NTH_ZERO), - LintId::of(methods::ITER_SKIP_NEXT), - LintId::of(methods::MANUAL_FILTER_MAP), - LintId::of(methods::MANUAL_FIND_MAP), - LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), - LintId::of(methods::MANUAL_SPLIT_ONCE), - LintId::of(methods::MANUAL_STR_REPEAT), - LintId::of(methods::MAP_COLLECT_RESULT_UNIT), - LintId::of(methods::MAP_IDENTITY), - LintId::of(methods::NEW_RET_NO_SELF), - LintId::of(methods::OK_EXPECT), - LintId::of(methods::OPTION_AS_REF_DEREF), - LintId::of(methods::OPTION_FILTER_MAP), - LintId::of(methods::OPTION_MAP_OR_NONE), - LintId::of(methods::OR_FUN_CALL), - LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), - LintId::of(methods::SEARCH_IS_SOME), - LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(methods::SINGLE_CHAR_ADD_STR), - LintId::of(methods::SINGLE_CHAR_PATTERN), - LintId::of(methods::SKIP_WHILE_NEXT), - LintId::of(methods::STRING_EXTEND_CHARS), - LintId::of(methods::SUSPICIOUS_MAP), - LintId::of(methods::SUSPICIOUS_SPLITN), - LintId::of(methods::UNINIT_ASSUMED_INIT), - LintId::of(methods::UNNECESSARY_FILTER_MAP), - LintId::of(methods::UNNECESSARY_FOLD), - LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), - LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), - LintId::of(methods::USELESS_ASREF), - LintId::of(methods::WRONG_SELF_CONVENTION), - LintId::of(methods::ZST_OFFSET), - LintId::of(minmax::MIN_MAX), - LintId::of(misc::CMP_NAN), - LintId::of(misc::CMP_OWNED), - LintId::of(misc::MODULO_ONE), - LintId::of(misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(misc::TOPLEVEL_REF_ARG), - LintId::of(misc::ZERO_PTR), - LintId::of(misc_early::BUILTIN_TYPE_SHADOW), - LintId::of(misc_early::DOUBLE_NEG), - LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), - LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(misc_early::REDUNDANT_PATTERN), - LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), - LintId::of(misc_early::ZERO_PREFIXED_LITERAL), - LintId::of(mut_key::MUTABLE_KEY_TYPE), - LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), - LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(mutex_atomic::MUTEX_ATOMIC), - LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), - LintId::of(needless_bool::BOOL_COMPARISON), - LintId::of(needless_bool::NEEDLESS_BOOL), - LintId::of(needless_borrow::NEEDLESS_BORROW), - 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), - LintId::of(neg_multiply::NEG_MULTIPLY), - LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), - LintId::of(no_effect::NO_EFFECT), - LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), - LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), - LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), - LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), - LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), - LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), - LintId::of(precedence::PRECEDENCE), - LintId::of(ptr::CMP_NULL), - LintId::of(ptr::INVALID_NULL_PTR_USAGE), - LintId::of(ptr::MUT_FROM_REF), - LintId::of(ptr::PTR_ARG), - LintId::of(ptr_eq::PTR_EQ), - LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(question_mark::QUESTION_MARK), - LintId::of(ranges::MANUAL_RANGE_CONTAINS), - LintId::of(ranges::RANGE_ZIP_WITH_LEN), - LintId::of(ranges::REVERSED_EMPTY_RANGES), - LintId::of(redundant_clone::REDUNDANT_CLONE), - LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), - LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(redundant_slicing::REDUNDANT_SLICING), - LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(reference::DEREF_ADDROF), - LintId::of(reference::REF_IN_DEREF), - LintId::of(regex::INVALID_REGEX), - LintId::of(repeat_once::REPEAT_ONCE), - LintId::of(returns::LET_AND_RETURN), - LintId::of(returns::NEEDLESS_RETURN), - LintId::of(self_assignment::SELF_ASSIGNMENT), - LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), - LintId::of(serde_api::SERDE_API_MISUSE), - 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), - LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - LintId::of(swap::ALMOST_SWAPPED), - LintId::of(swap::MANUAL_SWAP), - LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), - LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), - LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY), - LintId::of(transmute::CROSSPOINTER_TRANSMUTE), - LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), - LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), - LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), - LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), - LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), - LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(transmute::TRANSMUTE_PTR_TO_REF), - LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), - LintId::of(transmute::WRONG_TRANSMUTE), - LintId::of(transmuting_null::TRANSMUTING_NULL), - LintId::of(try_err::TRY_ERR), - LintId::of(types::BORROWED_BOX), - LintId::of(types::BOX_COLLECTION), - 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(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), - LintId::of(unit_types::UNIT_ARG), - LintId::of(unit_types::UNIT_CMP), - LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), - LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), - 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), - LintId::of(unused_unit::UNUSED_UNIT), - LintId::of(unwrap::PANICKING_UNWRAP), - LintId::of(unwrap::UNNECESSARY_UNWRAP), - LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), - LintId::of(useless_conversion::USELESS_CONVERSION), - LintId::of(vec::USELESS_VEC), - LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), - LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO), - LintId::of(write::PRINTLN_EMPTY_STRING), - LintId::of(write::PRINT_LITERAL), - LintId::of(write::PRINT_WITH_NEWLINE), - LintId::of(write::WRITELN_EMPTY_STRING), - LintId::of(write::WRITE_LITERAL), - LintId::of(write::WRITE_WITH_NEWLINE), - LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), - ]); - - store.register_group(true, "clippy::style", Some("clippy_style"), vec![ - LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), - LintId::of(assign_ops::ASSIGN_OP_PATTERN), - LintId::of(blacklisted_name::BLACKLISTED_NAME), - LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), - LintId::of(casts::FN_TO_NUMERIC_CAST), - LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), - LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(collapsible_match::COLLAPSIBLE_MATCH), - LintId::of(comparison_chain::COMPARISON_CHAIN), - LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), - LintId::of(doc::MISSING_SAFETY_DOC), - LintId::of(doc::NEEDLESS_DOCTEST_MAIN), - LintId::of(enum_variants::ENUM_VARIANT_NAMES), - LintId::of(enum_variants::MODULE_INCEPTION), - LintId::of(eq_op::OP_REF), - LintId::of(eta_reduction::REDUNDANT_CLOSURE), - LintId::of(float_literal::EXCESSIVE_PRECISION), - LintId::of(from_over_into::FROM_OVER_INTO), - LintId::of(from_str_radix_10::FROM_STR_RADIX_10), - LintId::of(functions::DOUBLE_MUST_USE), - LintId::of(functions::MUST_USE_UNIT), - LintId::of(functions::RESULT_UNIT_ERR), - LintId::of(if_then_panic::IF_THEN_PANIC), - LintId::of(inherent_to_string::INHERENT_TO_STRING), - LintId::of(len_zero::COMPARISON_TO_EMPTY), - LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), - LintId::of(len_zero::LEN_ZERO), - LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(loops::FOR_KV_MAP), - LintId::of(loops::NEEDLESS_RANGE_LOOP), - LintId::of(loops::SAME_ITEM_PUSH), - LintId::of(loops::WHILE_LET_ON_ITERATOR), - LintId::of(main_recursion::MAIN_RECURSION), - LintId::of(manual_async_fn::MANUAL_ASYNC_FN), - LintId::of(manual_map::MANUAL_MAP), - LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), - LintId::of(map_clone::MAP_CLONE), - LintId::of(match_result_ok::MATCH_RESULT_OK), - LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), - LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), - LintId::of(matches::MATCH_OVERLAPPING_ARM), - LintId::of(matches::MATCH_REF_PATS), - LintId::of(matches::REDUNDANT_PATTERN_MATCHING), - LintId::of(matches::SINGLE_MATCH), - LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), - LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), - LintId::of(methods::BYTES_NTH), - LintId::of(methods::CHARS_LAST_CMP), - LintId::of(methods::CHARS_NEXT_CMP), - LintId::of(methods::INTO_ITER_ON_REF), - LintId::of(methods::ITER_CLONED_COLLECT), - LintId::of(methods::ITER_NEXT_SLICE), - LintId::of(methods::ITER_NTH_ZERO), - LintId::of(methods::ITER_SKIP_NEXT), - LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), - LintId::of(methods::MAP_COLLECT_RESULT_UNIT), - LintId::of(methods::NEW_RET_NO_SELF), - LintId::of(methods::OK_EXPECT), - LintId::of(methods::OPTION_MAP_OR_NONE), - LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), - LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(methods::SINGLE_CHAR_ADD_STR), - LintId::of(methods::STRING_EXTEND_CHARS), - LintId::of(methods::UNNECESSARY_FOLD), - LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), - LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), - LintId::of(methods::WRONG_SELF_CONVENTION), - LintId::of(misc::TOPLEVEL_REF_ARG), - LintId::of(misc::ZERO_PTR), - LintId::of(misc_early::BUILTIN_TYPE_SHADOW), - LintId::of(misc_early::DOUBLE_NEG), - LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), - LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(misc_early::REDUNDANT_PATTERN), - LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), - LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(needless_borrow::NEEDLESS_BORROW), - LintId::of(neg_multiply::NEG_MULTIPLY), - LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), - LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), - LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), - LintId::of(ptr::CMP_NULL), - LintId::of(ptr::PTR_ARG), - LintId::of(ptr_eq::PTR_EQ), - LintId::of(question_mark::QUESTION_MARK), - LintId::of(ranges::MANUAL_RANGE_CONTAINS), - LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(returns::LET_AND_RETURN), - 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(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), - LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(try_err::TRY_ERR), - LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(unused_unit::UNUSED_UNIT), - LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), - LintId::of(write::PRINTLN_EMPTY_STRING), - LintId::of(write::PRINT_LITERAL), - LintId::of(write::PRINT_WITH_NEWLINE), - LintId::of(write::WRITELN_EMPTY_STRING), - LintId::of(write::WRITE_LITERAL), - LintId::of(write::WRITE_WITH_NEWLINE), - ]); - - store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ - LintId::of(attrs::DEPRECATED_CFG_ATTR), - LintId::of(booleans::NONMINIMAL_BOOL), - LintId::of(casts::CHAR_LIT_AS_U8), - LintId::of(casts::UNNECESSARY_CAST), - LintId::of(derivable_impls::DERIVABLE_IMPLS), - LintId::of(double_comparison::DOUBLE_COMPARISONS), - LintId::of(double_parens::DOUBLE_PARENS), - LintId::of(duration_subsec::DURATION_SUBSEC), - LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), - LintId::of(explicit_write::EXPLICIT_WRITE), - LintId::of(format::USELESS_FORMAT), - LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(get_last_with_len::GET_LAST_WITH_LEN), - LintId::of(identity_op::IDENTITY_OP), - LintId::of(int_plus_one::INT_PLUS_ONE), - LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), - LintId::of(lifetimes::NEEDLESS_LIFETIMES), - LintId::of(loops::EXPLICIT_COUNTER_LOOP), - LintId::of(loops::MANUAL_FLATTEN), - LintId::of(loops::SINGLE_ELEMENT_LOOP), - LintId::of(loops::WHILE_LET_LOOP), - LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), - LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), - LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), - LintId::of(matches::MATCH_AS_REF), - LintId::of(matches::MATCH_SINGLE_BINDING), - LintId::of(matches::WILDCARD_IN_OR_PATTERNS), - LintId::of(methods::BIND_INSTEAD_OF_MAP), - LintId::of(methods::CLONE_ON_COPY), - LintId::of(methods::FILTER_MAP_IDENTITY), - LintId::of(methods::FILTER_NEXT), - LintId::of(methods::FLAT_MAP_IDENTITY), - LintId::of(methods::INSPECT_FOR_EACH), - LintId::of(methods::ITER_COUNT), - LintId::of(methods::MANUAL_FILTER_MAP), - LintId::of(methods::MANUAL_FIND_MAP), - LintId::of(methods::MANUAL_SPLIT_ONCE), - LintId::of(methods::MAP_IDENTITY), - LintId::of(methods::OPTION_AS_REF_DEREF), - LintId::of(methods::OPTION_FILTER_MAP), - LintId::of(methods::SEARCH_IS_SOME), - LintId::of(methods::SKIP_WHILE_NEXT), - LintId::of(methods::UNNECESSARY_FILTER_MAP), - LintId::of(methods::USELESS_ASREF), - LintId::of(misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), - LintId::of(misc_early::ZERO_PREFIXED_LITERAL), - LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), - 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), - LintId::of(no_effect::NO_EFFECT), - LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), - LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), - LintId::of(precedence::PRECEDENCE), - LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(ranges::RANGE_ZIP_WITH_LEN), - LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), - LintId::of(redundant_slicing::REDUNDANT_SLICING), - LintId::of(reference::DEREF_ADDROF), - LintId::of(reference::REF_IN_DEREF), - LintId::of(repeat_once::REPEAT_ONCE), - LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), - LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), - LintId::of(swap::MANUAL_SWAP), - LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), - LintId::of(transmute::CROSSPOINTER_TRANSMUTE), - LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), - LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), - LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), - LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), - LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), - LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(transmute::TRANSMUTE_PTR_TO_REF), - LintId::of(types::BORROWED_BOX), - LintId::of(types::TYPE_COMPLEXITY), - LintId::of(types::VEC_BOX), - LintId::of(unit_types::UNIT_ARG), - LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY), - LintId::of(unwrap::UNNECESSARY_UNWRAP), - LintId::of(useless_conversion::USELESS_CONVERSION), - LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), - ]); - - store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ - LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), - LintId::of(approx_const::APPROX_CONSTANT), - LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), - LintId::of(attrs::DEPRECATED_SEMVER), - LintId::of(attrs::MISMATCHED_TARGET_OS), - LintId::of(attrs::USELESS_ATTRIBUTE), - LintId::of(bit_mask::BAD_BIT_MASK), - LintId::of(bit_mask::INEFFECTIVE_BIT_MASK), - LintId::of(booleans::LOGIC_BUG), - LintId::of(casts::CAST_REF_TO_MUT), - LintId::of(copies::IFS_SAME_COND), - LintId::of(copies::IF_SAME_THEN_ELSE), - LintId::of(derive::DERIVE_HASH_XOR_EQ), - LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(drop_forget_ref::DROP_COPY), - LintId::of(drop_forget_ref::DROP_REF), - LintId::of(drop_forget_ref::FORGET_COPY), - LintId::of(drop_forget_ref::FORGET_REF), - LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), - LintId::of(eq_op::EQ_OP), - LintId::of(erasing_op::ERASING_OP), - LintId::of(formatting::POSSIBLE_MISSING_COMMA), - LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), - LintId::of(if_let_mutex::IF_LET_MUTEX), - LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), - LintId::of(infinite_iter::INFINITE_ITER), - LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), - LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(let_underscore::LET_UNDERSCORE_LOCK), - LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(loops::ITER_NEXT_LOOP), - LintId::of(loops::NEVER_LOOP), - LintId::of(loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), - LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), - LintId::of(methods::CLONE_DOUBLE_REF), - LintId::of(methods::ITERATOR_STEP_BY_ZERO), - LintId::of(methods::SUSPICIOUS_SPLITN), - LintId::of(methods::UNINIT_ASSUMED_INIT), - LintId::of(methods::ZST_OFFSET), - LintId::of(minmax::MIN_MAX), - LintId::of(misc::CMP_NAN), - LintId::of(misc::MODULO_ONE), - LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), - LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), - LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(ptr::INVALID_NULL_PTR_USAGE), - LintId::of(ptr::MUT_FROM_REF), - LintId::of(ranges::REVERSED_EMPTY_RANGES), - LintId::of(regex::INVALID_REGEX), - LintId::of(self_assignment::SELF_ASSIGNMENT), - LintId::of(serde_api::SERDE_API_MISUSE), - LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), - LintId::of(swap::ALMOST_SWAPPED), - LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY), - 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(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), - LintId::of(unit_types::UNIT_CMP), - LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), - LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), - LintId::of(unwrap::PANICKING_UNWRAP), - LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO), - ]); - - store.register_group(true, "clippy::suspicious", None, vec![ - LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), - LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), - LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), - LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), - LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), - LintId::of(loops::EMPTY_LOOP), - LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), - LintId::of(loops::MUT_RANGE_BOUND), - LintId::of(methods::SUSPICIOUS_MAP), - LintId::of(mut_key::MUTABLE_KEY_TYPE), - LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - ]); - - store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ - LintId::of(entry::MAP_ENTRY), - LintId::of(escape::BOXED_LOCAL), - LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), - LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), - LintId::of(loops::MANUAL_MEMCPY), - LintId::of(loops::NEEDLESS_COLLECT), - LintId::of(methods::EXPECT_FUN_CALL), - LintId::of(methods::EXTEND_WITH_DRAIN), - LintId::of(methods::ITER_NTH), - LintId::of(methods::MANUAL_STR_REPEAT), - LintId::of(methods::OR_FUN_CALL), - LintId::of(methods::SINGLE_CHAR_PATTERN), - LintId::of(misc::CMP_OWNED), - LintId::of(mutex_atomic::MUTEX_ATOMIC), - LintId::of(redundant_clone::REDUNDANT_CLONE), - LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), - LintId::of(types::BOX_COLLECTION), - LintId::of(types::REDUNDANT_ALLOCATION), - LintId::of(vec::USELESS_VEC), - LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), - ]); - - store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ - LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA), - LintId::of(feature_name::NEGATIVE_FEATURE_NAMES), - LintId::of(feature_name::REDUNDANT_FEATURE_NAMES), - LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), - LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES), - ]); + include!("lib.register_internal.rs"); - store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ - LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), - LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), - LintId::of(copies::BRANCHES_SHARING_CODE), - LintId::of(disallowed_method::DISALLOWED_METHOD), - LintId::of(disallowed_type::DISALLOWED_TYPE), - LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), - LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), - LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS), - LintId::of(future_not_send::FUTURE_NOT_SEND), - LintId::of(let_if_seq::USELESS_LET_IF_SEQ), - LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), - LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), - LintId::of(mutex_atomic::MUTEX_INTEGER), - LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), - 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), - LintId::of(regex::TRIVIAL_REGEX), - LintId::of(strings::STRING_LIT_AS_BYTES), - LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), - LintId::of(transmute::USELESS_TRANSMUTE), - LintId::of(use_self::USE_SELF), - ]); + include!("lib.register_all.rs"); + include!("lib.register_style.rs"); + include!("lib.register_complexity.rs"); + include!("lib.register_correctness.rs"); + include!("lib.register_suspicious.rs"); + include!("lib.register_perf.rs"); + include!("lib.register_cargo.rs"); + include!("lib.register_nursery.rs"); #[cfg(feature = "metadata-collector-lint")] { @@ -1931,7 +555,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(same_name_method::SameNameMethod)); store.register_late_pass(|| Box::new(map_clone::MapClone)); store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore)); - store.register_late_pass(|| Box::new(shadow::Shadow)); + store.register_late_pass(|| Box::new(shadow::Shadow::default())); store.register_late_pass(|| Box::new(unit_types::UnitTypes)); store.register_late_pass(|| Box::new(loops::Loops)); store.register_late_pass(|| Box::new(main_recursion::MainRecursion::default())); @@ -2092,6 +716,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|| Box::new(future_not_send::FutureNotSend)); store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex)); + store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality)); store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock)); store.register_late_pass(|| Box::new(match_on_vec_items::MatchOnVecItems)); store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn)); @@ -2142,6 +767,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(feature_name::FeatureName)); store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator)); store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic)); + let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send; + store.register_late_pass(move || Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic_for_send))); } #[rustfmt::skip] diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 358d53e8859d0..aedf0844937d1 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -92,7 +92,7 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { - if let ty::BorrowKind::MutBorrow = bk { + if bk == ty::BorrowKind::MutBorrow { if let PlaceBase::Local(id) = cmt.place.base { if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 7157b80118558..172d9fc39a29f 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -95,7 +95,7 @@ pub(super) fn check<'tcx>( let mut take_expr = end; if let ExprKind::Binary(ref op, left, right) = end.kind { - if let BinOpKind::Add = op.node { + if op.node == BinOpKind::Add { let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 41956650c9f4e..c0fde5e51663e 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { NeverLoopResult::AlwaysBreak => { span_lint_and_then(cx, NEVER_LOOP, expr.span, "this loop never actually loops", |diag| { if_chain! { - if let LoopSource::ForLoop = source; + if source == LoopSource::ForLoop; if let Some((_, Node::Expr(parent_match))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1); if let Some(ForLoop { arg: iterator, pat, span: for_span, .. }) = ForLoop::hir(parent_match); then { diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 8e1385fb83a25..b632af455f855 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { ) { if_chain! { if let Some(header) = kind.header(); - if let IsAsync::NotAsync = header.asyncness; + if header.asyncness == IsAsync::NotAsync; // Check that this function returns `impl Future` if let FnRetTy::Return(ret_ty) = decl.output; if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty); @@ -178,7 +178,7 @@ fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) if args.len() == 1; if let Expr{kind: ExprKind::Closure(_, _, body_id, ..), ..} = args[0]; let closure_body = cx.tcx.hir().body(body_id); - if let Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) = closure_body.generator_kind; + if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block)); then { return Some(closure_body); } diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 952e250bb9e6c..40de9ffcd4e25 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -205,14 +205,13 @@ fn suggestion_msg(function_type: &str, map_type: &str) -> String { fn lint_map_unit_fn(cx: &LateContext<'_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr<'_>, map_args: &[hir::Expr<'_>]) { let var_arg = &map_args[0]; - let (map_type, variant, lint) = - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Option) { - ("Option", "Some", OPTION_MAP_UNIT_FN) - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Result) { - ("Result", "Ok", RESULT_MAP_UNIT_FN) - } else { - return; - }; + let (map_type, variant, lint) = if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Option) { + ("Option", "Some", OPTION_MAP_UNIT_FN) + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Result) { + ("Result", "Ok", RESULT_MAP_UNIT_FN) + } else { + return; + }; let fn_arg = &map_args[1]; if is_unit_function(cx, fn_arg) { diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index a685c1eaa2cd5..56d4163a6b343 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1025,8 +1025,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) let adt_def = match ty.kind() { ty::Adt(adt_def, _) if adt_def.is_enum() - && !(is_type_diagnostic_item(cx, ty, sym::Option) - || is_type_diagnostic_item(cx, ty, sym::Result)) => + && !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) => { adt_def }, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2025056ac94c3..b26d11c0d6b0d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1284,8 +1284,9 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// It looks suspicious. Maybe `map` was confused with `filter`. - /// If the `map` call is intentional, this should be rewritten. Or, if you intend to - /// drive the iterator to completion, you can just use `for_each` instead. + /// If the `map` call is intentional, this should be rewritten + /// using `inspect`. Or, if you intend to drive the iterator to + /// completion, you can just use `for_each` instead. /// /// ### Example /// ```rust diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index cabbb8400767d..b5bbbb09092af 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -178,15 +178,15 @@ pub(super) fn check<'tcx>( hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); }, - hir::ExprKind::Block(block, _) => { - if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules { - if let Some(block_expr) = block.expr { - if let hir::ExprKind::MethodCall(..) = block_expr.kind { - check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); - } + hir::ExprKind::Block(block, _) + if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => + { + if let Some(block_expr) = block.expr { + if let hir::ExprKind::MethodCall(..) = block_expr.kind { + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); } } - }, + } _ => (), } } diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index 0fd0668c73402..18ded291915e1 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -28,7 +28,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hi expr.span, "this call to `map()` won't have an effect on the call to `count()`", None, - "make sure you did not confuse `map` with `filter` or `for_each`", + "make sure you did not confuse `map` with `filter`, `for_each` or `inspect`", ); } } diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index a9d3764d92d44..59bdfb923ed49 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -35,8 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); match cx.typeck_results().expr_ty(&body.value).kind() { ty::Adt(adt, subst) - if cx.tcx.is_diagnostic_item(sym::Option, adt.did) - && TyS::same_type(in_ty, subst.type_at(0)) => + if cx.tcx.is_diagnostic_item(sym::Option, adt.did) && TyS::same_type(in_ty, subst.type_at(0)) => { "filter" }, diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 2d14943b56c95..f45e68233a177 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -128,7 +128,7 @@ impl<'tcx> LateLintPass<'tcx> for ModuloArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { match &expr.kind { ExprKind::Binary(op, lhs, rhs) | ExprKind::AssignOp(op, lhs, rhs) => { - if let BinOpKind::Rem = op.node { + if op.node == BinOpKind::Rem { let lhs_operand = analyze_operand(lhs, cx, expr); let rhs_operand = analyze_operand(rhs, cx, expr); if_chain! { diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index c9dd94400efb9..91944653500bb 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -248,7 +248,7 @@ fn check_comparison<'a, 'tcx>( if l_ty.is_bool() && r_ty.is_bool() { let mut applicability = Applicability::MachineApplicable; - if let BinOpKind::Eq = op.node { + if op.node == BinOpKind::Eq { let expression_info = one_side_is_unary_not(left_side, right_side); if expression_info.one_side_is_unary_not { span_lint_and_sugg( diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index fa36d8fb1b30f..1b15d29439f78 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -46,7 +46,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 let Constant::Int(1) = consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)); + if consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1); if cx.typeck_results().expr_ty(exp).is_integral(); then { span_lint(cx, NEG_MULTIPLY, span, "negation by multiplying with `-1`"); diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 0ad616a39d266..0ac27f1cba226 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { }) = item.kind { for assoc_item in items { - if let hir::AssocItemKind::Fn { has_self: false } = assoc_item.kind { + 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) { return; diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs new file mode 100644 index 0000000000000..374b7bd59649e --- /dev/null +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -0,0 +1,238 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_lint_allowed; +use clippy_utils::source::snippet; +use clippy_utils::ty::{implements_trait, is_copy}; +use rustc_ast::ImplPolarity; +use rustc_hir::def_id::DefId; +use rustc_hir::{FieldDef, Item, ItemKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Warns about fields in struct implementing `Send` that are neither `Send` nor `Copy`. + /// + /// ### Why is this bad? + /// Sending the struct to another thread will transfer the ownership to + /// the new thread by dropping in the current thread during the transfer. + /// This causes soundness issues for non-`Send` fields, as they are also + /// dropped and might not be set up to handle this. + /// + /// See: + /// * [*The Rustonomicon* about *Send and Sync*](https://doc.rust-lang.org/nomicon/send-and-sync.html) + /// * [The documentation of `Send`](https://doc.rust-lang.org/std/marker/trait.Send.html) + /// + /// ### Known Problems + /// Data structures that contain raw pointers may cause false positives. + /// They are sometimes safe to be sent across threads but do not implement + /// the `Send` trait. This lint has a heuristic to filter out basic cases + /// such as `Vec<*const T>`, but it's not perfect. Feel free to create an + /// issue if you have a suggestion on how this heuristic can be improved. + /// + /// ### Example + /// ```rust,ignore + /// struct ExampleStruct { + /// rc_is_not_send: Rc, + /// unbounded_generic_field: T, + /// } + /// + /// // This impl is unsound because it allows sending `!Send` types through `ExampleStruct` + /// unsafe impl Send for ExampleStruct {} + /// ``` + /// Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) + /// or specify correct bounds on generic type parameters (`T: Send`). + pub NON_SEND_FIELDS_IN_SEND_TY, + nursery, + "there is field that does not implement `Send` in a `Send` struct" +} + +#[derive(Copy, Clone)] +pub struct NonSendFieldInSendTy { + enable_raw_pointer_heuristic: bool, +} + +impl NonSendFieldInSendTy { + pub fn new(enable_raw_pointer_heuristic: bool) -> Self { + Self { + enable_raw_pointer_heuristic, + } + } +} + +impl_lint_pass!(NonSendFieldInSendTy => [NON_SEND_FIELDS_IN_SEND_TY]); + +impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let ty_allowed_in_send = if self.enable_raw_pointer_heuristic { + ty_allowed_with_raw_pointer_heuristic + } else { + ty_allowed_without_raw_pointer_heuristic + }; + + // Checks if we are in `Send` impl item. + // We start from `Send` impl instead of `check_field_def()` because + // single `AdtDef` may have multiple `Send` impls due to generic + // parameters, and the lint is much easier to implement in this way. + if_chain! { + if let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send); + if let ItemKind::Impl(hir_impl) = &item.kind; + if let Some(trait_ref) = &hir_impl.of_trait; + if let Some(trait_id) = trait_ref.trait_def_id(); + if send_trait == trait_id; + if hir_impl.polarity == ImplPolarity::Positive; + if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.def_id); + if let self_ty = ty_trait_ref.self_ty(); + if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind(); + then { + let mut non_send_fields = Vec::new(); + + let hir_map = cx.tcx.hir(); + for variant in &adt_def.variants { + for field in &variant.fields { + if_chain! { + if let Some(field_hir_id) = field + .did + .as_local() + .map(|local_def_id| hir_map.local_def_id_to_hir_id(local_def_id)); + if !is_lint_allowed(cx, NON_SEND_FIELDS_IN_SEND_TY, field_hir_id); + if let field_ty = field.ty(cx.tcx, impl_trait_substs); + if !ty_allowed_in_send(cx, field_ty, send_trait); + if let Node::Field(field_def) = hir_map.get(field_hir_id); + then { + non_send_fields.push(NonSendField { + def: field_def, + ty: field_ty, + generic_params: collect_generic_params(cx, field_ty), + }) + } + } + } + } + + if !non_send_fields.is_empty() { + span_lint_and_then( + cx, + NON_SEND_FIELDS_IN_SEND_TY, + item.span, + &format!( + "this implementation is unsound, as some fields in `{}` are `!Send`", + snippet(cx, hir_impl.self_ty.span, "Unknown") + ), + |diag| { + for field in non_send_fields { + diag.span_note( + field.def.span, + &format!("the type of field `{}` is `!Send`", field.def.ident.name), + ); + + match field.generic_params.len() { + 0 => diag.help("use a thread-safe type that implements `Send`"), + 1 if is_ty_param(field.ty) => diag.help(&format!("add `{}: Send` bound in `Send` impl", field.ty)), + _ => diag.help(&format!( + "add bounds on type parameter{} `{}` that satisfy `{}: Send`", + if field.generic_params.len() > 1 { "s" } else { "" }, + field.generic_params_string(), + snippet(cx, field.def.ty.span, "Unknown"), + )), + }; + } + }, + ); + } + } + } + } +} + +struct NonSendField<'tcx> { + def: &'tcx FieldDef<'tcx>, + ty: Ty<'tcx>, + generic_params: Vec>, +} + +impl<'tcx> NonSendField<'tcx> { + fn generic_params_string(&self) -> String { + self.generic_params + .iter() + .map(ToString::to_string) + .collect::>() + .join(", ") + } +} + +/// Given a type, collect all of its generic parameters. +/// Example: `MyStruct>` => `vec![P, Q, R]` +fn collect_generic_params<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Vec> { + ty.walk(cx.tcx) + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty), + _ => None, + }) + .filter(|&inner_ty| is_ty_param(inner_ty)) + .collect() +} + +/// Be more strict when the heuristic is disabled +fn ty_allowed_without_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool { + if implements_trait(cx, ty, send_trait, &[]) { + return true; + } + + if is_copy(cx, ty) && !contains_raw_pointer(cx, ty) { + return true; + } + + false +} + +/// Heuristic to allow cases like `Vec<*const u8>` +fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool { + if implements_trait(cx, ty, send_trait, &[]) || is_copy(cx, ty) { + return true; + } + + // The type is known to be `!Send` and `!Copy` + match ty.kind() { + ty::Tuple(_) => ty + .tuple_fields() + .all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)), + ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), + ty::Adt(_, substs) => { + if contains_raw_pointer(cx, ty) { + // descends only if ADT contains any raw pointers + substs.iter().all(|generic_arg| match generic_arg.unpack() { + GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), + // Lifetimes and const generics are not solid part of ADT and ignored + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true, + }) + } else { + false + } + }, + // Raw pointers are `!Send` but allowed by the heuristic + ty::RawPtr(_) => true, + _ => false, + } +} + +/// Checks if the type contains any raw pointers in substs (including nested ones). +fn contains_raw_pointer<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool { + for ty_node in target_ty.walk(cx.tcx) { + if_chain! { + if let GenericArgKind::Type(inner_ty) = ty_node.unpack(); + if let ty::RawPtr(_) = inner_ty.kind(); + then { + return true; + } + } + } + + false +} + +/// Returns `true` if the type is a type parameter such as `T`. +fn is_ty_param(target_ty: Ty<'_>) -> bool { + matches!(target_ty.kind(), ty::Param(_)) +} diff --git a/clippy_lints/src/overflow_check_conditional.rs b/clippy_lints/src/overflow_check_conditional.rs index 34755afdb72f0..0f9e5ada3a8a4 100644 --- a/clippy_lints/src/overflow_check_conditional.rs +++ b/clippy_lints/src/overflow_check_conditional.rs @@ -26,6 +26,9 @@ declare_clippy_lint! { declare_lint_pass!(OverflowCheckConditional => [OVERFLOW_CHECK_CONDITIONAL]); +const OVERFLOW_MSG: &str = "you are trying to use classic C overflow conditions that will fail in Rust"; +const UNDERFLOW_MSG: &str = "you are trying to use classic C underflow conditions that will fail in Rust"; + impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { // a + b < a, a > a + b, a < a - b, a - b > a fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -40,17 +43,11 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { if cx.typeck_results().expr_ty(ident1).is_integral(); if cx.typeck_results().expr_ty(ident2).is_integral(); then { - if let BinOpKind::Lt = op.node { - if let BinOpKind::Add = op2.node { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "you are trying to use classic C overflow conditions that will fail in Rust"); - } + if op.node == BinOpKind::Lt && op2.node == BinOpKind::Add { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG); } - if let BinOpKind::Gt = op.node { - if let BinOpKind::Sub = op2.node { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "you are trying to use classic C underflow conditions that will fail in Rust"); - } + if op.node == BinOpKind::Gt && op2.node == BinOpKind::Sub { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG); } } } @@ -65,17 +62,11 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { if cx.typeck_results().expr_ty(ident1).is_integral(); if cx.typeck_results().expr_ty(ident2).is_integral(); then { - if let BinOpKind::Gt = op.node { - if let BinOpKind::Add = op2.node { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "you are trying to use classic C overflow conditions that will fail in Rust"); - } + if op.node == BinOpKind::Gt && op2.node == BinOpKind::Add { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG); } - if let BinOpKind::Lt = op.node { - if let BinOpKind::Sub = op2.node { - span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, - "you are trying to use classic C underflow conditions that will fail in Rust"); - } + if op.node == BinOpKind::Lt && op2.node == BinOpKind::Sub { + span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG); } } } diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index ed2e1f90fa597..919d4e11e5a06 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -41,25 +41,23 @@ 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) { - if let Some(false) = self.is_exported.last() { - 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( - cx, - REDUNDANT_PUB_CRATE, - span, - &format!("pub(crate) {} inside private module", descr), - |diag| { - diag.span_suggestion( - item.vis.span, - "consider using", - "pub".to_string(), - Applicability::MachineApplicable, - ); - }, - ); - } + 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()); + span_lint_and_then( + cx, + REDUNDANT_PUB_CRATE, + span, + &format!("pub(crate) {} inside private module", descr), + |diag| { + diag.span_suggestion( + item.vis.span, + "consider using", + "pub".to_string(), + Applicability::MachineApplicable, + ); + }, + ); } } diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 5fd0d1527639e..cf94c0e97d930 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { if_chain! { if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind; if path.ident.name == sym!(repeat); - if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(count); + if constant_context(cx, cx.typeck_results()).expr(count) == Some(Constant::Int(1)); if !in_macro(receiver.span); then { let ty = cx.typeck_results().expr_ty(receiver).peel_refs(); diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index b9e317a3cfd03..2ca7c18800ee2 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -1,17 +1,14 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::source::snippet; -use clippy_utils::{contains_name, higher, iter_input_pats}; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{ - Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind, - UnOp, -}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; -use rustc_span::symbol::Symbol; +use clippy_utils::visitors::is_local_used; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::Res; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::hir_id::ItemLocalId; +use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -23,10 +20,6 @@ declare_clippy_lint! { /// code. Still, some may opt to avoid it in their code base, they can set this /// lint to `Warn`. /// - /// ### Known problems - /// This lint, as the other shadowing related lints, - /// currently only catches very simple patterns. - /// /// ### Example /// ```rust /// # let x = 1; @@ -52,10 +45,6 @@ declare_clippy_lint! { /// because a value may be bound to different things depending on position in /// the code. /// - /// ### Known problems - /// This lint, as the other shadowing related lints, - /// currently only catches very simple patterns. - /// /// ### Example /// ```rust /// let x = 2; @@ -83,12 +72,6 @@ declare_clippy_lint! { /// any place in the code. This can be alleviated by either giving more specific /// names to bindings or introducing more scopes to contain the bindings. /// - /// ### Known problems - /// This lint, as the other shadowing related lints, - /// currently only catches very simple patterns. Note that - /// `allow`/`warn`/`deny`/`forbid` attributes only work on the function level - /// for this lint. - /// /// ### Example /// ```rust /// # let y = 1; @@ -102,307 +85,151 @@ declare_clippy_lint! { /// let w = z; // use different variable name /// ``` pub SHADOW_UNRELATED, - pedantic, + restriction, "rebinding a name without even using the original value" } -declare_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]); +#[derive(Default)] +pub(crate) struct Shadow { + bindings: Vec>>, +} + +impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]); impl<'tcx> LateLintPass<'tcx> for Shadow { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - _: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - body: &'tcx Body<'_>, - _: Span, - _: HirId, - ) { - if in_external_macro(cx.sess(), body.value.span) { + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { + let (id, ident) = match pat.kind { + PatKind::Binding(_, hir_id, ident, _) => (hir_id, ident), + _ => return, + }; + if ident.span.from_expansion() || ident.span.is_dummy() { return; } - check_fn(cx, decl, body); - } -} + let HirId { owner, local_id } = id; -fn check_fn<'tcx>(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>) { - let mut bindings = Vec::with_capacity(decl.inputs.len()); - for arg in iter_input_pats(decl, body) { - if let PatKind::Binding(.., ident, _) = arg.pat.kind { - bindings.push((ident.name, ident.span)); + // get (or insert) the list of items for this owner and symbol + let data = self.bindings.last_mut().unwrap(); + let items_with_name = data.entry(ident.name).or_default(); + + // check other bindings with the same name, most recently seen first + for &prev in items_with_name.iter().rev() { + if prev == local_id { + // repeated binding in an `Or` pattern + return; + } + + if is_shadow(cx, owner, prev, local_id) { + let prev_hir_id = HirId { owner, local_id: prev }; + lint_shadow(cx, pat, prev_hir_id, ident.span); + // only lint against the "nearest" shadowed binding + break; + } } + // store the binding + items_with_name.push(local_id); } - check_expr(cx, &body.value, &mut bindings); -} -fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Symbol, Span)>) { - let len = bindings.len(); - for stmt in block.stmts { - match stmt.kind { - StmtKind::Local(local) => check_local(cx, local, bindings), - StmtKind::Expr(e) | StmtKind::Semi(e) => check_expr(cx, e, bindings), - StmtKind::Item(..) => {}, + fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { + let hir = cx.tcx.hir(); + if !matches!(hir.body_owner_kind(hir.body_owner(body.id())), BodyOwnerKind::Closure) { + self.bindings.push(FxHashMap::default()); } } - if let Some(o) = block.expr { - check_expr(cx, o, bindings); - } - bindings.truncate(len); -} -fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Symbol, Span)>) { - if in_external_macro(cx.sess(), local.span) { - return; - } - if higher::is_from_for_desugar(local) { - return; - } - let Local { - pat, - ref ty, - ref init, - span, - .. - } = *local; - if let Some(t) = *ty { - check_ty(cx, t, bindings); - } - if let Some(o) = *init { - check_expr(cx, o, bindings); - check_pat(cx, pat, Some(o), span, bindings); - } else { - check_pat(cx, pat, None, span, bindings); + fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { + let hir = cx.tcx.hir(); + if !matches!(hir.body_owner_kind(hir.body_owner(body.id())), BodyOwnerKind::Closure) { + self.bindings.pop(); + } } } -fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool { - let var_ty = cx.typeck_results().node_type_opt(pat_id); - var_ty.map_or(false, |var_ty| !matches!(var_ty.kind(), ty::Adt(..))) +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); + scope_tree.is_subscope_of(second_scope, first_scope) } -fn check_pat<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - init: Option<&'tcx Expr<'_>>, - span: Span, - bindings: &mut Vec<(Symbol, Span)>, -) { - // TODO: match more stuff / destructuring - match pat.kind { - PatKind::Binding(.., ident, ref inner) => { - let name = ident.name; - if is_binding(cx, pat.hir_id) { - let mut new_binding = true; - for tup in bindings.iter_mut() { - if tup.0 == name { - lint_shadow(cx, name, span, pat.span, init, tup.1); - tup.1 = ident.span; - new_binding = false; - break; - } - } - if new_binding { - bindings.push((name, ident.span)); - } - } - if let Some(p) = *inner { - check_pat(cx, p, init, span, bindings); - } - }, - PatKind::Struct(_, pfields, _) => { - if let Some(init_struct) = init { - if let ExprKind::Struct(_, efields, _) = init_struct.kind { - for field in pfields { - let name = field.ident.name; - let efield = efields - .iter() - .find_map(|f| if f.ident.name == name { Some(&*f.expr) } else { None }); - check_pat(cx, field.pat, efield, span, bindings); - } - } else { - for field in pfields { - check_pat(cx, field.pat, init, span, bindings); - } - } - } else { - for field in pfields { - check_pat(cx, field.pat, None, span, bindings); - } - } +fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) { + let (lint, msg) = match find_init(cx, pat.hir_id) { + Some(expr) if is_self_shadow(cx, pat, expr, shadowed) => { + let msg = format!( + "`{}` is shadowed by itself in `{}`", + snippet(cx, pat.span, "_"), + snippet(cx, expr.span, "..") + ); + (SHADOW_SAME, msg) }, - PatKind::Tuple(inner, _) => { - if let Some(init_tup) = init { - if let ExprKind::Tup(tup) = init_tup.kind { - for (i, p) in inner.iter().enumerate() { - check_pat(cx, p, Some(&tup[i]), p.span, bindings); - } - } else { - for p in inner { - check_pat(cx, p, init, span, bindings); - } - } - } else { - for p in inner { - check_pat(cx, p, None, span, bindings); - } - } + Some(expr) if is_local_used(cx, expr, shadowed) => { + let msg = format!( + "`{}` is shadowed by `{}` which reuses the original value", + snippet(cx, pat.span, "_"), + snippet(cx, expr.span, "..") + ); + (SHADOW_REUSE, msg) }, - PatKind::Box(inner) => { - if let Some(initp) = init { - if let ExprKind::Box(inner_init) = initp.kind { - check_pat(cx, inner, Some(inner_init), span, bindings); - } else { - check_pat(cx, inner, init, span, bindings); - } - } else { - check_pat(cx, inner, init, span, bindings); - } + _ => { + let msg = format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_")); + (SHADOW_UNRELATED, msg) }, - PatKind::Ref(inner, _) => check_pat(cx, inner, init, span, bindings), - // PatVec(Vec>, Option>, Vec>), - _ => (), - } + }; + span_lint_and_note( + cx, + lint, + span, + &msg, + Some(cx.tcx.hir().span(shadowed)), + "previous binding is here", + ); } -fn lint_shadow<'tcx>( - cx: &LateContext<'tcx>, - name: Symbol, - span: Span, - pattern_span: Span, - init: Option<&'tcx Expr<'_>>, - prev_span: Span, -) { - if let Some(expr) = init { - if is_self_shadow(name, expr) { - span_lint_and_then( - cx, - SHADOW_SAME, - span, - &format!( - "`{}` is shadowed by itself in `{}`", - snippet(cx, pattern_span, "_"), - snippet(cx, expr.span, "..") - ), - |diag| { - diag.span_note(prev_span, "previous binding is here"); - }, - ); - } else if contains_name(name, expr) { - span_lint_and_then( - cx, - SHADOW_REUSE, - pattern_span, - &format!( - "`{}` is shadowed by `{}` which reuses the original value", - snippet(cx, pattern_span, "_"), - snippet(cx, expr.span, "..") - ), - |diag| { - diag.span_note(expr.span, "initialization happens here"); - diag.span_note(prev_span, "previous binding is here"); - }, - ); - } else { - span_lint_and_then( - cx, - SHADOW_UNRELATED, - pattern_span, - &format!("`{}` is being shadowed", snippet(cx, pattern_span, "_")), - |diag| { - diag.span_note(expr.span, "initialization happens here"); - diag.span_note(prev_span, "previous binding is here"); +/// Returns true if the expression is a simple transformation of a local binding such as `&x` +fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_id: HirId) -> bool { + let hir = cx.tcx.hir(); + let is_direct_binding = hir + .parent_iter(pat.hir_id) + .map_while(|(_id, node)| match node { + Node::Pat(pat) => Some(pat), + _ => None, + }) + .all(|pat| matches!(pat.kind, PatKind::Ref(..) | PatKind::Or(_))); + if !is_direct_binding { + return false; + } + loop { + expr = match expr.kind { + ExprKind::Box(e) + | ExprKind::AddrOf(_, _, e) + | ExprKind::Block( + &Block { + stmts: [], + expr: Some(e), + .. }, - ); + _, + ) + | ExprKind::Unary(UnOp::Deref, e) => e, + ExprKind::Path(QPath::Resolved(None, path)) => break path.res == Res::Local(hir_id), + _ => break false, } - } else { - span_lint_and_then( - cx, - SHADOW_UNRELATED, - span, - &format!("`{}` shadows a previous declaration", snippet(cx, pattern_span, "_")), - |diag| { - diag.span_note(prev_span, "previous binding is here"); - }, - ); } } -fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Symbol, Span)>) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - match expr.kind { - ExprKind::Unary(_, e) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Box(e) => { - check_expr(cx, e, bindings); - }, - ExprKind::Block(block, _) | ExprKind::Loop(block, ..) => check_block(cx, block, bindings), - // ExprKind::Call - // ExprKind::MethodCall - ExprKind::Array(v) | ExprKind::Tup(v) => { - for e in v { - check_expr(cx, e, bindings); - } - }, - ExprKind::If(cond, then, ref otherwise) => { - check_expr(cx, cond, bindings); - check_expr(cx, then, bindings); - if let Some(o) = *otherwise { - check_expr(cx, o, bindings); - } - }, - ExprKind::Match(init, arms, _) => { - check_expr(cx, init, bindings); - let len = bindings.len(); - for arm in arms { - check_pat(cx, arm.pat, Some(init), arm.pat.span, bindings); - // This is ugly, but needed to get the right type - if let Some(ref guard) = arm.guard { - match guard { - Guard::If(if_expr) => check_expr(cx, if_expr, bindings), - Guard::IfLet(guard_pat, guard_expr) => { - check_pat(cx, guard_pat, Some(*guard_expr), guard_pat.span, bindings); - check_expr(cx, guard_expr, bindings); - }, - } - } - check_expr(cx, arm.body, bindings); - bindings.truncate(len); - } - }, - _ => (), - } -} - -fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) { - match ty.kind { - TyKind::Slice(sty) => check_ty(cx, sty, bindings), - TyKind::Array(fty, ref anon_const) => { - check_ty(cx, fty, bindings); - check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings); - }, - TyKind::Ptr(MutTy { ty: mty, .. }) | TyKind::Rptr(_, MutTy { ty: mty, .. }) => check_ty(cx, mty, bindings), - TyKind::Tup(tup) => { - for t in tup { - check_ty(cx, t, bindings); - } - }, - TyKind::Typeof(ref anon_const) => check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings), - _ => (), - } -} - -fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool { - match expr.kind { - ExprKind::Box(inner) | ExprKind::AddrOf(_, _, inner) => is_self_shadow(name, inner), - ExprKind::Block(block, _) => { - block.stmts.is_empty() && block.expr.as_ref().map_or(false, |e| is_self_shadow(name, e)) - }, - ExprKind::Unary(op, inner) => (UnOp::Deref == op) && is_self_shadow(name, inner), - ExprKind::Path(QPath::Resolved(_, path)) => path_eq_name(name, path), - _ => false, +/// Finds the "init" expression for a pattern: `let = ;` or +/// `match { .., => .., .. }` +fn find_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { + for (_, node) in cx.tcx.hir().parent_iter(hir_id) { + let init = match node { + Node::Arm(_) | Node::Pat(_) => continue, + Node::Expr(expr) => match expr.kind { + ExprKind::Match(e, _, _) => Some(e), + _ => None, + }, + Node::Local(local) => local.init, + _ => None, + }; + return init; } -} - -fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool { - !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.name == name + None } diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index a67fa7922059c..ef80663d1da41 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -49,8 +49,8 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { let mut const_eval_context = constant_context(cx, cx.typeck_results()); if_chain! { if let ExprKind::Path(ref _qpath) = arg.kind; - let x = const_eval_context.expr(arg); - if let Some(Constant::RawPtr(0)) = x; + if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg); + if x == 0; then { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) } diff --git a/clippy_lints/src/types/option_option.rs b/clippy_lints/src/types/option_option.rs index 4f50284e941ab..903e62995c617 100644 --- a/clippy_lints/src/types/option_option.rs +++ b/clippy_lints/src/types/option_option.rs @@ -7,9 +7,7 @@ use rustc_span::symbol::sym; use super::OPTION_OPTION; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { - if cx.tcx.is_diagnostic_item(sym::Option, def_id) - && is_ty_param_diagnostic_item(cx, qpath, sym::Option).is_some() - { + if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_ty_param_diagnostic_item(cx, qpath, sym::Option).is_some() { span_lint( cx, OPTION_OPTION, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 1e0447239be99..6cbada4c1505b 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -284,6 +284,10 @@ define_Conf! { /// /// The list of unicode scripts allowed to be used in the scope. (allowed_scripts: Vec = vec!["Latin".to_string()]), + /// Lint: NON_SEND_FIELDS_IN_SEND_TY. + /// + /// Whether to apply the raw pointer heuristic to determine if a type is `Send`. + (enable_raw_pointer_heuristic_for_send: bool = true), } /// Search for the configuration file. diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 59c40050522b2..9f9edbf258ac2 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -8,7 +8,7 @@ use clippy_utils::{ paths, SpanlessEq, }; use if_chain::if_chain; -use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId}; +use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId}; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; @@ -18,8 +18,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{ - BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, - TyKind, UnOp, + BinOpKind, Block, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, TyKind, + UnOp, }; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -317,7 +317,7 @@ declare_clippy_lint! { declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { - fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { @@ -412,7 +412,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { } } - fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) { + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) { return; } @@ -907,7 +907,7 @@ pub struct InterningDefinedSymbol { impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { - fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + fn check_crate(&mut self, cx: &LateContext<'_>) { if !self.symbol_map.is_empty() { return; } diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index b29ced28ac491..e0746ce4d8121 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -32,7 +32,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { // check for instances of 0.0/0.0 if_chain! { if let ExprKind::Binary(ref op, left, right) = expr.kind; - if let BinOpKind::Div = op.node; + if op.node == BinOpKind::Div; // TODO - constant_simple does not fold many operations involving floats. // That's probably fine for this lint - it's pretty unlikely that someone would // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 00123fdba2490..c47aa9170e547 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -509,7 +509,6 @@ pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool { } /// Gets the definition associated to a path. -#[allow(clippy::shadow_unrelated)] // false positive #6563 pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res { macro_rules! try_res { ($e:expr) => { @@ -683,7 +682,17 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { _ => false, }, ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)), - ExprKind::Repeat(x, _) => is_default_equivalent(cx, x), + ExprKind::Repeat(x, y) => if_chain! { + if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(y.body).value.kind; + if let LitKind::Int(v, _) = const_lit.node; + if v <= 32 && is_default_equivalent(cx, x); + then { + true + } + else { + false + } + }, ExprKind::Call(repl_func, _) => if_chain! { if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); @@ -1498,7 +1507,7 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc if let ExprKind::Match(_, arms, ref source) = expr.kind { // desugared from a `?` operator - if let MatchSource::TryDesugar = *source { + if *source == MatchSource::TryDesugar { return Some(expr); } diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 4a28c7dd9a04a..68dd1b29845a5 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -74,7 +74,7 @@ impl<'a> NumericLiteral<'a> { }; // Grab part of the literal after prefix, if present. - let (prefix, mut sans_prefix) = if let Radix::Decimal = radix { + let (prefix, mut sans_prefix) = if radix == Radix::Decimal { (None, lit) } else { let (p, s) = lit.split_at(2); @@ -157,8 +157,10 @@ impl<'a> NumericLiteral<'a> { } if let Some((separator, exponent)) = self.exponent { - output.push_str(separator); - Self::group_digits(&mut output, exponent, group_size, true, false); + if exponent != "0" { + output.push_str(separator); + Self::group_digits(&mut output, exponent, group_size, true, false); + } } if let Some(suffix) = self.suffix { @@ -177,6 +179,13 @@ impl<'a> NumericLiteral<'a> { let mut digits = input.chars().filter(|&c| c != '_'); + // The exponent may have a sign, output it early, otherwise it will be + // treated as a digit + if digits.clone().next() == Some('-') { + let _ = digits.next(); + output.push('-'); + } + let first_group_size; if partial_group_first { diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 5e0182ec1b8ea..e43c575602145 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -129,11 +129,17 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; /// Preferably use the diagnostic item `sym::Result` where possible pub const RESULT: [&str; 3] = ["core", "result", "Result"]; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index ab05a0b423853..5b0efb1fd7132 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -311,7 +311,7 @@ impl<'a> Sugg<'a> { /// Return `true` if `sugg` is enclosed in parenthesis. fn has_enclosing_paren(sugg: impl AsRef) -> bool { let mut chars = sugg.as_ref().chars(); - if let Some('(') = chars.next() { + if chars.next() == Some('(') { let mut depth = 1; for c in &mut chars { if c == '(' { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 96cb8a4dae43e..6ebe1a0028a31 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -224,15 +224,15 @@ fn is_normalizable_helper<'tcx>( result } -/// Returns true iff the given type is a non aggregate primitive (a bool or char, any integer or -/// floating-point number type). For checking aggregation of primitive types (e.g. tuples and slices -/// of primitive type) see `is_recursively_primitive_type` +/// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any +/// integer or floating-point number type). For checking aggregation of primitive types (e.g. +/// tuples and slices of primitive type) see `is_recursively_primitive_type` pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool { matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_)) } -/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point -/// number type, a str, or an array, slice, or tuple of those types). +/// Returns `true` if the given type is a primitive (a `bool` or `char`, any integer or +/// floating-point number type, a `str`, or an array, slice, or tuple of those types). pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { match ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 098ec175fe2d9..34206b5ae2b21 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -65,7 +65,7 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) { - if let ty::BorrowKind::MutBorrow = bk { + if bk == ty::BorrowKind::MutBorrow { self.update(cmt); } } diff --git a/rust-toolchain b/rust-toolchain index 660401ff28c5d..f98819303e682 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-09-28" +channel = "nightly-2021-10-07" components = ["llvm-tools-preview", "rustc-dev", "rust-src"] diff --git a/tests/compile-test.rs b/tests/compile-test.rs index d7596f6ff0cae..e8b1640c8693e 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -92,7 +92,9 @@ fn extern_flags() -> String { .collect(); assert!( not_found.is_empty(), - "dependencies not found in depinfo: {:?}", + "dependencies not found in depinfo: {:?}\n\ + help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\ + help: Try adding to dev-dependencies in Cargo.toml", not_found ); crates diff --git a/tests/ui-toml/strict_non_send_fields_in_send_ty/clippy.toml b/tests/ui-toml/strict_non_send_fields_in_send_ty/clippy.toml new file mode 100644 index 0000000000000..a942709d14acc --- /dev/null +++ b/tests/ui-toml/strict_non_send_fields_in_send_ty/clippy.toml @@ -0,0 +1 @@ +enable-raw-pointer-heuristic-for-send = false diff --git a/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs b/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs new file mode 100644 index 0000000000000..90c2439dc34f4 --- /dev/null +++ b/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs @@ -0,0 +1,43 @@ +#![warn(clippy::non_send_fields_in_send_ty)] +#![feature(extern_types)] + +use std::rc::Rc; + +// Basic tests should not be affected +pub struct NoGeneric { + rc_is_not_send: Rc, +} + +unsafe impl Send for NoGeneric {} + +pub struct MultiField { + field1: T, + field2: T, + field3: T, +} + +unsafe impl Send for MultiField {} + +pub enum MyOption { + MySome(T), + MyNone, +} + +unsafe impl Send for MyOption {} + +// All fields are disallowed when raw pointer heuristic is off +extern "C" { + type NonSend; +} + +pub struct HeuristicTest { + field1: Vec<*const NonSend>, + field2: [*const NonSend; 3], + field3: (*const NonSend, *const NonSend, *const NonSend), + field4: (*const NonSend, Rc), + field5: Vec>, +} + +unsafe impl Send for HeuristicTest {} + +fn main() {} diff --git a/tests/ui-toml/strict_non_send_fields_in_send_ty/test.stderr b/tests/ui-toml/strict_non_send_fields_in_send_ty/test.stderr new file mode 100644 index 0000000000000..b07f9dd3df30e --- /dev/null +++ b/tests/ui-toml/strict_non_send_fields_in_send_ty/test.stderr @@ -0,0 +1,91 @@ +error: this implementation is unsound, as some fields in `NoGeneric` are `!Send` + --> $DIR/test.rs:11:1 + | +LL | unsafe impl Send for NoGeneric {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::non-send-fields-in-send-ty` implied by `-D warnings` +note: the type of field `rc_is_not_send` is `!Send` + --> $DIR/test.rs:8:5 + | +LL | rc_is_not_send: Rc, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` + +error: this implementation is unsound, as some fields in `MultiField` are `!Send` + --> $DIR/test.rs:19:1 + | +LL | unsafe impl Send for MultiField {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `field1` is `!Send` + --> $DIR/test.rs:14:5 + | +LL | field1: T, + | ^^^^^^^^^ + = help: add `T: Send` bound in `Send` impl +note: the type of field `field2` is `!Send` + --> $DIR/test.rs:15:5 + | +LL | field2: T, + | ^^^^^^^^^ + = help: add `T: Send` bound in `Send` impl +note: the type of field `field3` is `!Send` + --> $DIR/test.rs:16:5 + | +LL | field3: T, + | ^^^^^^^^^ + = help: add `T: Send` bound in `Send` impl + +error: this implementation is unsound, as some fields in `MyOption` are `!Send` + --> $DIR/test.rs:26:1 + | +LL | unsafe impl Send for MyOption {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `0` is `!Send` + --> $DIR/test.rs:22:12 + | +LL | MySome(T), + | ^ + = help: add `T: Send` bound in `Send` impl + +error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send` + --> $DIR/test.rs:41:1 + | +LL | unsafe impl Send for HeuristicTest {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `field1` is `!Send` + --> $DIR/test.rs:34:5 + | +LL | field1: Vec<*const NonSend>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` +note: the type of field `field2` is `!Send` + --> $DIR/test.rs:35:5 + | +LL | field2: [*const NonSend; 3], + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` +note: the type of field `field3` is `!Send` + --> $DIR/test.rs:36:5 + | +LL | field3: (*const NonSend, *const NonSend, *const NonSend), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` +note: the type of field `field4` is `!Send` + --> $DIR/test.rs:37:5 + | +LL | field4: (*const NonSend, Rc), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` +note: the type of field `field5` is `!Send` + --> $DIR/test.rs:38:5 + | +LL | field5: Vec>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` + +error: aborting due to 4 previous errors + 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 e0029ebeb27ac..97bab1308aa52 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`, `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`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/approx_const.rs b/tests/ui/approx_const.rs index 2ae4d613507ed..ccdbd34f7ec7b 100644 --- a/tests/ui/approx_const.rs +++ b/tests/ui/approx_const.rs @@ -1,5 +1,5 @@ #[warn(clippy::approx_constant)] -#[allow(unused, clippy::shadow_unrelated, clippy::similar_names)] +#[allow(clippy::similar_names)] fn main() { let my_e = 2.7182; let almost_e = 2.718; diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.rs b/tests/ui/branches_sharing_code/shared_at_bottom.rs index ce2040bdeb82d..12f550d9c9a80 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_bottom.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] +#![allow(dead_code, clippy::equatable_if_let)] #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] // This tests the branches_sharing_code lint at the end of blocks diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index c69a46f0a77ee..bb6c4c0703d51 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(clippy::assertions_on_constants)] +#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)] #[rustfmt::skip] #[warn(clippy::collapsible_if)] diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 1359c7eb6278e..6d4f688db8c0a 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(clippy::assertions_on_constants)] +#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)] #[rustfmt::skip] #[warn(clippy::collapsible_if)] diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index e4c088bf6f03f..5b0e4a473c4ad 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(clippy::assertions_on_constants)] +#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)] #[rustfmt::skip] #[warn(clippy::collapsible_if)] diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index d6cf01c831940..cd231a5d7abb0 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(clippy::assertions_on_constants)] +#![allow(clippy::assertions_on_constants, clippy::equatable_if_let)] #[rustfmt::skip] #[warn(clippy::collapsible_if)] diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 4ce365cc7649a..603ae7dc9eb11 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -1,5 +1,10 @@ #![warn(clippy::collapsible_match)] -#![allow(clippy::needless_return, clippy::no_effect, clippy::single_match)] +#![allow( + clippy::needless_return, + clippy::no_effect, + clippy::single_match, + clippy::equatable_if_let +)] fn lint_cases(opt_opt: Option>, res_opt: Result, String>) { // match without block diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index c119570e8abd8..5f18b69350295 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -1,5 +1,5 @@ error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:7:20 + --> $DIR/collapsible_match.rs:12:20 | LL | Ok(val) => match val { | ____________________^ @@ -10,7 +10,7 @@ LL | | }, | = note: `-D clippy::collapsible-match` implied by `-D warnings` help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:7:12 + --> $DIR/collapsible_match.rs:12:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -18,7 +18,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:16:20 + --> $DIR/collapsible_match.rs:21:20 | LL | Ok(val) => match val { | ____________________^ @@ -28,7 +28,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:16:12 + --> $DIR/collapsible_match.rs:21:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -36,7 +36,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:25:9 + --> $DIR/collapsible_match.rs:30:9 | LL | / if let Some(n) = val { LL | | take(n); @@ -44,7 +44,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:24:15 + --> $DIR/collapsible_match.rs:29:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -52,7 +52,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:32:9 + --> $DIR/collapsible_match.rs:37:9 | LL | / if let Some(n) = val { LL | | take(n); @@ -62,7 +62,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:31:15 + --> $DIR/collapsible_match.rs:36:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -70,7 +70,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:43:9 + --> $DIR/collapsible_match.rs:48:9 | LL | / match val { LL | | Some(n) => foo(n), @@ -79,7 +79,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:42:15 + --> $DIR/collapsible_match.rs:47:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -88,7 +88,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:52:13 + --> $DIR/collapsible_match.rs:57:13 | LL | / if let Some(n) = val { LL | | take(n); @@ -96,7 +96,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:51:12 + --> $DIR/collapsible_match.rs:56:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -104,7 +104,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:61:9 + --> $DIR/collapsible_match.rs:66:9 | LL | / match val { LL | | Some(n) => foo(n), @@ -113,7 +113,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:60:15 + --> $DIR/collapsible_match.rs:65:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -122,7 +122,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:72:13 + --> $DIR/collapsible_match.rs:77:13 | LL | / if let Some(n) = val { LL | | take(n); @@ -132,7 +132,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:71:12 + --> $DIR/collapsible_match.rs:76:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -140,7 +140,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:83:20 + --> $DIR/collapsible_match.rs:88:20 | LL | Ok(val) => match val { | ____________________^ @@ -150,7 +150,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:83:12 + --> $DIR/collapsible_match.rs:88:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -158,7 +158,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:92:22 + --> $DIR/collapsible_match.rs:97:22 | LL | Some(val) => match val { | ______________________^ @@ -168,7 +168,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:92:14 + --> $DIR/collapsible_match.rs:97:14 | LL | Some(val) => match val { | ^^^ replace this binding diff --git a/tests/ui/crashes/ice-3462.rs b/tests/ui/crashes/ice-3462.rs index 7d62e315da2fc..02c49aa0d7c1f 100644 --- a/tests/ui/crashes/ice-3462.rs +++ b/tests/ui/crashes/ice-3462.rs @@ -1,5 +1,5 @@ #![warn(clippy::all)] -#![allow(clippy::blacklisted_name)] +#![allow(clippy::blacklisted_name, clippy::equatable_if_let)] #![allow(unused)] /// Test for https://github.com/rust-lang/rust-clippy/issues/3462 diff --git a/tests/ui/def_id_nocore.rs b/tests/ui/def_id_nocore.rs index 1ed78547a60cd..156c88e2e45b7 100644 --- a/tests/ui/def_id_nocore.rs +++ b/tests/ui/def_id_nocore.rs @@ -3,6 +3,7 @@ #![feature(no_core, lang_items, start)] #![no_core] +#![allow(clippy::missing_safety_doc)] #[link(name = "c")] extern "C" {} diff --git a/tests/ui/def_id_nocore.stderr b/tests/ui/def_id_nocore.stderr index 6210d7c6cfd80..40d355e9a2e3e 100644 --- a/tests/ui/def_id_nocore.stderr +++ b/tests/ui/def_id_nocore.stderr @@ -1,5 +1,5 @@ error: methods called `as_*` usually take `self` by reference or `self` by mutable reference - --> $DIR/def_id_nocore.rs:27:19 + --> $DIR/def_id_nocore.rs:28:19 | LL | pub fn as_ref(self) -> &'static str { | ^^^^ diff --git a/tests/ui/derivable_impls.rs b/tests/ui/derivable_impls.rs index ebbc0c77e3265..a6412004726d3 100644 --- a/tests/ui/derivable_impls.rs +++ b/tests/ui/derivable_impls.rs @@ -207,4 +207,37 @@ impl Default for Color2 { } } +pub struct RepeatDefault1 { + a: [i8; 32], +} + +impl Default for RepeatDefault1 { + fn default() -> Self { + RepeatDefault1 { a: [0; 32] } + } +} + +pub struct RepeatDefault2 { + a: [i8; 33], +} + +impl Default for RepeatDefault2 { + fn default() -> Self { + RepeatDefault2 { a: [0; 33] } + } +} + +// https://github.com/rust-lang/rust-clippy/issues/7753 + +pub enum IntOrString { + Int(i32), + String(String), +} + +impl Default for IntOrString { + fn default() -> Self { + IntOrString::Int(0) + } +} + fn main() {} diff --git a/tests/ui/derivable_impls.stderr b/tests/ui/derivable_impls.stderr index 4ed64fade026d..49fb471a21962 100644 --- a/tests/ui/derivable_impls.stderr +++ b/tests/ui/derivable_impls.stderr @@ -73,5 +73,17 @@ LL | | } | = help: try annotating `WithoutSelfParan` with `#[derive(Default)]` -error: aborting due to 6 previous errors +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:214:1 + | +LL | / impl Default for RepeatDefault1 { +LL | | fn default() -> Self { +LL | | RepeatDefault1 { a: [0; 32] } +LL | | } +LL | | } + | |_^ + | + = help: try annotating `RepeatDefault1` with `#[derive(Default)]` + +error: aborting due to 7 previous errors diff --git a/tests/ui/doc/doc.rs b/tests/ui/doc/doc.rs index 8b0c0f304fce0..342208e52b8e9 100644 --- a/tests/ui/doc/doc.rs +++ b/tests/ui/doc/doc.rs @@ -203,6 +203,11 @@ fn issue_2343() {} /// __|_ _|__||_| fn pulldown_cmark_crash() {} +/// This should not lint +/// (regression test for #7758) +/// [plain text][path::to::item] +fn intra_doc_link() {} + // issue #7033 - generic_const_exprs ICE struct S where [(); N.checked_next_power_of_two().unwrap()]: { diff --git a/tests/ui/doc_unsafe.rs b/tests/ui/doc_unsafe.rs index 484aa72d59a25..8f823f1672ba2 100644 --- a/tests/ui/doc_unsafe.rs +++ b/tests/ui/doc_unsafe.rs @@ -34,16 +34,25 @@ mod private_mod { pub use private_mod::republished; -pub trait UnsafeTrait { +pub trait SafeTraitUnsafeMethods { unsafe fn woefully_underdocumented(self); /// # Safety unsafe fn at_least_somewhat_documented(self); } +pub unsafe trait UnsafeTrait { + fn method(); +} + +/// # Safety +pub unsafe trait DocumentedUnsafeTrait { + fn method2(); +} + pub struct Struct; -impl UnsafeTrait for Struct { +impl SafeTraitUnsafeMethods for Struct { unsafe fn woefully_underdocumented(self) { // all is well } @@ -53,6 +62,14 @@ impl UnsafeTrait for Struct { } } +unsafe impl UnsafeTrait for Struct { + fn method() {} +} + +unsafe impl DocumentedUnsafeTrait for Struct { + fn method2() {} +} + impl Struct { pub unsafe fn more_undocumented_unsafe() -> Self { unimplemented!(); diff --git a/tests/ui/doc_unsafe.stderr b/tests/ui/doc_unsafe.stderr index 73b53f3431e7a..34ca37a6efdc0 100644 --- a/tests/ui/doc_unsafe.stderr +++ b/tests/ui/doc_unsafe.stderr @@ -22,8 +22,16 @@ error: unsafe function's docs miss `# Safety` section LL | unsafe fn woefully_underdocumented(self); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: docs for unsafe trait missing `# Safety` section + --> $DIR/doc_unsafe.rs:44:1 + | +LL | / pub unsafe trait UnsafeTrait { +LL | | fn method(); +LL | | } + | |_^ + error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:57:5 + --> $DIR/doc_unsafe.rs:74:5 | LL | / pub unsafe fn more_undocumented_unsafe() -> Self { LL | | unimplemented!(); @@ -31,7 +39,7 @@ LL | | } | |_____^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:73:9 + --> $DIR/doc_unsafe.rs:90:9 | LL | / pub unsafe fn whee() { LL | | unimplemented!() @@ -43,5 +51,5 @@ LL | very_unsafe!(); | = note: this error originates in the macro `very_unsafe` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed new file mode 100644 index 0000000000000..ba72cc237b4a5 --- /dev/null +++ b/tests/ui/equatable_if_let.fixed @@ -0,0 +1,69 @@ +// run-rustfix + +#![allow(unused_variables, dead_code)] +#![warn(clippy::equatable_if_let)] + +use std::cmp::Ordering; + +#[derive(PartialEq)] +enum Enum { + TupleVariant(i32, u64), + RecordVariant { a: i64, b: u32 }, + UnitVariant, + Recursive(Struct), +} + +#[derive(PartialEq)] +struct Struct { + a: i32, + b: bool, +} + +enum NotPartialEq { + A, + B, +} + +enum NotStructuralEq { + A, + B, +} + +impl PartialEq for NotStructuralEq { + fn eq(&self, _: &NotStructuralEq) -> bool { + false + } +} + +fn main() { + let a = 2; + let b = 3; + let c = Some(2); + let d = Struct { a: 2, b: false }; + let e = Enum::UnitVariant; + let f = NotPartialEq::A; + let g = NotStructuralEq::A; + + // true + + if a == 2 {} + if a.cmp(&b) == Ordering::Greater {} + if c == Some(2) {} + if d == (Struct { a: 2, b: false }) {} + if e == Enum::TupleVariant(32, 64) {} + if e == (Enum::RecordVariant { a: 64, b: 32 }) {} + if e == Enum::UnitVariant {} + if (e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false }) {} + + // false + + if let 2 | 3 = a {} + if let x @ 2 = a {} + if let Some(3 | 4) = c {} + if let Struct { a, b: false } = d {} + if let Struct { a: 2, b: x } = d {} + if let NotPartialEq::A = f {} + if g == NotStructuralEq::A {} + if let Some(NotPartialEq::A) = Some(f) {} + if Some(g) == Some(NotStructuralEq::A) {} +} diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs new file mode 100644 index 0000000000000..12526ca193db6 --- /dev/null +++ b/tests/ui/equatable_if_let.rs @@ -0,0 +1,69 @@ +// run-rustfix + +#![allow(unused_variables, dead_code)] +#![warn(clippy::equatable_if_let)] + +use std::cmp::Ordering; + +#[derive(PartialEq)] +enum Enum { + TupleVariant(i32, u64), + RecordVariant { a: i64, b: u32 }, + UnitVariant, + Recursive(Struct), +} + +#[derive(PartialEq)] +struct Struct { + a: i32, + b: bool, +} + +enum NotPartialEq { + A, + B, +} + +enum NotStructuralEq { + A, + B, +} + +impl PartialEq for NotStructuralEq { + fn eq(&self, _: &NotStructuralEq) -> bool { + false + } +} + +fn main() { + let a = 2; + let b = 3; + let c = Some(2); + let d = Struct { a: 2, b: false }; + let e = Enum::UnitVariant; + let f = NotPartialEq::A; + let g = NotStructuralEq::A; + + // true + + if let 2 = a {} + if let Ordering::Greater = a.cmp(&b) {} + if let Some(2) = c {} + if let Struct { a: 2, b: false } = d {} + if let Enum::TupleVariant(32, 64) = e {} + if let Enum::RecordVariant { a: 64, b: 32 } = e {} + if let Enum::UnitVariant = e {} + if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {} + + // false + + if let 2 | 3 = a {} + if let x @ 2 = a {} + if let Some(3 | 4) = c {} + if let Struct { a, b: false } = d {} + if let Struct { a: 2, b: x } = d {} + if let NotPartialEq::A = f {} + if let NotStructuralEq::A = g {} + if let Some(NotPartialEq::A) = Some(f) {} + if let Some(NotStructuralEq::A) = Some(g) {} +} diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr new file mode 100644 index 0000000000000..79ef919384df2 --- /dev/null +++ b/tests/ui/equatable_if_let.stderr @@ -0,0 +1,64 @@ +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:49:8 + | +LL | if let 2 = a {} + | ^^^^^^^^^ help: try: `a == 2` + | + = note: `-D clippy::equatable-if-let` implied by `-D warnings` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:50:8 + | +LL | if let Ordering::Greater = a.cmp(&b) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:51:8 + | +LL | if let Some(2) = c {} + | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:52:8 + | +LL | if let Struct { a: 2, b: false } = d {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:53:8 + | +LL | if let Enum::TupleVariant(32, 64) = e {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:54:8 + | +LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:55:8 + | +LL | if let Enum::UnitVariant = e {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:56:8 + | +LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:66:8 + | +LL | if let NotStructuralEq::A = g {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:68:8 + | +LL | if let Some(NotStructuralEq::A) = Some(g) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)` + +error: aborting due to 10 previous errors + diff --git a/tests/ui/excessive_precision.fixed b/tests/ui/excessive_precision.fixed index 90376620a9fd8..b74bda182be9b 100644 --- a/tests/ui/excessive_precision.fixed +++ b/tests/ui/excessive_precision.fixed @@ -60,4 +60,10 @@ fn main() { // issue #2840 let num = 0.000_000_000_01e-10f64; + + // issue #7744 + let _ = 2.225_073_858_507_201e-308_f64; + + // issue #7745 + let _ = 0_f64; } diff --git a/tests/ui/excessive_precision.rs b/tests/ui/excessive_precision.rs index ce4722a90f900..6e84a71f24cb6 100644 --- a/tests/ui/excessive_precision.rs +++ b/tests/ui/excessive_precision.rs @@ -60,4 +60,10 @@ fn main() { // issue #2840 let num = 0.000_000_000_01e-10f64; + + // issue #7744 + let _ = 2.225_073_858_507_201_1e-308_f64; + + // issue #7745 + let _ = 1.000_000_000_000_001e-324_f64; } diff --git a/tests/ui/excessive_precision.stderr b/tests/ui/excessive_precision.stderr index e59c20c30b4fd..42d9d4de193c4 100644 --- a/tests/ui/excessive_precision.stderr +++ b/tests/ui/excessive_precision.stderr @@ -78,5 +78,17 @@ error: float has excessive precision LL | let bad_bige32: f32 = 1.123_456_788_888E-10; | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10` -error: aborting due to 13 previous errors +error: float has excessive precision + --> $DIR/excessive_precision.rs:65:13 + | +LL | let _ = 2.225_073_858_507_201_1e-308_f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `2.225_073_858_507_201e-308_f64` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:68:13 + | +LL | let _ = 1.000_000_000_000_001e-324_f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0_f64` + +error: aborting due to 15 previous errors diff --git a/tests/ui/for_loop_fixable.fixed b/tests/ui/for_loop_fixable.fixed index f0e4835415f30..f373e905d05ca 100644 --- a/tests/ui/for_loop_fixable.fixed +++ b/tests/ui/for_loop_fixable.fixed @@ -23,12 +23,7 @@ impl Unrelated { clippy::iter_next_loop, clippy::for_kv_map )] -#[allow( - clippy::linkedlist, - clippy::shadow_unrelated, - clippy::unnecessary_mut_passed, - clippy::similar_names -)] +#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] #[allow(unused_variables)] fn main() { let mut vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/for_loop_fixable.rs b/tests/ui/for_loop_fixable.rs index 1edef175fb983..3814583bb6ef4 100644 --- a/tests/ui/for_loop_fixable.rs +++ b/tests/ui/for_loop_fixable.rs @@ -23,12 +23,7 @@ impl Unrelated { clippy::iter_next_loop, clippy::for_kv_map )] -#[allow( - clippy::linkedlist, - clippy::shadow_unrelated, - clippy::unnecessary_mut_passed, - clippy::similar_names -)] +#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] #[allow(unused_variables)] fn main() { let mut vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/for_loop_fixable.stderr b/tests/ui/for_loop_fixable.stderr index ddfe66d675f91..009dbe1a0bfaf 100644 --- a/tests/ui/for_loop_fixable.stderr +++ b/tests/ui/for_loop_fixable.stderr @@ -1,5 +1,5 @@ error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:43:15 + --> $DIR/for_loop_fixable.rs:38:15 | LL | for _v in vec.iter() {} | ^^^^^^^^^^ help: to write this more concisely, try: `&vec` @@ -7,13 +7,13 @@ LL | for _v in vec.iter() {} = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:45:15 + --> $DIR/for_loop_fixable.rs:40:15 | LL | for _v in vec.iter_mut() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec` error: it is more concise to loop over containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:48:15 + --> $DIR/for_loop_fixable.rs:43:15 | LL | for _v in out_vec.into_iter() {} | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec` @@ -21,73 +21,73 @@ LL | for _v in out_vec.into_iter() {} = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:53:15 + --> $DIR/for_loop_fixable.rs:48:15 | LL | for _v in [1, 2, 3].iter() {} | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:57:15 + --> $DIR/for_loop_fixable.rs:52:15 | LL | for _v in [0; 32].iter() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:62:15 + --> $DIR/for_loop_fixable.rs:57:15 | LL | for _v in ll.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&ll` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:65:15 + --> $DIR/for_loop_fixable.rs:60:15 | LL | for _v in vd.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&vd` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:68:15 + --> $DIR/for_loop_fixable.rs:63:15 | LL | for _v in bh.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bh` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:71:15 + --> $DIR/for_loop_fixable.rs:66:15 | LL | for _v in hm.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&hm` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:74:15 + --> $DIR/for_loop_fixable.rs:69:15 | LL | for _v in bt.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bt` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:77:15 + --> $DIR/for_loop_fixable.rs:72:15 | LL | for _v in hs.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&hs` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:80:15 + --> $DIR/for_loop_fixable.rs:75:15 | LL | for _v in bs.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bs` error: it is more concise to loop over containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:255:18 + --> $DIR/for_loop_fixable.rs:250:18 | LL | for i in iterator.into_iter() { | ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:275:18 + --> $DIR/for_loop_fixable.rs:270:18 | LL | for _ in t.into_iter() {} | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t` error: it is more concise to loop over containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:277:18 + --> $DIR/for_loop_fixable.rs:272:18 | LL | for _ in r.into_iter() {} | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r` diff --git a/tests/ui/for_loop_unfixable.rs b/tests/ui/for_loop_unfixable.rs index e73536052f0f5..efcaffce24ea4 100644 --- a/tests/ui/for_loop_unfixable.rs +++ b/tests/ui/for_loop_unfixable.rs @@ -7,14 +7,7 @@ clippy::iter_next_loop, clippy::for_kv_map )] -#[allow( - clippy::linkedlist, - clippy::shadow_unrelated, - clippy::unnecessary_mut_passed, - clippy::similar_names, - unused, - dead_code -)] +#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] fn main() { let vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/for_loop_unfixable.stderr b/tests/ui/for_loop_unfixable.stderr index 1c9287b6acbb3..f769b4bdc9411 100644 --- a/tests/ui/for_loop_unfixable.stderr +++ b/tests/ui/for_loop_unfixable.stderr @@ -1,5 +1,5 @@ error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loop_unfixable.rs:21:15 + --> $DIR/for_loop_unfixable.rs:14:15 | LL | for _v in vec.iter().next() {} | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index e4dc5b647dfd2..69189d9e0c00d 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -2,6 +2,7 @@ #![allow( clippy::blacklisted_name, clippy::collapsible_else_if, + clippy::equatable_if_let, clippy::collapsible_if, clippy::ifs_same_cond, clippy::needless_return, diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index 6524be0af8517..cac788f859d1e 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -1,5 +1,5 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:13:13 + --> $DIR/if_same_then_else2.rs:14:13 | LL | if true { | _____________^ @@ -13,7 +13,7 @@ LL | | } else { | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else2.rs:22:12 + --> $DIR/if_same_then_else2.rs:23:12 | LL | } else { | ____________^ @@ -26,7 +26,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:34:13 + --> $DIR/if_same_then_else2.rs:35:13 | LL | if true { | _____________^ @@ -35,7 +35,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:36:12 + --> $DIR/if_same_then_else2.rs:37:12 | LL | } else { | ____________^ @@ -45,7 +45,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:41:13 + --> $DIR/if_same_then_else2.rs:42:13 | LL | if true { | _____________^ @@ -54,7 +54,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:43:12 + --> $DIR/if_same_then_else2.rs:44:12 | LL | } else { | ____________^ @@ -64,7 +64,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:91:21 + --> $DIR/if_same_then_else2.rs:92:21 | LL | let _ = if true { | _____________________^ @@ -73,7 +73,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:93:12 + --> $DIR/if_same_then_else2.rs:94:12 | LL | } else { | ____________^ @@ -83,7 +83,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:98:13 + --> $DIR/if_same_then_else2.rs:99:13 | LL | if true { | _____________^ @@ -92,7 +92,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:100:12 + --> $DIR/if_same_then_else2.rs:101:12 | LL | } else { | ____________^ @@ -102,7 +102,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:122:20 + --> $DIR/if_same_then_else2.rs:123:20 | LL | } else if true { | ____________________^ @@ -112,7 +112,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:125:12 + --> $DIR/if_same_then_else2.rs:126:12 | LL | } else { | ____________^ diff --git a/tests/ui/if_then_panic.fixed b/tests/ui/if_then_panic.fixed index fc57ae0dfa5ee..0998f8ffa9de4 100644 --- a/tests/ui/if_then_panic.fixed +++ b/tests/ui/if_then_panic.fixed @@ -31,4 +31,10 @@ fn main() { } else { println!("qwq"); } + let b = vec![1, 2, 3]; + assert!(!b.is_empty(), "panic1"); + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); } diff --git a/tests/ui/if_then_panic.rs b/tests/ui/if_then_panic.rs index d1ac93d8d413c..10433c8d54f2d 100644 --- a/tests/ui/if_then_panic.rs +++ b/tests/ui/if_then_panic.rs @@ -35,4 +35,20 @@ fn main() { } else { println!("qwq"); } + let b = vec![1, 2, 3]; + if b.is_empty() { + panic!("panic1"); + } + if b.is_empty() && a.is_empty() { + panic!("panic2"); + } + if a.is_empty() && !b.is_empty() { + panic!("panic3"); + } + if b.is_empty() || a.is_empty() { + panic!("panic4"); + } + if a.is_empty() || !b.is_empty() { + panic!("panic5"); + } } diff --git a/tests/ui/if_then_panic.stderr b/tests/ui/if_then_panic.stderr index b92c9bdf67430..5bb62f8756606 100644 --- a/tests/ui/if_then_panic.stderr +++ b/tests/ui/if_then_panic.stderr @@ -16,5 +16,45 @@ LL | | panic!("qwqwq"); LL | | } | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");` -error: aborting due to 2 previous errors +error: only a `panic!` in `if`-then statement + --> $DIR/if_then_panic.rs:39:5 + | +LL | / if b.is_empty() { +LL | | panic!("panic1"); +LL | | } + | |_____^ help: try: `assert!(!b.is_empty(), "panic1");` + +error: only a `panic!` in `if`-then statement + --> $DIR/if_then_panic.rs:42:5 + | +LL | / if b.is_empty() && a.is_empty() { +LL | | panic!("panic2"); +LL | | } + | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` + +error: only a `panic!` in `if`-then statement + --> $DIR/if_then_panic.rs:45:5 + | +LL | / if a.is_empty() && !b.is_empty() { +LL | | panic!("panic3"); +LL | | } + | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` + +error: only a `panic!` in `if`-then statement + --> $DIR/if_then_panic.rs:48:5 + | +LL | / if b.is_empty() || a.is_empty() { +LL | | panic!("panic4"); +LL | | } + | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` + +error: only a `panic!` in `if`-then statement + --> $DIR/if_then_panic.rs:51:5 + | +LL | / if a.is_empty() || !b.is_empty() { +LL | | panic!("panic5"); +LL | | } + | |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` + +error: aborting due to 7 previous errors diff --git a/tests/ui/implicit_hasher.rs b/tests/ui/implicit_hasher.rs index 97c26bc83ad4b..aa69b0974101c 100644 --- a/tests/ui/implicit_hasher.rs +++ b/tests/ui/implicit_hasher.rs @@ -1,3 +1,4 @@ +// edition:2018 // aux-build:implicit_hasher_macros.rs #![deny(clippy::implicit_hasher)] #![allow(unused)] @@ -96,4 +97,7 @@ __implicit_hasher_test_macro!(impl for HashMap where V: test_macro:: // #4260 implicit_hasher_fn!(); +// #7712 +pub async fn election_vote(_data: HashMap) {} + fn main() {} diff --git a/tests/ui/implicit_hasher.stderr b/tests/ui/implicit_hasher.stderr index 2e62dd30f9fc5..dad5ab71f157f 100644 --- a/tests/ui/implicit_hasher.stderr +++ b/tests/ui/implicit_hasher.stderr @@ -1,11 +1,11 @@ error: impl for `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:16:35 + --> $DIR/implicit_hasher.rs:17:35 | LL | impl Foo for HashMap { | ^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/implicit_hasher.rs:2:9 + --> $DIR/implicit_hasher.rs:3:9 | LL | #![deny(clippy::implicit_hasher)] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default: | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: impl for `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:25:36 + --> $DIR/implicit_hasher.rs:26:36 | LL | impl Foo for (HashMap,) { | ^^^^^^^^^^^^^ @@ -34,7 +34,7 @@ LL | ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Defa | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: impl for `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:30:19 + --> $DIR/implicit_hasher.rs:31:19 | LL | impl Foo for HashMap { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default: | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: impl for `HashSet` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:47:32 + --> $DIR/implicit_hasher.rs:48:32 | LL | impl Foo for HashSet { | ^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default: | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: impl for `HashSet` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:52:19 + --> $DIR/implicit_hasher.rs:53:19 | LL | impl Foo for HashSet { | ^^^^^^^^^^^^^^^ @@ -79,7 +79,7 @@ LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default: | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: parameter of type `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:69:23 + --> $DIR/implicit_hasher.rs:70:23 | LL | pub fn foo(_map: &mut HashMap, _set: &mut HashSet) {} | ^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | pub fn foo(_map: &mut HashMap, _s | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~ error: parameter of type `HashSet` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:69:53 + --> $DIR/implicit_hasher.rs:70:53 | LL | pub fn foo(_map: &mut HashMap, _set: &mut HashSet) {} | ^^^^^^^^^^^^ @@ -101,7 +101,7 @@ LL | pub fn foo(_map: &mut HashMap, _set: | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~ error: impl for `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:73:43 + --> $DIR/implicit_hasher.rs:74:43 | LL | impl Foo for HashMap { | ^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: parameter of type `HashMap` should be generalized over different hashers - --> $DIR/implicit_hasher.rs:81:33 + --> $DIR/implicit_hasher.rs:82:33 | LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} | ^^^^^^^^^^^^^^^^^ @@ -135,7 +135,7 @@ LL | pub fn $name(_map: &mut HashMap $DIR/implicit_hasher.rs:81:63 + --> $DIR/implicit_hasher.rs:82:63 | LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} | ^^^^^^^^^^^^ @@ -149,5 +149,16 @@ help: consider adding a type parameter LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) {} | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~ -error: aborting due to 10 previous errors +error: parameter of type `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:101:35 + | +LL | pub async fn election_vote(_data: HashMap) {} + | ^^^^^^^^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | pub async fn election_vote(_data: HashMap) {} + | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 11 previous errors diff --git a/tests/ui/integer_arithmetic.rs b/tests/ui/integer_arithmetic.rs index b74c93dc4a666..67f24b4548aac 100644 --- a/tests/ui/integer_arithmetic.rs +++ b/tests/ui/integer_arithmetic.rs @@ -1,12 +1,5 @@ #![warn(clippy::integer_arithmetic, clippy::float_arithmetic)] -#![allow( - unused, - clippy::shadow_reuse, - clippy::shadow_unrelated, - clippy::no_effect, - clippy::unnecessary_operation, - clippy::op_ref -)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::op_ref)] #[rustfmt::skip] fn main() { diff --git a/tests/ui/integer_arithmetic.stderr b/tests/ui/integer_arithmetic.stderr index add3b6b90fa26..9a795b1f29154 100644 --- a/tests/ui/integer_arithmetic.stderr +++ b/tests/ui/integer_arithmetic.stderr @@ -1,5 +1,5 @@ error: this operation will panic at runtime - --> $DIR/integer_arithmetic.rs:37:5 + --> $DIR/integer_arithmetic.rs:30:5 | LL | i /= 0; | ^^^^^^ attempt to divide `_` by zero @@ -7,13 +7,13 @@ LL | i /= 0; = note: `#[deny(unconditional_panic)]` on by default error: this operation will panic at runtime - --> $DIR/integer_arithmetic.rs:42:5 + --> $DIR/integer_arithmetic.rs:35:5 | LL | i %= 0; | ^^^^^^ attempt to calculate the remainder of `_` with a divisor of zero error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:16:5 + --> $DIR/integer_arithmetic.rs:9:5 | LL | 1 + i; | ^^^^^ @@ -21,146 +21,146 @@ LL | 1 + i; = note: `-D clippy::integer-arithmetic` implied by `-D warnings` error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:17:5 + --> $DIR/integer_arithmetic.rs:10:5 | LL | i * 2; | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:18:5 + --> $DIR/integer_arithmetic.rs:11:5 | LL | / 1 % LL | | i / 2; // no error, this is part of the expression in the preceding line | |_____^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:20:5 + --> $DIR/integer_arithmetic.rs:13:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:21:5 + --> $DIR/integer_arithmetic.rs:14:5 | LL | -i; | ^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:22:5 + --> $DIR/integer_arithmetic.rs:15:5 | LL | i >> 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:23:5 + --> $DIR/integer_arithmetic.rs:16:5 | LL | i << 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:33:5 + --> $DIR/integer_arithmetic.rs:26:5 | LL | i += 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:34:5 + --> $DIR/integer_arithmetic.rs:27:5 | LL | i -= 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:35:5 + --> $DIR/integer_arithmetic.rs:28:5 | LL | i *= 2; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:38:11 + --> $DIR/integer_arithmetic.rs:31:11 | LL | i /= -1; | ^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:39:5 + --> $DIR/integer_arithmetic.rs:32:5 | LL | i /= var1; | ^^^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:40:5 + --> $DIR/integer_arithmetic.rs:33:5 | LL | i /= var2; | ^^^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:43:11 + --> $DIR/integer_arithmetic.rs:36:11 | LL | i %= -1; | ^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:44:5 + --> $DIR/integer_arithmetic.rs:37:5 | LL | i %= var1; | ^^^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:45:5 + --> $DIR/integer_arithmetic.rs:38:5 | LL | i %= var2; | ^^^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:46:5 + --> $DIR/integer_arithmetic.rs:39:5 | LL | i <<= 3; | ^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:47:5 + --> $DIR/integer_arithmetic.rs:40:5 | LL | i >>= 2; | ^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:89:5 + --> $DIR/integer_arithmetic.rs:82:5 | LL | 3 + &1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:90:5 + --> $DIR/integer_arithmetic.rs:83:5 | LL | &3 + 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:91:5 + --> $DIR/integer_arithmetic.rs:84:5 | LL | &3 + &1; | ^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:96:5 + --> $DIR/integer_arithmetic.rs:89:5 | LL | a + x | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:100:5 + --> $DIR/integer_arithmetic.rs:93:5 | LL | x + y | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:104:5 + --> $DIR/integer_arithmetic.rs:97:5 | LL | x + y | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:108:5 + --> $DIR/integer_arithmetic.rs:101:5 | LL | (&x + &y) | ^^^^^^^^^ diff --git a/tests/ui/large_enum_variant.rs b/tests/ui/large_enum_variant.rs index d22fee3f27b05..b45cc849eaec4 100644 --- a/tests/ui/large_enum_variant.rs +++ b/tests/ui/large_enum_variant.rs @@ -35,6 +35,7 @@ enum LargeEnum2 { VariantOk(i32, u32), ContainingLargeEnum(LargeEnum), } + enum LargeEnum3 { ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), VoidVariant, @@ -56,6 +57,23 @@ enum LargeEnumOk { LargeB([i32; 8001]), } +enum LargeEnum6 { + A, + B([u8; 255]), + C([u8; 200]), +} + +enum LargeEnum7 { + A, + B([u8; 1255]), + C([u8; 200]), +} + +enum LargeEnum8 { + VariantOk(i32, u32), + ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]), +} + fn main() { large_enum_variant!(); } diff --git a/tests/ui/large_enum_variant.stderr b/tests/ui/large_enum_variant.stderr index 0eac28fbd3500..899f97ce2e1e9 100644 --- a/tests/ui/large_enum_variant.stderr +++ b/tests/ui/large_enum_variant.stderr @@ -32,30 +32,45 @@ LL | ContainingLargeEnum(Box), | ~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:46:5 + --> $DIR/large_enum_variant.rs:40:5 + | +LL | ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 70004 bytes + | +note: and the second-largest variant is 8 bytes: + --> $DIR/large_enum_variant.rs:42:5 + | +LL | StructLikeLittle { x: i32, y: i32 }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>), + | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:47:5 | LL | StructLikeLarge { x: [i32; 8000], y: i32 }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes | note: and the second-largest variant is 8 bytes: - --> $DIR/large_enum_variant.rs:45:5 + --> $DIR/large_enum_variant.rs:46:5 | LL | VariantOk(i32, u32), | ^^^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:46:5 | -LL | StructLikeLarge { x: [i32; 8000], y: i32 }, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | StructLikeLarge { x: Box<[i32; 8000]>, y: i32 }, + | ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:51:5 + --> $DIR/large_enum_variant.rs:52:5 | LL | StructLikeLarge2 { x: [i32; 8000] }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes | note: and the second-largest variant is 8 bytes: - --> $DIR/large_enum_variant.rs:50:5 + --> $DIR/large_enum_variant.rs:51:5 | LL | VariantOk(i32, u32), | ^^^^^^^^^^^^^^^^^^^ @@ -64,5 +79,37 @@ help: consider boxing the large fields to reduce the total size of the enum LL | StructLikeLarge2 { x: Box<[i32; 8000]> }, | ~~~~~~~~~~~~~~~~ -error: aborting due to 4 previous errors +error: large size difference between variants + --> $DIR/large_enum_variant.rs:68:5 + | +LL | B([u8; 1255]), + | ^^^^^^^^^^^^^ this variant is 1255 bytes + | +note: and the second-largest variant is 200 bytes: + --> $DIR/large_enum_variant.rs:69:5 + | +LL | C([u8; 200]), + | ^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box<[u8; 1255]>), + | ~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:74:5 + | +LL | ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 70128 bytes + | +note: and the second-largest variant is 8 bytes: + --> $DIR/large_enum_variant.rs:73:5 + | +LL | VariantOk(i32, u32), + | ^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]), + | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + +error: aborting due to 7 previous errors diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index 178d8705c2f02..0860dcf8e0ddb 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -1,11 +1,11 @@ // run-rustfix -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::iter_cloned_collect)] -#![allow(clippy::clone_on_copy, clippy::redundant_clone)] -#![allow(clippy::let_underscore_drop)] -#![allow(clippy::missing_docs_in_private_items)] -#![allow(clippy::redundant_closure_for_method_calls)] -#![allow(clippy::many_single_char_names)] +#![warn(clippy::map_clone)] +#![allow( + clippy::clone_on_copy, + clippy::iter_cloned_collect, + clippy::many_single_char_names, + clippy::redundant_clone +)] fn main() { let _: Vec = vec![5_i8; 6].iter().copied().collect(); diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index c73d81713b8a3..b6987336834b8 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -1,11 +1,11 @@ // run-rustfix -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::iter_cloned_collect)] -#![allow(clippy::clone_on_copy, clippy::redundant_clone)] -#![allow(clippy::let_underscore_drop)] -#![allow(clippy::missing_docs_in_private_items)] -#![allow(clippy::redundant_closure_for_method_calls)] -#![allow(clippy::many_single_char_names)] +#![warn(clippy::map_clone)] +#![allow( + clippy::clone_on_copy, + clippy::iter_cloned_collect, + clippy::many_single_char_names, + clippy::redundant_clone +)] fn main() { let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 319299862a700..c611f76bf9605 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns, dead_code)] +#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] fn main() { let x = Some(5); diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index 2ef6cf42387f6..2deeb84e74138 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns, dead_code)] +#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] fn main() { let x = Some(5); diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index c84e31ea482a4..846d665d1d864 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -2,7 +2,7 @@ #![feature(half_open_range_patterns)] #![warn(clippy::match_overlapping_arm)] #![allow(clippy::redundant_pattern_matching)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::equatable_if_let)] /// Tests for match_overlapping_arm diff --git a/tests/ui/match_ref_pats.rs b/tests/ui/match_ref_pats.rs index 5de43733ad336..6cbb4d32b0d71 100644 --- a/tests/ui/match_ref_pats.rs +++ b/tests/ui/match_ref_pats.rs @@ -1,4 +1,5 @@ #![warn(clippy::match_ref_pats)] +#![allow(clippy::equatable_if_let)] fn ref_pats() { { diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index a57a338b27634..072aff445e97f 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -1,5 +1,5 @@ error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:6:9 + --> $DIR/match_ref_pats.rs:7:9 | LL | / match v { LL | | &Some(v) => println!("{:?}", v), @@ -16,7 +16,7 @@ LL ~ None => println!("none"), | error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:17:5 + --> $DIR/match_ref_pats.rs:18:5 | LL | / match tup { LL | | &(v, 1) => println!("{}", v), @@ -31,7 +31,7 @@ LL ~ (v, 1) => println!("{}", v), | error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_ref_pats.rs:23:5 + --> $DIR/match_ref_pats.rs:24:5 | LL | / match &w { LL | | &Some(v) => println!("{:?}", v), @@ -47,7 +47,7 @@ LL ~ None => println!("none"), | error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:35:12 + --> $DIR/match_ref_pats.rs:36:12 | LL | if let &None = a { | -------^^^^^---- help: try this: `if a.is_none()` @@ -55,7 +55,7 @@ LL | if let &None = a { = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:35:5 + --> $DIR/match_ref_pats.rs:36:5 | LL | / if let &None = a { LL | | println!("none"); @@ -68,13 +68,13 @@ LL | if let None = *a { | ~~~~ ~~ error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:40:12 + --> $DIR/match_ref_pats.rs:41:12 | LL | if let &None = &b { | -------^^^^^----- help: try this: `if b.is_none()` error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_ref_pats.rs:40:5 + --> $DIR/match_ref_pats.rs:41:5 | LL | / if let &None = &b { LL | | println!("none"); @@ -87,7 +87,7 @@ LL | if let None = b { | ~~~~ ~ error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:67:9 + --> $DIR/match_ref_pats.rs:68:9 | LL | / match foo_variant!(0) { LL | | &Foo::A => println!("A"), diff --git a/tests/ui/modulo_arithmetic_float.rs b/tests/ui/modulo_arithmetic_float.rs index b010b0dbdfa69..b1861f07cd189 100644 --- a/tests/ui/modulo_arithmetic_float.rs +++ b/tests/ui/modulo_arithmetic_float.rs @@ -1,12 +1,5 @@ #![warn(clippy::modulo_arithmetic)] -#![allow( - unused, - clippy::shadow_reuse, - clippy::shadow_unrelated, - clippy::no_effect, - clippy::unnecessary_operation, - clippy::modulo_one -)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] fn main() { // Lint when both sides are const and of the opposite sign diff --git a/tests/ui/modulo_arithmetic_float.stderr b/tests/ui/modulo_arithmetic_float.stderr index 7bfdb0bde6070..97844aaaa7598 100644 --- a/tests/ui/modulo_arithmetic_float.stderr +++ b/tests/ui/modulo_arithmetic_float.stderr @@ -1,5 +1,5 @@ error: you are using modulo operator on constants with different signs: `-1.600 % 2.100` - --> $DIR/modulo_arithmetic_float.rs:13:5 + --> $DIR/modulo_arithmetic_float.rs:6:5 | LL | -1.6 % 2.1; | ^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | -1.6 % 2.1; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on constants with different signs: `1.600 % -2.100` - --> $DIR/modulo_arithmetic_float.rs:14:5 + --> $DIR/modulo_arithmetic_float.rs:7:5 | LL | 1.6 % -2.1; | ^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | 1.6 % -2.1; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on constants with different signs: `-1.200 % 3.400` - --> $DIR/modulo_arithmetic_float.rs:15:5 + --> $DIR/modulo_arithmetic_float.rs:8:5 | LL | (1.1 - 2.3) % (1.1 + 2.3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | (1.1 - 2.3) % (1.1 + 2.3); = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on constants with different signs: `3.400 % -1.200` - --> $DIR/modulo_arithmetic_float.rs:16:5 + --> $DIR/modulo_arithmetic_float.rs:9:5 | LL | (1.1 + 2.3) % (1.1 - 2.3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | (1.1 + 2.3) % (1.1 - 2.3); = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_float.rs:21:5 + --> $DIR/modulo_arithmetic_float.rs:14:5 | LL | a_f32 % b_f32; | ^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | a_f32 % b_f32; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_float.rs:22:5 + --> $DIR/modulo_arithmetic_float.rs:15:5 | LL | b_f32 % a_f32; | ^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | b_f32 % a_f32; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_float.rs:23:5 + --> $DIR/modulo_arithmetic_float.rs:16:5 | LL | b_f32 %= a_f32; | ^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | b_f32 %= a_f32; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_float.rs:27:5 + --> $DIR/modulo_arithmetic_float.rs:20:5 | LL | a_f64 % b_f64; | ^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | a_f64 % b_f64; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_float.rs:28:5 + --> $DIR/modulo_arithmetic_float.rs:21:5 | LL | b_f64 % a_f64; | ^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | b_f64 % a_f64; = note: double check for expected result especially when interoperating with different languages error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_float.rs:29:5 + --> $DIR/modulo_arithmetic_float.rs:22:5 | LL | b_f64 %= a_f64; | ^^^^^^^^^^^^^^ diff --git a/tests/ui/modulo_arithmetic_integral.rs b/tests/ui/modulo_arithmetic_integral.rs index 779d035c5f8a2..fc1acc39ebc77 100644 --- a/tests/ui/modulo_arithmetic_integral.rs +++ b/tests/ui/modulo_arithmetic_integral.rs @@ -1,12 +1,5 @@ #![warn(clippy::modulo_arithmetic)] -#![allow( - unused, - clippy::shadow_reuse, - clippy::shadow_unrelated, - clippy::no_effect, - clippy::unnecessary_operation, - clippy::modulo_one -)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] fn main() { // Lint on signed integral numbers diff --git a/tests/ui/modulo_arithmetic_integral.stderr b/tests/ui/modulo_arithmetic_integral.stderr index e863b838699e9..f71adf5b0d014 100644 --- a/tests/ui/modulo_arithmetic_integral.stderr +++ b/tests/ui/modulo_arithmetic_integral.stderr @@ -1,5 +1,5 @@ error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:15:5 + --> $DIR/modulo_arithmetic_integral.rs:8:5 | LL | a % b; | ^^^^^ @@ -9,7 +9,7 @@ LL | a % b; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:16:5 + --> $DIR/modulo_arithmetic_integral.rs:9:5 | LL | b % a; | ^^^^^ @@ -18,7 +18,7 @@ LL | b % a; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:17:5 + --> $DIR/modulo_arithmetic_integral.rs:10:5 | LL | b %= a; | ^^^^^^ @@ -27,7 +27,7 @@ LL | b %= a; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:21:5 + --> $DIR/modulo_arithmetic_integral.rs:14:5 | LL | a_i8 % b_i8; | ^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | a_i8 % b_i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:22:5 + --> $DIR/modulo_arithmetic_integral.rs:15:5 | LL | b_i8 %= a_i8; | ^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | b_i8 %= a_i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:26:5 + --> $DIR/modulo_arithmetic_integral.rs:19:5 | LL | a_i16 % b_i16; | ^^^^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | a_i16 % b_i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:27:5 + --> $DIR/modulo_arithmetic_integral.rs:20:5 | LL | b_i16 %= a_i16; | ^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | b_i16 %= a_i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:31:5 + --> $DIR/modulo_arithmetic_integral.rs:24:5 | LL | a_i32 % b_i32; | ^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | a_i32 % b_i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:32:5 + --> $DIR/modulo_arithmetic_integral.rs:25:5 | LL | b_i32 %= a_i32; | ^^^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL | b_i32 %= a_i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:36:5 + --> $DIR/modulo_arithmetic_integral.rs:29:5 | LL | a_i64 % b_i64; | ^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | a_i64 % b_i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:37:5 + --> $DIR/modulo_arithmetic_integral.rs:30:5 | LL | b_i64 %= a_i64; | ^^^^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL | b_i64 %= a_i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:41:5 + --> $DIR/modulo_arithmetic_integral.rs:34:5 | LL | a_i128 % b_i128; | ^^^^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL | a_i128 % b_i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:42:5 + --> $DIR/modulo_arithmetic_integral.rs:35:5 | LL | b_i128 %= a_i128; | ^^^^^^^^^^^^^^^^ @@ -117,7 +117,7 @@ LL | b_i128 %= a_i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:46:5 + --> $DIR/modulo_arithmetic_integral.rs:39:5 | LL | a_isize % b_isize; | ^^^^^^^^^^^^^^^^^ @@ -126,7 +126,7 @@ LL | a_isize % b_isize; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:47:5 + --> $DIR/modulo_arithmetic_integral.rs:40:5 | LL | b_isize %= a_isize; | ^^^^^^^^^^^^^^^^^^ @@ -135,7 +135,7 @@ LL | b_isize %= a_isize; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:51:5 + --> $DIR/modulo_arithmetic_integral.rs:44:5 | LL | a % b; | ^^^^^ @@ -144,7 +144,7 @@ LL | a % b; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on types that might have different signs - --> $DIR/modulo_arithmetic_integral.rs:52:5 + --> $DIR/modulo_arithmetic_integral.rs:45:5 | LL | b %= a; | ^^^^^^ diff --git a/tests/ui/modulo_arithmetic_integral_const.rs b/tests/ui/modulo_arithmetic_integral_const.rs index 57a96692c0097..047a29fa1e327 100644 --- a/tests/ui/modulo_arithmetic_integral_const.rs +++ b/tests/ui/modulo_arithmetic_integral_const.rs @@ -1,12 +1,5 @@ #![warn(clippy::modulo_arithmetic)] -#![allow( - unused, - clippy::shadow_reuse, - clippy::shadow_unrelated, - clippy::no_effect, - clippy::unnecessary_operation, - clippy::modulo_one -)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] 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 de328bb75fe91..64335f35f0f82 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:13:5 + --> $DIR/modulo_arithmetic_integral_const.rs:6: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:14:5 + --> $DIR/modulo_arithmetic_integral_const.rs:7: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:15:5 + --> $DIR/modulo_arithmetic_integral_const.rs:8: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:16:5 + --> $DIR/modulo_arithmetic_integral_const.rs:9: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:17:5 + --> $DIR/modulo_arithmetic_integral_const.rs:10: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:19:5 + --> $DIR/modulo_arithmetic_integral_const.rs:12: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:20:5 + --> $DIR/modulo_arithmetic_integral_const.rs:13: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:21:5 + --> $DIR/modulo_arithmetic_integral_const.rs:14: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:22:5 + --> $DIR/modulo_arithmetic_integral_const.rs:15: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:23:5 + --> $DIR/modulo_arithmetic_integral_const.rs:16: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:24:5 + --> $DIR/modulo_arithmetic_integral_const.rs:17: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:25:5 + --> $DIR/modulo_arithmetic_integral_const.rs:18: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:26:5 + --> $DIR/modulo_arithmetic_integral_const.rs:19: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:27:5 + --> $DIR/modulo_arithmetic_integral_const.rs:20: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:28:5 + --> $DIR/modulo_arithmetic_integral_const.rs:21: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:29:5 + --> $DIR/modulo_arithmetic_integral_const.rs:22: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:30:5 + --> $DIR/modulo_arithmetic_integral_const.rs:23:5 | LL | 1isize % -2isize; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/needless_bool/fixable.fixed b/tests/ui/needless_bool/fixable.fixed index 639eac8b8b315..a2e3988daff1b 100644 --- a/tests/ui/needless_bool/fixable.fixed +++ b/tests/ui/needless_bool/fixable.fixed @@ -6,6 +6,7 @@ dead_code, clippy::no_effect, clippy::if_same_then_else, + clippy::equatable_if_let, clippy::needless_return, clippy::self_named_constructors )] diff --git a/tests/ui/needless_bool/fixable.rs b/tests/ui/needless_bool/fixable.rs index a3ce086a1c90a..75805e8578919 100644 --- a/tests/ui/needless_bool/fixable.rs +++ b/tests/ui/needless_bool/fixable.rs @@ -6,6 +6,7 @@ dead_code, clippy::no_effect, clippy::if_same_then_else, + clippy::equatable_if_let, clippy::needless_return, clippy::self_named_constructors )] diff --git a/tests/ui/needless_bool/fixable.stderr b/tests/ui/needless_bool/fixable.stderr index 8026d643c4488..1fa12add16739 100644 --- a/tests/ui/needless_bool/fixable.stderr +++ b/tests/ui/needless_bool/fixable.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:40:5 + --> $DIR/fixable.rs:41:5 | LL | / if x { LL | | true @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::needless-bool` implied by `-D warnings` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:45:5 + --> $DIR/fixable.rs:46:5 | LL | / if x { LL | | false @@ -21,7 +21,7 @@ LL | | }; | |_____^ help: you can reduce it to: `!x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:50:5 + --> $DIR/fixable.rs:51:5 | LL | / if x && y { LL | | false @@ -31,7 +31,7 @@ LL | | }; | |_____^ help: you can reduce it to: `!(x && y)` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:70:5 + --> $DIR/fixable.rs:71:5 | LL | / if x { LL | | return true; @@ -41,7 +41,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:78:5 + --> $DIR/fixable.rs:79:5 | LL | / if x { LL | | return false; @@ -51,7 +51,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:86:5 + --> $DIR/fixable.rs:87:5 | LL | / if x && y { LL | | return true; @@ -61,7 +61,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x && y` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:94:5 + --> $DIR/fixable.rs:95:5 | LL | / if x && y { LL | | return false; @@ -71,7 +71,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !(x && y)` error: equality checks against true are unnecessary - --> $DIR/fixable.rs:102:8 + --> $DIR/fixable.rs:103:8 | LL | if x == true {}; | ^^^^^^^^^ help: try simplifying it as shown: `x` @@ -79,25 +79,25 @@ LL | if x == true {}; = note: `-D clippy::bool-comparison` implied by `-D warnings` error: equality checks against false can be replaced by a negation - --> $DIR/fixable.rs:106:8 + --> $DIR/fixable.rs:107:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: equality checks against true are unnecessary - --> $DIR/fixable.rs:116:8 + --> $DIR/fixable.rs:117:8 | LL | if x == true {}; | ^^^^^^^^^ help: try simplifying it as shown: `x` error: equality checks against false can be replaced by a negation - --> $DIR/fixable.rs:117:8 + --> $DIR/fixable.rs:118:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: this if-then-else expression returns a bool literal - --> $DIR/fixable.rs:126:12 + --> $DIR/fixable.rs:127:12 | LL | } else if returns_bool() { | ____________^ diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 37efa6274df7a..9c999e12b4cbc 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -3,7 +3,12 @@ #![feature(let_else)] #![allow(unused)] -#![allow(clippy::if_same_then_else, clippy::single_match, clippy::needless_bool)] +#![allow( + clippy::if_same_then_else, + clippy::single_match, + clippy::needless_bool, + clippy::equatable_if_let +)] #![warn(clippy::needless_return)] macro_rules! the_answer { diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index cbf384ac9e435..da7dcf4f0a9ea 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -3,7 +3,12 @@ #![feature(let_else)] #![allow(unused)] -#![allow(clippy::if_same_then_else, clippy::single_match, clippy::needless_bool)] +#![allow( + clippy::if_same_then_else, + clippy::single_match, + clippy::needless_bool, + clippy::equatable_if_let +)] #![warn(clippy::needless_return)] macro_rules! the_answer { diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 7ce7028bbae4b..2e802cff1e686 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement - --> $DIR/needless_return.rs:20:5 + --> $DIR/needless_return.rs:25:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` @@ -7,187 +7,187 @@ LL | return true; = note: `-D clippy::needless-return` implied by `-D warnings` error: unneeded `return` statement - --> $DIR/needless_return.rs:24:5 + --> $DIR/needless_return.rs:29:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:29:9 + --> $DIR/needless_return.rs:34:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:31:9 + --> $DIR/needless_return.rs:36:9 | LL | return false; | ^^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:37:17 + --> $DIR/needless_return.rs:42:17 | LL | true => return false, | ^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:39:13 + --> $DIR/needless_return.rs:44:13 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:46:9 + --> $DIR/needless_return.rs:51:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:48:16 + --> $DIR/needless_return.rs:53:16 | LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:56:5 + --> $DIR/needless_return.rs:61:5 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:61:9 + --> $DIR/needless_return.rs:66:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:63:9 + --> $DIR/needless_return.rs:68:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:70:14 + --> $DIR/needless_return.rs:75:14 | LL | _ => return, | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:85:9 + --> $DIR/needless_return.rs:90:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` error: unneeded `return` statement - --> $DIR/needless_return.rs:87:9 + --> $DIR/needless_return.rs:92:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` error: unneeded `return` statement - --> $DIR/needless_return.rs:108:32 + --> $DIR/needless_return.rs:113:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:113:13 + --> $DIR/needless_return.rs:118:13 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:115:20 + --> $DIR/needless_return.rs:120:20 | LL | let _ = || return; | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:121:32 + --> $DIR/needless_return.rs:126:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ help: remove `return`: `Foo` error: unneeded `return` statement - --> $DIR/needless_return.rs:130:5 + --> $DIR/needless_return.rs:135:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:134:5 + --> $DIR/needless_return.rs:139:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:139:9 + --> $DIR/needless_return.rs:144:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:141:9 + --> $DIR/needless_return.rs:146:9 | LL | return false; | ^^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:147:17 + --> $DIR/needless_return.rs:152:17 | LL | true => return false, | ^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:149:13 + --> $DIR/needless_return.rs:154:13 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:156:9 + --> $DIR/needless_return.rs:161:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:158:16 + --> $DIR/needless_return.rs:163:16 | LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:166:5 + --> $DIR/needless_return.rs:171:5 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:171:9 + --> $DIR/needless_return.rs:176:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:173:9 + --> $DIR/needless_return.rs:178:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:180:14 + --> $DIR/needless_return.rs:185:14 | LL | _ => return, | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:195:9 + --> $DIR/needless_return.rs:200:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` error: unneeded `return` statement - --> $DIR/needless_return.rs:197:9 + --> $DIR/needless_return.rs:202:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` diff --git a/tests/ui/non_send_fields_in_send_ty.rs b/tests/ui/non_send_fields_in_send_ty.rs new file mode 100644 index 0000000000000..eca7f5e565591 --- /dev/null +++ b/tests/ui/non_send_fields_in_send_ty.rs @@ -0,0 +1,127 @@ +#![warn(clippy::non_send_fields_in_send_ty)] +#![feature(extern_types)] + +use std::cell::UnsafeCell; +use std::ptr::NonNull; +use std::rc::Rc; +use std::sync::{Arc, Mutex, MutexGuard}; + +// disrustor / RUSTSEC-2020-0150 +pub struct RingBuffer { + data: Vec>, + capacity: usize, + mask: usize, +} + +unsafe impl Send for RingBuffer {} + +// noise_search / RUSTSEC-2020-0141 +pub struct MvccRwLock { + raw: *const T, + lock: Mutex>, +} + +unsafe impl Send for MvccRwLock {} + +// async-coap / RUSTSEC-2020-0124 +pub struct ArcGuard { + inner: T, + head: Arc, +} + +unsafe impl Send for ArcGuard {} + +// rusb / RUSTSEC-2020-0098 +extern "C" { + type libusb_device_handle; +} + +pub trait UsbContext { + // some user trait that does not guarantee `Send` +} + +pub struct DeviceHandle { + context: T, + handle: NonNull, +} + +unsafe impl Send for DeviceHandle {} + +// Other basic tests +pub struct NoGeneric { + rc_is_not_send: Rc, +} + +unsafe impl Send for NoGeneric {} + +pub struct MultiField { + field1: T, + field2: T, + field3: T, +} + +unsafe impl Send for MultiField {} + +pub enum MyOption { + MySome(T), + MyNone, +} + +unsafe impl Send for MyOption {} + +// Multiple type parameters +pub struct MultiParam { + vec: Vec<(A, B)>, +} + +unsafe impl Send for MultiParam {} + +// Tests for raw pointer heuristic +extern "C" { + type NonSend; +} + +pub struct HeuristicTest { + // raw pointers are allowed + field1: Vec<*const NonSend>, + field2: [*const NonSend; 3], + field3: (*const NonSend, *const NonSend, *const NonSend), + // not allowed when it contains concrete `!Send` field + field4: (*const NonSend, Rc), + // nested raw pointer is also allowed + field5: Vec>, +} + +unsafe impl Send for HeuristicTest {} + +// Test attributes +#[allow(clippy::non_send_fields_in_send_ty)] +pub struct AttrTest1(T); + +pub struct AttrTest2 { + #[allow(clippy::non_send_fields_in_send_ty)] + field: T, +} + +pub enum AttrTest3 { + #[allow(clippy::non_send_fields_in_send_ty)] + Enum1(T), + Enum2(T), +} + +unsafe impl Send for AttrTest1 {} +unsafe impl Send for AttrTest2 {} +unsafe impl Send for AttrTest3 {} + +// Multiple non-overlapping `Send` for a single type +pub struct Complex { + field1: A, + field2: B, +} + +unsafe impl

Send for Complex {} + +// `MutexGuard` is non-Send +unsafe impl Send for Complex> {} + +fn main() {} diff --git a/tests/ui/non_send_fields_in_send_ty.stderr b/tests/ui/non_send_fields_in_send_ty.stderr new file mode 100644 index 0000000000000..8b8a1d16d9bb9 --- /dev/null +++ b/tests/ui/non_send_fields_in_send_ty.stderr @@ -0,0 +1,171 @@ +error: this implementation is unsound, as some fields in `RingBuffer` are `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:16:1 + | +LL | unsafe impl Send for RingBuffer {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::non-send-fields-in-send-ty` implied by `-D warnings` +note: the type of field `data` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:11:5 + | +LL | data: Vec>, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = help: add bounds on type parameter `T` that satisfy `Vec>: Send` + +error: this implementation is unsound, as some fields in `MvccRwLock` are `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:24:1 + | +LL | unsafe impl Send for MvccRwLock {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `lock` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:21:5 + | +LL | lock: Mutex>, + | ^^^^^^^^^^^^^^^^^^^ + = help: add bounds on type parameter `T` that satisfy `Mutex>: Send` + +error: this implementation is unsound, as some fields in `ArcGuard` are `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:32:1 + | +LL | unsafe impl Send for ArcGuard {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `head` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:29:5 + | +LL | head: Arc, + | ^^^^^^^^^^^^^ + = help: add bounds on type parameter `RC` that satisfy `Arc: Send` + +error: this implementation is unsound, as some fields in `DeviceHandle` are `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:48:1 + | +LL | unsafe impl Send for DeviceHandle {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `context` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:44:5 + | +LL | context: T, + | ^^^^^^^^^^ + = help: add `T: Send` bound in `Send` impl + +error: this implementation is unsound, as some fields in `NoGeneric` are `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:55:1 + | +LL | unsafe impl Send for NoGeneric {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `rc_is_not_send` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:52:5 + | +LL | rc_is_not_send: Rc, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` + +error: this implementation is unsound, as some fields in `MultiField` are `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:63:1 + | +LL | unsafe impl Send for MultiField {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `field1` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:58:5 + | +LL | field1: T, + | ^^^^^^^^^ + = help: add `T: Send` bound in `Send` impl +note: the type of field `field2` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:59:5 + | +LL | field2: T, + | ^^^^^^^^^ + = help: add `T: Send` bound in `Send` impl +note: the type of field `field3` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:60:5 + | +LL | field3: T, + | ^^^^^^^^^ + = help: add `T: Send` bound in `Send` impl + +error: this implementation is unsound, as some fields in `MyOption` are `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:70:1 + | +LL | unsafe impl Send for MyOption {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `0` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:66:12 + | +LL | MySome(T), + | ^ + = help: add `T: Send` bound in `Send` impl + +error: this implementation is unsound, as some fields in `MultiParam` are `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:77:1 + | +LL | unsafe impl Send for MultiParam {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `vec` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:74:5 + | +LL | vec: Vec<(A, B)>, + | ^^^^^^^^^^^^^^^^ + = help: add bounds on type parameters `A, B` that satisfy `Vec<(A, B)>: Send` + +error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:95:1 + | +LL | unsafe impl Send for HeuristicTest {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `field4` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:90:5 + | +LL | field4: (*const NonSend, Rc), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` + +error: this implementation is unsound, as some fields in `AttrTest3` are `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:114:1 + | +LL | unsafe impl Send for AttrTest3 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `0` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:109:11 + | +LL | Enum2(T), + | ^ + = help: add `T: Send` bound in `Send` impl + +error: this implementation is unsound, as some fields in `Complex` are `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:122:1 + | +LL | unsafe impl

Send for Complex {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `field1` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:118:5 + | +LL | field1: A, + | ^^^^^^^^^ + = help: add `P: Send` bound in `Send` impl + +error: this implementation is unsound, as some fields in `Complex>` are `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:125:1 + | +LL | unsafe impl Send for Complex> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the type of field `field2` is `!Send` + --> $DIR/non_send_fields_in_send_ty.rs:119:5 + | +LL | field2: B, + | ^^^^^^^^^ + = help: use a thread-safe type that implements `Send` + +error: aborting due to 12 previous errors + diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index d1815d0aec331..a3ebe5d070384 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -2,7 +2,7 @@ // run-rustfix #![warn(clippy::option_if_let_else)] #![allow(clippy::redundant_closure)] -#![allow(clippy::ref_option_ref)] +#![allow(clippy::ref_option_ref, clippy::equatable_if_let)] 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 a15627338cb4a..b11df3db60f57 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -2,7 +2,7 @@ // run-rustfix #![warn(clippy::option_if_let_else)] #![allow(clippy::redundant_closure)] -#![allow(clippy::ref_option_ref)] +#![allow(clippy::ref_option_ref, clippy::equatable_if_let)] fn bad1(string: Option<&str>) -> (bool, &str) { if let Some(x) = string { diff --git a/tests/ui/redundant_pattern_matching_drop_order.fixed b/tests/ui/redundant_pattern_matching_drop_order.fixed index 794ed542435d1..ce3229f17591e 100644 --- a/tests/ui/redundant_pattern_matching_drop_order.fixed +++ b/tests/ui/redundant_pattern_matching_drop_order.fixed @@ -2,7 +2,7 @@ // Issue #5746 #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::equatable_if_let)] use std::task::Poll::{Pending, Ready}; fn main() { diff --git a/tests/ui/redundant_pattern_matching_drop_order.rs b/tests/ui/redundant_pattern_matching_drop_order.rs index b9c82d86f618b..29b8543cf473a 100644 --- a/tests/ui/redundant_pattern_matching_drop_order.rs +++ b/tests/ui/redundant_pattern_matching_drop_order.rs @@ -2,7 +2,7 @@ // Issue #5746 #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::equatable_if_let)] use std::task::Poll::{Pending, Ready}; fn main() { diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index 997144772669b..813e268a60c3d 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -6,6 +6,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, + clippy::equatable_if_let, clippy::if_same_then_else )] diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index 8309847e18162..82a98468943df 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -6,6 +6,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, + clippy::equatable_if_let, clippy::if_same_then_else )] diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 613a30d4a4845..3a58e5ad7bee9 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:13:12 + --> $DIR/redundant_pattern_matching_option.rs:14:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` @@ -7,43 +7,43 @@ LL | if let None = None::<()> {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:15:12 + --> $DIR/redundant_pattern_matching_option.rs:16:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:17:12 + --> $DIR/redundant_pattern_matching_option.rs:18:12 | LL | if let Some(_) = Some(42) { | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:23:15 + --> $DIR/redundant_pattern_matching_option.rs:24:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:25:15 + --> $DIR/redundant_pattern_matching_option.rs:26:15 | LL | while let None = Some(42) {} | ----------^^^^----------- help: try this: `while Some(42).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:27:15 + --> $DIR/redundant_pattern_matching_option.rs:28:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:30:15 + --> $DIR/redundant_pattern_matching_option.rs:31:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:38:5 + --> $DIR/redundant_pattern_matching_option.rs:39:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:43:5 + --> $DIR/redundant_pattern_matching_option.rs:44:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -61,7 +61,7 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:48:13 + --> $DIR/redundant_pattern_matching_option.rs:49:13 | LL | let _ = match None::<()> { | _____________^ @@ -71,49 +71,49 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:54:20 + --> $DIR/redundant_pattern_matching_option.rs:55:20 | LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:58:20 + --> $DIR/redundant_pattern_matching_option.rs:59:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:60:19 + --> $DIR/redundant_pattern_matching_option.rs:61:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:79:12 + --> $DIR/redundant_pattern_matching_option.rs:80:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:81:12 + --> $DIR/redundant_pattern_matching_option.rs:82:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:83:15 + --> $DIR/redundant_pattern_matching_option.rs:84:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:85:15 + --> $DIR/redundant_pattern_matching_option.rs:86:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:87:5 + --> $DIR/redundant_pattern_matching_option.rs:88:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -122,7 +122,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:92:5 + --> $DIR/redundant_pattern_matching_option.rs:93:5 | LL | / match None::<()> { LL | | Some(_) => false, diff --git a/tests/ui/redundant_pattern_matching_poll.fixed b/tests/ui/redundant_pattern_matching_poll.fixed index c297745380404..3645f2c4bfdd2 100644 --- a/tests/ui/redundant_pattern_matching_poll.fixed +++ b/tests/ui/redundant_pattern_matching_poll.fixed @@ -6,6 +6,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, + clippy::equatable_if_let, clippy::if_same_then_else )] diff --git a/tests/ui/redundant_pattern_matching_poll.rs b/tests/ui/redundant_pattern_matching_poll.rs index 665c8c417504d..866c71b7cfa80 100644 --- a/tests/ui/redundant_pattern_matching_poll.rs +++ b/tests/ui/redundant_pattern_matching_poll.rs @@ -6,6 +6,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, + clippy::equatable_if_let, clippy::if_same_then_else )] diff --git a/tests/ui/redundant_pattern_matching_poll.stderr b/tests/ui/redundant_pattern_matching_poll.stderr index 5ecf024a733a3..1b480f3157f70 100644 --- a/tests/ui/redundant_pattern_matching_poll.stderr +++ b/tests/ui/redundant_pattern_matching_poll.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:15:12 + --> $DIR/redundant_pattern_matching_poll.rs:16:12 | LL | if let Pending = Pending::<()> {} | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` @@ -7,37 +7,37 @@ LL | if let Pending = Pending::<()> {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:17:12 + --> $DIR/redundant_pattern_matching_poll.rs:18:12 | LL | if let Ready(_) = Ready(42) {} | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:19:12 + --> $DIR/redundant_pattern_matching_poll.rs:20:12 | LL | if let Ready(_) = Ready(42) { | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:25:15 + --> $DIR/redundant_pattern_matching_poll.rs:26:15 | LL | while let Ready(_) = Ready(42) {} | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:27:15 + --> $DIR/redundant_pattern_matching_poll.rs:28:15 | LL | while let Pending = Ready(42) {} | ----------^^^^^^^------------ help: try this: `while Ready(42).is_pending()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:29:15 + --> $DIR/redundant_pattern_matching_poll.rs:30:15 | LL | while let Pending = Pending::<()> {} | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:35:5 + --> $DIR/redundant_pattern_matching_poll.rs:36:5 | LL | / match Ready(42) { LL | | Ready(_) => true, @@ -46,7 +46,7 @@ LL | | }; | |_____^ help: try this: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:40:5 + --> $DIR/redundant_pattern_matching_poll.rs:41:5 | LL | / match Pending::<()> { LL | | Ready(_) => false, @@ -55,7 +55,7 @@ LL | | }; | |_____^ help: try this: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:45:13 + --> $DIR/redundant_pattern_matching_poll.rs:46:13 | LL | let _ = match Pending::<()> { | _____________^ @@ -65,49 +65,49 @@ LL | | }; | |_____^ help: try this: `Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:51:20 + --> $DIR/redundant_pattern_matching_poll.rs:52:20 | LL | let _ = if let Ready(_) = poll { true } else { false }; | -------^^^^^^^^------- help: try this: `if poll.is_ready()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:55:20 + --> $DIR/redundant_pattern_matching_poll.rs:56:20 | LL | let _ = if let Ready(_) = gen_poll() { | -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:57:19 + --> $DIR/redundant_pattern_matching_poll.rs:58:19 | LL | } else if let Pending = gen_poll() { | -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:73:12 + --> $DIR/redundant_pattern_matching_poll.rs:74:12 | LL | if let Ready(_) = Ready(42) {} | -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:75:12 + --> $DIR/redundant_pattern_matching_poll.rs:76:12 | LL | if let Pending = Pending::<()> {} | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:77:15 + --> $DIR/redundant_pattern_matching_poll.rs:78:15 | LL | while let Ready(_) = Ready(42) {} | ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:79:15 + --> $DIR/redundant_pattern_matching_poll.rs:80:15 | LL | while let Pending = Pending::<()> {} | ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()` error: redundant pattern matching, consider using `is_ready()` - --> $DIR/redundant_pattern_matching_poll.rs:81:5 + --> $DIR/redundant_pattern_matching_poll.rs:82:5 | LL | / match Ready(42) { LL | | Ready(_) => true, @@ -116,7 +116,7 @@ LL | | }; | |_____^ help: try this: `Ready(42).is_ready()` error: redundant pattern matching, consider using `is_pending()` - --> $DIR/redundant_pattern_matching_poll.rs:86:5 + --> $DIR/redundant_pattern_matching_poll.rs:87:5 | LL | / match Pending::<()> { LL | | Ready(_) => false, diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index e366c75335c20..02e838456d0b5 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -1,54 +1,77 @@ -#![warn( - clippy::all, - clippy::pedantic, - clippy::shadow_same, - clippy::shadow_reuse, - clippy::shadow_unrelated -)] -#![allow( - unused_parens, - unused_variables, - clippy::manual_unwrap_or, - clippy::missing_docs_in_private_items, - clippy::single_match -)] - -fn id(x: T) -> T { - x +#![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)] + +fn shadow_same() { + let x = 1; + let x = x; + let mut x = &x; + let x = &mut x; + let x = *x; } -#[must_use] -fn first(x: (isize, isize)) -> isize { - x.0 +fn shadow_reuse() -> Option<()> { + let x = ([[0]], ()); + let x = x.0; + let x = x[0]; + let [x] = x; + let x = Some(x); + let x = foo(x); + let x = || x; + let x = Some(1).map(|_| x)?; + None } -fn main() { - let mut x = 1; - let x = &mut x; - let x = { x }; - let x = (&*x); - let x = { *x + 1 }; - let x = id(x); - let x = (1, x); - let x = first(x); - let y = 1; - let x = y; - - let x; - x = 42; - - let o = Some(1_u8); - - if let Some(p) = o { - assert_eq!(1, p); +fn shadow_unrelated() { + let x = 1; + let x = 2; +} + +fn syntax() { + fn f(x: u32) { + let x = 1; + } + let x = 1; + match Some(1) { + Some(1) => {}, + Some(x) => { + let x = 1; + }, + _ => {}, } - match o { - Some(p) => p, // no error, because the p above is in its own scope - None => 0, + if let Some(x) = Some(1) {} + while let Some(x) = Some(1) {} + let _ = |[x]: [u32; 1]| { + let x = 1; }; +} - match (x, o) { - (1, Some(a)) | (a, Some(1)) => (), // no error though `a` appears twice - _ => (), +fn negative() { + match Some(1) { + Some(x) if x == 1 => {}, + Some(x) => {}, + None => {}, } + match [None, Some(1)] { + [Some(x), None] | [None, Some(x)] => {}, + _ => {}, + } + if let Some(x) = Some(1) { + let y = 1; + } else { + let x = 1; + let y = 1; + } + let x = 1; + #[allow(clippy::shadow_unrelated)] + let x = 1; +} + +fn foo(_: T) {} + +fn question_mark() -> Option<()> { + let val = 1; + // `?` expands with a `val` binding + None?; + None } + +fn main() {} diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index 7c1ad2949e91b..8b60e072c9342 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -1,138 +1,233 @@ -error: `x` is shadowed by itself in `&mut x` - --> $DIR/shadow.rs:27:5 +error: `x` is shadowed by itself in `x` + --> $DIR/shadow.rs:5:9 | -LL | let x = &mut x; - | ^^^^^^^^^^^^^^^ +LL | let x = x; + | ^ | = note: `-D clippy::shadow-same` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:26:13 + --> $DIR/shadow.rs:4:9 | -LL | let mut x = 1; - | ^ +LL | let x = 1; + | ^ -error: `x` is shadowed by itself in `{ x }` - --> $DIR/shadow.rs:28:5 +error: `mut x` is shadowed by itself in `&x` + --> $DIR/shadow.rs:6:13 | -LL | let x = { x }; - | ^^^^^^^^^^^^^^ +LL | let mut x = &x; + | ^ | note: previous binding is here - --> $DIR/shadow.rs:27:9 + --> $DIR/shadow.rs:5:9 + | +LL | let x = x; + | ^ + +error: `x` is shadowed by itself in `&mut x` + --> $DIR/shadow.rs:7:9 | LL | let x = &mut x; | ^ + | +note: previous binding is here + --> $DIR/shadow.rs:6:9 + | +LL | let mut x = &x; + | ^^^^^ -error: `x` is shadowed by itself in `(&*x)` - --> $DIR/shadow.rs:29:5 +error: `x` is shadowed by itself in `*x` + --> $DIR/shadow.rs:8:9 | -LL | let x = (&*x); - | ^^^^^^^^^^^^^^ +LL | let x = *x; + | ^ | note: previous binding is here - --> $DIR/shadow.rs:28:9 + --> $DIR/shadow.rs:7:9 | -LL | let x = { x }; +LL | let x = &mut x; | ^ -error: `x` is shadowed by `{ *x + 1 }` which reuses the original value - --> $DIR/shadow.rs:30:9 +error: `x` is shadowed by `x.0` which reuses the original value + --> $DIR/shadow.rs:13:9 | -LL | let x = { *x + 1 }; +LL | let x = x.0; | ^ | = note: `-D clippy::shadow-reuse` implied by `-D warnings` -note: initialization happens here - --> $DIR/shadow.rs:30:13 - | -LL | let x = { *x + 1 }; - | ^^^^^^^^^^ note: previous binding is here - --> $DIR/shadow.rs:29:9 + --> $DIR/shadow.rs:12:9 | -LL | let x = (&*x); +LL | let x = ([[0]], ()); | ^ -error: `x` is shadowed by `id(x)` which reuses the original value - --> $DIR/shadow.rs:31:9 +error: `x` is shadowed by `x[0]` which reuses the original value + --> $DIR/shadow.rs:14:9 | -LL | let x = id(x); +LL | let x = x[0]; | ^ | -note: initialization happens here - --> $DIR/shadow.rs:31:13 +note: previous binding is here + --> $DIR/shadow.rs:13:9 + | +LL | let x = x.0; + | ^ + +error: `x` is shadowed by `x` which reuses the original value + --> $DIR/shadow.rs:15:10 + | +LL | let [x] = x; + | ^ | -LL | let x = id(x); - | ^^^^^ note: previous binding is here - --> $DIR/shadow.rs:30:9 + --> $DIR/shadow.rs:14:9 | -LL | let x = { *x + 1 }; +LL | let x = x[0]; | ^ -error: `x` is shadowed by `(1, x)` which reuses the original value - --> $DIR/shadow.rs:32:9 +error: `x` is shadowed by `Some(x)` which reuses the original value + --> $DIR/shadow.rs:16:9 | -LL | let x = (1, x); +LL | let x = Some(x); | ^ | -note: initialization happens here - --> $DIR/shadow.rs:32:13 +note: previous binding is here + --> $DIR/shadow.rs:15:10 + | +LL | let [x] = x; + | ^ + +error: `x` is shadowed by `foo(x)` which reuses the original value + --> $DIR/shadow.rs:17:9 + | +LL | let x = foo(x); + | ^ | -LL | let x = (1, x); - | ^^^^^^ note: previous binding is here - --> $DIR/shadow.rs:31:9 + --> $DIR/shadow.rs:16:9 | -LL | let x = id(x); +LL | let x = Some(x); | ^ -error: `x` is shadowed by `first(x)` which reuses the original value - --> $DIR/shadow.rs:33:9 +error: `x` is shadowed by `|| x` which reuses the original value + --> $DIR/shadow.rs:18:9 | -LL | let x = first(x); +LL | let x = || x; | ^ | -note: initialization happens here - --> $DIR/shadow.rs:33:13 +note: previous binding is here + --> $DIR/shadow.rs:17:9 + | +LL | let x = foo(x); + | ^ + +error: `x` is shadowed by `Some(1).map(|_| x)?` which reuses the original value + --> $DIR/shadow.rs:19:9 + | +LL | let x = Some(1).map(|_| x)?; + | ^ | -LL | let x = first(x); - | ^^^^^^^^ note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:18:9 | -LL | let x = (1, x); +LL | let x = || x; | ^ -error: `x` is being shadowed - --> $DIR/shadow.rs:35:9 +error: `x` shadows a previous, unrelated binding + --> $DIR/shadow.rs:25:9 | -LL | let x = y; +LL | let x = 2; | ^ | = note: `-D clippy::shadow-unrelated` implied by `-D warnings` -note: initialization happens here - --> $DIR/shadow.rs:35:13 +note: previous binding is here + --> $DIR/shadow.rs:24:9 | -LL | let x = y; +LL | let x = 1; + | ^ + +error: `x` shadows a previous, unrelated binding + --> $DIR/shadow.rs:30:13 + | +LL | let x = 1; | ^ + | +note: previous binding is here + --> $DIR/shadow.rs:29:10 + | +LL | fn f(x: u32) { + | ^ + +error: `x` shadows a previous, unrelated binding + --> $DIR/shadow.rs:35:14 + | +LL | Some(x) => { + | ^ + | +note: previous binding is here + --> $DIR/shadow.rs:32:9 + | +LL | let x = 1; + | ^ + +error: `x` shadows a previous, unrelated binding + --> $DIR/shadow.rs:36:17 + | +LL | let x = 1; + | ^ + | +note: previous binding is here + --> $DIR/shadow.rs:35:14 + | +LL | Some(x) => { + | ^ + +error: `x` shadows a previous, unrelated binding + --> $DIR/shadow.rs:40:17 + | +LL | if let Some(x) = Some(1) {} + | ^ + | +note: previous binding is here + --> $DIR/shadow.rs:32:9 + | +LL | let x = 1; + | ^ + +error: `x` shadows a previous, unrelated binding + --> $DIR/shadow.rs:41:20 + | +LL | while let Some(x) = Some(1) {} + | ^ + | note: previous binding is here - --> $DIR/shadow.rs:33:9 + --> $DIR/shadow.rs:32:9 | -LL | let x = first(x); +LL | let x = 1; | ^ -error: `x` shadows a previous declaration - --> $DIR/shadow.rs:37:5 +error: `x` shadows a previous, unrelated binding + --> $DIR/shadow.rs:42:15 | -LL | let x; - | ^^^^^^ +LL | let _ = |[x]: [u32; 1]| { + | ^ | note: previous binding is here - --> $DIR/shadow.rs:35:9 + --> $DIR/shadow.rs:32:9 | -LL | let x = y; +LL | let x = 1; | ^ -error: aborting due to 9 previous errors +error: `x` shadows a previous, unrelated binding + --> $DIR/shadow.rs:43:13 + | +LL | let x = 1; + | ^ + | +note: previous binding is here + --> $DIR/shadow.rs:42:15 + | +LL | let _ = |[x]: [u32; 1]| { + | ^ + +error: aborting due to 19 previous errors diff --git a/tests/ui/suspicious_map.stderr b/tests/ui/suspicious_map.stderr index 8c3f36584a5bd..3ffcd1a903174 100644 --- a/tests/ui/suspicious_map.stderr +++ b/tests/ui/suspicious_map.stderr @@ -5,7 +5,7 @@ LL | let _ = (0..3).map(|x| x + 2).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::suspicious-map` implied by `-D warnings` - = help: make sure you did not confuse `map` with `filter` or `for_each` + = help: make sure you did not confuse `map` with `filter`, `for_each` or `inspect` error: this call to `map()` won't have an effect on the call to `count()` --> $DIR/suspicious_map.rs:7:13 @@ -13,7 +13,7 @@ error: this call to `map()` won't have an effect on the call to `count()` LL | let _ = (0..3).map(f).count(); | ^^^^^^^^^^^^^^^^^^^^^ | - = help: make sure you did not confuse `map` with `filter` or `for_each` + = help: make sure you did not confuse `map` with `filter`, `for_each` or `inspect` error: aborting due to 2 previous errors diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index f5a341909023e..1e74ad2de655a 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -1,7 +1,13 @@ // run-rustfix #![warn(clippy::while_let_on_iterator)] -#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)] +#![allow( + clippy::never_loop, + unreachable_code, + unused_mut, + dead_code, + clippy::equatable_if_let +)] fn base() { let mut iter = 1..20; diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 72f34257d1f46..69cb636cee826 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -1,7 +1,13 @@ // run-rustfix #![warn(clippy::while_let_on_iterator)] -#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)] +#![allow( + clippy::never_loop, + unreachable_code, + unused_mut, + dead_code, + clippy::equatable_if_let +)] fn base() { let mut iter = 1..20; diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 5e2fce4491af0..1a11ba26eef0f 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:8:5 + --> $DIR/while_let_on_iterator.rs:14:5 | LL | while let Option::Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` @@ -7,85 +7,85 @@ LL | while let Option::Some(x) = iter.next() { = note: `-D clippy::while-let-on-iterator` implied by `-D warnings` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:13:5 + --> $DIR/while_let_on_iterator.rs:19:5 | LL | while let Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:18:5 + --> $DIR/while_let_on_iterator.rs:24:5 | LL | while let Some(_) = iter.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:94:9 + --> $DIR/while_let_on_iterator.rs:100:9 | LL | while let Some([..]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:101:9 + --> $DIR/while_let_on_iterator.rs:107:9 | LL | while let Some([_x]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:114:9 + --> $DIR/while_let_on_iterator.rs:120:9 | LL | while let Some(x @ [_]) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:134:9 + --> $DIR/while_let_on_iterator.rs:140:9 | LL | while let Some(_) = y.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:191:9 + --> $DIR/while_let_on_iterator.rs:197:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:202:5 + --> $DIR/while_let_on_iterator.rs:208:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:204:9 + --> $DIR/while_let_on_iterator.rs:210:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:213:9 + --> $DIR/while_let_on_iterator.rs:219:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:222:9 + --> $DIR/while_let_on_iterator.rs:228:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:239:9 + --> $DIR/while_let_on_iterator.rs:245:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:254:13 + --> $DIR/while_let_on_iterator.rs:260:13 | LL | while let Some(i) = self.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()` error: manual `!RangeInclusive::contains` implementation - --> $DIR/while_let_on_iterator.rs:255:20 + --> $DIR/while_let_on_iterator.rs:261:20 | LL | if i < 3 || i > 7 { | ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)` @@ -93,37 +93,37 @@ LL | if i < 3 || i > 7 { = note: `-D clippy::manual-range-contains` implied by `-D warnings` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:286:13 + --> $DIR/while_let_on_iterator.rs:292:13 | LL | while let Some(i) = self.0.0.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:315:5 + --> $DIR/while_let_on_iterator.rs:321:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:327:9 + --> $DIR/while_let_on_iterator.rs:333:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:341:5 + --> $DIR/while_let_on_iterator.rs:347:5 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:352:5 + --> $DIR/while_let_on_iterator.rs:358:5 | LL | while let Some(x) = it.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:371:5 + --> $DIR/while_let_on_iterator.rs:377:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` diff --git a/util/etc/pre-commit.sh b/util/etc/pre-commit.sh index 528f8953b25d8..5dd2ba3d5f53b 100755 --- a/util/etc/pre-commit.sh +++ b/util/etc/pre-commit.sh @@ -6,6 +6,7 @@ set -e # Update lints cargo dev update_lints git add clippy_lints/src/lib.rs +git add clippy_lints/src/lib.*.rs # Formatting: # Git will not automatically add the formatted code to the staged changes once From fbd0fb9aedbee06d0eefeecd62bc710832e0c457 Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Thu, 7 Oct 2021 11:50:14 +1100 Subject: [PATCH 004/101] Restriction lint for function pointer casts --- CHANGELOG.md | 1 + .../src/casts/fn_to_numeric_cast_any.rs | 34 ++++++ clippy_lints/src/casts/mod.rs | 39 +++++++ clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_restriction.rs | 1 + tests/ui/fn_to_numeric_cast_any.rs | 76 +++++++++++++ tests/ui/fn_to_numeric_cast_any.stderr | 106 ++++++++++++++++++ 7 files changed, 258 insertions(+) create mode 100644 clippy_lints/src/casts/fn_to_numeric_cast_any.rs create mode 100644 tests/ui/fn_to_numeric_cast_any.rs create mode 100644 tests/ui/fn_to_numeric_cast_any.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fdb300c97741..b13a5d760026f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2730,6 +2730,7 @@ Released 2018-09-13 [`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons [`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast +[`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any [`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation [`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 diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs new file mode 100644 index 0000000000000..03621887a34a6 --- /dev/null +++ b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs @@ -0,0 +1,34 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +use super::FN_TO_NUMERIC_CAST_ANY; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + // We allow casts from any function type to any function type. + match cast_to.kind() { + ty::FnDef(..) | ty::FnPtr(..) => return, + _ => { /* continue to checks */ }, + } + + match cast_from.kind() { + ty::FnDef(..) | ty::FnPtr(_) => { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); + + span_lint_and_sugg( + cx, + FN_TO_NUMERIC_CAST_ANY, + expr.span, + &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), + "did you mean to invoke the function?", + format!("{}() as {}", from_snippet, cast_to), + applicability, + ); + }, + _ => {}, + } +} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 27e1bea799353..f0800c6a6f18f 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -7,6 +7,7 @@ mod cast_ref_to_mut; mod cast_sign_loss; mod char_lit_as_u8; mod fn_to_numeric_cast; +mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; mod ptr_as_ptr; mod unnecessary_cast; @@ -251,6 +252,42 @@ declare_clippy_lint! { "casting a function pointer to a numeric type not wide enough to store the address" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of a function pointer to any integer type. + /// + /// ### 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 + /// 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. + /// + /// ### Example + /// ```rust + /// // Bad: fn1 is cast as `usize` + /// fn fn1() -> u16 { + /// 1 + /// }; + /// let _ = fn1 as usize; + /// + /// // Good: maybe you intended to call the function? + /// fn fn2() -> u16 { + /// 1 + /// }; + /// let _ = fn2() as usize; + /// + /// // Good: maybe you intended to cast it to a function type? + /// fn fn3() -> u16 { + /// 1 + /// } + /// let _ = fn3 as fn() -> u16; + /// ``` + pub FN_TO_NUMERIC_CAST_ANY, + restriction, + "casting a function pointer to any integer type" +} + declare_clippy_lint! { /// ### What it does /// Checks for casts of `&T` to `&mut T` anywhere in the code. @@ -360,6 +397,7 @@ impl_lint_pass!(Casts => [ CAST_REF_TO_MUT, CAST_PTR_ALIGNMENT, UNNECESSARY_CAST, + FN_TO_NUMERIC_CAST_ANY, FN_TO_NUMERIC_CAST, FN_TO_NUMERIC_CAST_WITH_TRUNCATION, CHAR_LIT_AS_U8, @@ -385,6 +423,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } + fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) { diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 2ba2b3da55cd1..3c6d8103b6da0 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -67,6 +67,7 @@ store.register_lints(&[ casts::CAST_SIGN_LOSS, casts::CHAR_LIT_AS_U8, casts::FN_TO_NUMERIC_CAST, + casts::FN_TO_NUMERIC_CAST_ANY, casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, casts::PTR_AS_PTR, casts::UNNECESSARY_CAST, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 4463dea5fcb84..503f4f64a0e2b 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -8,6 +8,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(as_conversions::AS_CONVERSIONS), LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), + LintId::of(casts::FN_TO_NUMERIC_CAST_ANY), LintId::of(create_dir::CREATE_DIR), LintId::of(dbg_macro::DBG_MACRO), LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), diff --git a/tests/ui/fn_to_numeric_cast_any.rs b/tests/ui/fn_to_numeric_cast_any.rs new file mode 100644 index 0000000000000..46704683926b2 --- /dev/null +++ b/tests/ui/fn_to_numeric_cast_any.rs @@ -0,0 +1,76 @@ +#![warn(clippy::fn_to_numeric_cast_any)] +#![allow(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] + +fn foo() -> u8 { + 0 +} + +fn generic_foo(x: T) -> T { + x +} + +trait Trait { + fn static_method() -> u32 { + 2 + } +} + +struct Struct; + +impl Trait for Struct {} + +fn fn_pointer_to_integer() { + let _ = foo as i8; + let _ = foo as i16; + let _ = foo as i32; + let _ = foo as i64; + let _ = foo as i128; + let _ = foo as isize; + + let _ = foo as u8; + let _ = foo as u16; + let _ = foo as u32; + let _ = foo as u64; + let _ = foo as u128; + let _ = foo as usize; +} + +fn static_method_to_integer() { + let _ = Struct::static_method as usize; +} + +fn fn_with_fn_arg(f: fn(i32) -> u32) -> usize { + f as usize +} + +fn fn_with_generic_static_trait_method() -> usize { + T::static_method as usize +} + +fn closure_to_fn_to_integer() { + let clos = |x| x * 2_u32; + + let _ = (clos as fn(u32) -> u32) as usize; +} + +fn fn_to_raw_ptr() { + let _ = foo as *const (); +} + +fn cast_fn_to_self() { + // Casting to the same function pointer type should be permitted. + let _ = foo as fn() -> u8; +} + +fn cast_generic_to_concrete() { + // Casting to a more concrete function pointer type should be permitted. + let _ = generic_foo as fn(usize) -> usize; +} + +fn cast_closure_to_fn() { + // Casting a closure to a function pointer should be permitted. + let id = |x| x; + let _ = id as fn(usize) -> usize; +} + +fn main() {} diff --git a/tests/ui/fn_to_numeric_cast_any.stderr b/tests/ui/fn_to_numeric_cast_any.stderr new file mode 100644 index 0000000000000..a6c4a77672f86 --- /dev/null +++ b/tests/ui/fn_to_numeric_cast_any.stderr @@ -0,0 +1,106 @@ +error: casting function pointer `foo` to `i8` + --> $DIR/fn_to_numeric_cast_any.rs:23:13 + | +LL | let _ = foo as i8; + | ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i8` + | + = note: `-D clippy::fn-to-numeric-cast-any` implied by `-D warnings` + +error: casting function pointer `foo` to `i16` + --> $DIR/fn_to_numeric_cast_any.rs:24:13 + | +LL | let _ = foo as i16; + | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i16` + +error: casting function pointer `foo` to `i32` + --> $DIR/fn_to_numeric_cast_any.rs:25:13 + | +LL | let _ = foo as i32; + | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i32` + +error: casting function pointer `foo` to `i64` + --> $DIR/fn_to_numeric_cast_any.rs:26:13 + | +LL | let _ = foo as i64; + | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i64` + +error: casting function pointer `foo` to `i128` + --> $DIR/fn_to_numeric_cast_any.rs:27:13 + | +LL | let _ = foo as i128; + | ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i128` + +error: casting function pointer `foo` to `isize` + --> $DIR/fn_to_numeric_cast_any.rs:28:13 + | +LL | let _ = foo as isize; + | ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as isize` + +error: casting function pointer `foo` to `u8` + --> $DIR/fn_to_numeric_cast_any.rs:30:13 + | +LL | let _ = foo as u8; + | ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u8` + +error: casting function pointer `foo` to `u16` + --> $DIR/fn_to_numeric_cast_any.rs:31:13 + | +LL | let _ = foo as u16; + | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u16` + +error: casting function pointer `foo` to `u32` + --> $DIR/fn_to_numeric_cast_any.rs:32:13 + | +LL | let _ = foo as u32; + | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u32` + +error: casting function pointer `foo` to `u64` + --> $DIR/fn_to_numeric_cast_any.rs:33:13 + | +LL | let _ = foo as u64; + | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u64` + +error: casting function pointer `foo` to `u128` + --> $DIR/fn_to_numeric_cast_any.rs:34:13 + | +LL | let _ = foo as u128; + | ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u128` + +error: casting function pointer `foo` to `usize` + --> $DIR/fn_to_numeric_cast_any.rs:35:13 + | +LL | let _ = foo as usize; + | ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as usize` + +error: casting function pointer `Struct::static_method` to `usize` + --> $DIR/fn_to_numeric_cast_any.rs:39:13 + | +LL | let _ = Struct::static_method as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `Struct::static_method() as usize` + +error: casting function pointer `f` to `usize` + --> $DIR/fn_to_numeric_cast_any.rs:43:5 + | +LL | f as usize + | ^^^^^^^^^^ help: did you mean to invoke the function?: `f() as usize` + +error: casting function pointer `T::static_method` to `usize` + --> $DIR/fn_to_numeric_cast_any.rs:47:5 + | +LL | T::static_method as usize + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `T::static_method() as usize` + +error: casting function pointer `(clos as fn(u32) -> u32)` to `usize` + --> $DIR/fn_to_numeric_cast_any.rs:53:13 + | +LL | let _ = (clos as fn(u32) -> u32) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `(clos as fn(u32) -> u32)() as usize` + +error: casting function pointer `foo` to `*const ()` + --> $DIR/fn_to_numeric_cast_any.rs:57:13 + | +LL | let _ = foo as *const (); + | ^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as *const ()` + +error: aborting due to 17 previous errors + From 412b862fba9df1219ab4fb5bd5a096bfbdaa37d2 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 1 Oct 2021 23:25:48 -0400 Subject: [PATCH 005/101] Add undocumented_unsafe_blocks lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_restriction.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/undocumented_unsafe_blocks.rs | 225 ++++++++++++++ tests/ui/undocumented_unsafe_blocks.rs | 287 ++++++++++++++++++ tests/ui/undocumented_unsafe_blocks.stderr | 159 ++++++++++ 7 files changed, 676 insertions(+) create mode 100644 clippy_lints/src/undocumented_unsafe_blocks.rs create mode 100644 tests/ui/undocumented_unsafe_blocks.rs create mode 100644 tests/ui/undocumented_unsafe_blocks.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index b13a5d760026f..13067e4e92ae3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3033,6 +3033,7 @@ Released 2018-09-13 [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds +[`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 3c6d8103b6da0..3c4b720671a66 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -465,6 +465,7 @@ store.register_lints(&[ types::REDUNDANT_ALLOCATION, 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, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 503f4f64a0e2b..3d68a6e900958 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -57,6 +57,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(strings::STR_TO_STRING), LintId::of(types::RC_BUFFER), LintId::of(types::RC_MUTEX), + LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS), LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(verbose_file_reads::VERBOSE_FILE_READS), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9fc6a9e0ccca0..7e1bcbbd0ed04 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -358,6 +358,7 @@ mod transmute; mod transmuting_null; mod try_err; mod types; +mod undocumented_unsafe_blocks; mod undropped_manually_drops; mod unicode; mod unit_return_expecting_ord; @@ -769,6 +770,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic)); let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send; store.register_late_pass(move || Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic_for_send))); + store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default())); } #[rustfmt::skip] diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs new file mode 100644 index 0000000000000..e08e4d03c7efe --- /dev/null +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -0,0 +1,225 @@ +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet}; +use clippy_utils::{in_macro, is_lint_allowed}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, HirId, Local, UnsafeSource}; +use rustc_lexer::TokenKind; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +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; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `unsafe` blocks without a `// Safety: ` comment + /// explaining why the unsafe operations performed inside + /// the block are safe. + /// + /// ### Why is this bad? + /// Undocumented unsafe blocks can make it difficult to + /// read and maintain code, as well as uncover unsoundness + /// and bugs. + /// + /// ### Example + /// ```rust + /// use std::ptr::NonNull; + /// let a = &mut 42; + /// + /// let ptr = unsafe { NonNull::new_unchecked(a) }; + /// ``` + /// Use instead: + /// ```rust + /// use std::ptr::NonNull; + /// let a = &mut 42; + /// + /// // Safety: references are guaranteed to be non-null. + /// let ptr = unsafe { NonNull::new_unchecked(a) }; + /// ``` + pub UNDOCUMENTED_UNSAFE_BLOCKS, + restriction, + "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, +} + +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 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); + } + } + } + } + + 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; + } + } +} + +impl<'hir> Visitor<'hir> for UndocumentedUnsafeBlocks { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + 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), + } + } +} + +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 in_macro(block_span) { + self.macro_expansion = true; + enclosing_scope_span.with_hi(block_span.hi()) + } else { + self.macro_expansion = false; + enclosing_scope_span.to(block_span) + }; + + 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 + 1) as usize; + let src_str = source_file.src.as_ref()?[lex_start..between_span.hi().0 as usize].to_string(); + + 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_with_col_display(BytePos((lex_start + 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; + } + }, + } + + 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'); + } + + 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, + ); + } + } +} diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs new file mode 100644 index 0000000000000..52577323a5837 --- /dev/null +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -0,0 +1,287 @@ +#![warn(clippy::undocumented_unsafe_blocks)] + +// Valid comments + +fn nested_local() { + let _ = { + let _ = { + // Safety: + let _ = unsafe {}; + }; + }; +} + +fn deep_nest() { + let _ = { + let _ = { + // Safety: + let _ = unsafe {}; + + // Safety: + unsafe {}; + + let _ = { + let _ = { + let _ = { + let _ = { + let _ = { + // Safety: + let _ = unsafe {}; + + // Safety: + unsafe {}; + }; + }; + }; + + // Safety: + unsafe {}; + }; + }; + }; + + // Safety: + unsafe {}; + }; + + // Safety: + unsafe {}; +} + +fn local_tuple_expression() { + // Safety: + let _ = (42, unsafe {}); +} + +fn line_comment() { + // Safety: + unsafe {} +} + +fn line_comment_newlines() { + // Safety: + + unsafe {} +} + +fn line_comment_empty() { + // Safety: + // + // + // + unsafe {} +} + +fn line_comment_with_extras() { + // This is a description + // Safety: + unsafe {} +} + +fn block_comment() { + /* Safety: */ + unsafe {} +} + +fn block_comment_newlines() { + /* Safety: */ + + unsafe {} +} + +#[rustfmt::skip] +fn inline_block_comment() { + /* Safety: */unsafe {} +} + +fn block_comment_with_extras() { + /* This is a description + * Safety: + */ + unsafe {} +} + +fn block_comment_terminator_same_line() { + /* This is a description + * Safety: */ + unsafe {} +} + +fn buried_safety() { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint + // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + // laborum. Safety: + // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi + // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio + // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl + // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. + unsafe {} +} + +fn safety_with_prepended_text() { + // This is a test. Safety: + unsafe {} +} + +fn local_line_comment() { + // Safety: + let _ = unsafe {}; +} + +fn local_block_comment() { + /* Safety: */ + let _ = unsafe {}; +} + +fn comment_array() { + // Safety: + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; +} + +fn comment_tuple() { + // Safety: + let _ = (42, unsafe {}, "test", unsafe {}); +} + +fn comment_unary() { + // Safety: + let _ = *unsafe { &42 }; +} + +#[allow(clippy::match_single_binding)] +fn comment_match() { + // Safety: + let _ = match unsafe {} { + _ => {}, + }; +} + +fn comment_addr_of() { + // Safety: + let _ = &unsafe {}; +} + +fn comment_repeat() { + // Safety: + let _ = [unsafe {}; 5]; +} + +fn comment_macro_call() { + macro_rules! t { + ($b:expr) => { + $b + }; + } + + t!( + // Safety: + unsafe {} + ); +} + +fn comment_macro_def() { + macro_rules! t { + () => { + // Safety: + unsafe {} + }; + } + + t!(); +} + +fn non_ascii_comment() { + // ॐ᧻໒ Safety: ௵∰ + unsafe {}; +} + +fn local_commented_block() { + let _ = + // Safety: + unsafe {}; +} + +fn local_nest() { + // Safety: + let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})]; +} + +// Invalid comments + +fn no_comment() { + unsafe {} +} + +fn no_comment_array() { + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; +} + +fn no_comment_tuple() { + let _ = (42, unsafe {}, "test", unsafe {}); +} + +fn no_comment_unary() { + let _ = *unsafe { &42 }; +} + +#[allow(clippy::match_single_binding)] +fn no_comment_match() { + let _ = match unsafe {} { + _ => {}, + }; +} + +fn no_comment_addr_of() { + let _ = &unsafe {}; +} + +fn no_comment_repeat() { + let _ = [unsafe {}; 5]; +} + +fn local_no_comment() { + let _ = unsafe {}; +} + +fn no_comment_macro_call() { + macro_rules! t { + ($b:expr) => { + $b + }; + } + + t!(unsafe {}); +} + +fn no_comment_macro_def() { + macro_rules! t { + () => { + unsafe {} + }; + } + + t!(); +} + +fn trailing_comment() { + unsafe {} // Safety: +} + +fn internal_comment() { + unsafe { + // Safety: + } +} + +fn interference() { + // Safety + + let _ = 42; + + unsafe {}; +} + +fn main() {} diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr new file mode 100644 index 0000000000000..b32069a334ca9 --- /dev/null +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -0,0 +1,159 @@ +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:215:5 + | +LL | unsafe {} + | ^^^^^^^^^ + | + = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + unsafe {} + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:219:5 + | +LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:223:5 + | +LL | let _ = (42, unsafe {}, "test", unsafe {}); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = (42, unsafe {}, "test", unsafe {}); + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:227:5 + | +LL | let _ = *unsafe { &42 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = *unsafe { &42 }; + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:232:5 + | +LL | let _ = match unsafe {} { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = match unsafe {} { + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:238:5 + | +LL | let _ = &unsafe {}; + | ^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = &unsafe {}; + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:242:5 + | +LL | let _ = [unsafe {}; 5]; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = [unsafe {}; 5]; + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:246:5 + | +LL | let _ = unsafe {}; + | ^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = unsafe {}; + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:256:8 + | +LL | t!(unsafe {}); + | ^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ t!(// Safety: ... +LL ~ unsafe {}); + | + +error: unsafe block in macro expansion missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:262:13 + | +LL | unsafe {} + | ^^^^^^^^^ +... +LL | t!(); + | ----- in this macro invocation + | + = help: consider adding a safety comment in the macro definition + = 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 + | +LL | unsafe {} // Safety: + | ^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL ~ unsafe {} // Safety: + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:274:5 + | +LL | unsafe { + | ^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + unsafe { + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:284:5 + | +LL | unsafe {}; + | ^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL ~ unsafe {}; + | + +error: aborting due to 13 previous errors + From 28fb591b6e681493b6ba3917146fbfe410dd45d0 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 8 Oct 2021 15:26:10 +0200 Subject: [PATCH 006/101] Do not expand macros in equatable_if_let suggestion --- clippy_lints/src/equatable_if_let.rs | 8 ++++---- tests/ui/equatable_if_let.fixed | 9 +++++++++ tests/ui/equatable_if_let.rs | 9 +++++++++ tests/ui/equatable_if_let.stderr | 8 +++++++- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index 0c6ba91c9430b..e8b1d6f6edaaa 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_errors::Applicability; @@ -77,9 +77,9 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { let pat_str = match pat.kind { PatKind::Struct(..) => format!( "({})", - snippet_with_applicability(cx, pat.span, "..", &mut applicability), + snippet_with_context(cx, pat.span, expr.span.ctxt(), "..", &mut applicability).0, ), - _ => snippet_with_applicability(cx, pat.span, "..", &mut applicability).to_string(), + _ => snippet_with_context(cx, pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), }; span_lint_and_sugg( cx, @@ -89,7 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { "try", format!( "{} == {}", - snippet_with_applicability(cx, exp.span, "..", &mut applicability), + snippet_with_context(cx, exp.span, expr.span.ctxt(), "..", &mut applicability).0, pat_str, ), applicability, diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index ba72cc237b4a5..88918d9671e42 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -66,4 +66,13 @@ fn main() { if g == NotStructuralEq::A {} if let Some(NotPartialEq::A) = Some(f) {} if Some(g) == Some(NotStructuralEq::A) {} + + macro_rules! m1 { + (x) => { + "abc" + }; + } + if "abc" == m1!(x) { + println!("OK"); + } } diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index 12526ca193db6..9a7ab75ef450f 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -66,4 +66,13 @@ fn main() { if let NotStructuralEq::A = g {} if let Some(NotPartialEq::A) = Some(f) {} if let Some(NotStructuralEq::A) = Some(g) {} + + macro_rules! m1 { + (x) => { + "abc" + }; + } + if let m1!(x) = "abc" { + println!("OK"); + } } diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr index 79ef919384df2..760ff88f448f0 100644 --- a/tests/ui/equatable_if_let.stderr +++ b/tests/ui/equatable_if_let.stderr @@ -60,5 +60,11 @@ error: this pattern matching can be expressed using equality LL | if let Some(NotStructuralEq::A) = Some(g) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)` -error: aborting due to 10 previous errors +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:75:8 + | +LL | if let m1!(x) = "abc" { + | ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)` + +error: aborting due to 11 previous errors From b423b85ca99c2a085797e228ec405038bbd3308f Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 8 Oct 2021 16:16:56 +0200 Subject: [PATCH 007/101] Don't trigger semicolon_if_nothing_returned in expanded code Before this lint didn't trigger on macros. With rust-lang/rust#88175 this isn't enough anymore. In this PR a `WhileLoop` desugaring kind was introduced. This overrides the span of expanded expressions when lowering the while loop. So if a while loop is in a macro, the expressions that it expands to are no longer marked with `ExpnKind::Macro`, but with `ExpnKind::Desugaring`. In general, this is the correct behavior and the same that is done for `ForLoop`s. It just tripped up this lint. --- clippy_lints/src/semicolon_if_nothing_returned.rs | 4 ++-- tests/ui/semicolon_if_nothing_returned.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 6966230156cfa..c0e4914efe0bd 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,7 +1,7 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{in_macro, sugg}; +use clippy_utils::sugg; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, ExprKind}; @@ -39,7 +39,7 @@ declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED] impl LateLintPass<'_> for SemicolonIfNothingReturned { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if_chain! { - if !in_macro(block.span); + if !block.span.from_expansion(); if let Some(expr) = block.expr; let t_expr = cx.typeck_results().expr_ty(expr); if t_expr.is_unit(); diff --git a/tests/ui/semicolon_if_nothing_returned.rs b/tests/ui/semicolon_if_nothing_returned.rs index 9644a23296831..7a45f1b18d4af 100644 --- a/tests/ui/semicolon_if_nothing_returned.rs +++ b/tests/ui/semicolon_if_nothing_returned.rs @@ -98,3 +98,15 @@ fn unsafe_checks() { let mut s = MaybeUninit::::uninit(); let _d = || unsafe { ptr::drop_in_place(s.as_mut_ptr()) }; } + +// Issue #7768 +#[rustfmt::skip] +fn macro_with_semicolon() { + macro_rules! repro { + () => { + while false { + } + }; + } + repro!(); +} From 72078faf9122e99f496f3f868d5e4ff59cfa0b00 Mon Sep 17 00:00:00 2001 From: James Hinshelwood Date: Fri, 8 Oct 2021 20:45:44 +0100 Subject: [PATCH 008/101] Allow giving reasons for `disallowed_types` Co-authored-by: James Hinshelwood --- clippy_lints/src/disallowed_type.rs | 59 +++++++++++-------- clippy_lints/src/lib.rs | 4 +- clippy_lints/src/utils/conf.rs | 10 +++- .../ui-toml/toml_disallowed_type/clippy.toml | 6 +- .../conf_disallowed_type.rs | 4 ++ .../conf_disallowed_type.stderr | 34 +++++++---- 6 files changed, 79 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/disallowed_type.rs b/clippy_lints/src/disallowed_type.rs index 87124f093a86d..261e9af11c821 100644 --- a/clippy_lints/src/disallowed_type.rs +++ b/clippy_lints/src/disallowed_type.rs @@ -1,12 +1,14 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, TraitBoundModifier, Ty, TyKind, UseKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; + +use crate::utils::conf; declare_clippy_lint! { /// ### What it does @@ -38,33 +40,30 @@ declare_clippy_lint! { } #[derive(Clone, Debug)] pub struct DisallowedType { - disallowed: FxHashSet>, - def_ids: FxHashSet, - prim_tys: FxHashSet, + disallowed: Vec, + def_ids: FxHashMap>, + prim_tys: FxHashMap>, } impl DisallowedType { - pub fn new(disallowed: &FxHashSet) -> Self { + pub fn new(disallowed: Vec) -> Self { Self { - disallowed: disallowed - .iter() - .map(|s| s.split("::").map(Symbol::intern).collect::>()) - .collect(), - def_ids: FxHashSet::default(), - prim_tys: FxHashSet::default(), + disallowed, + def_ids: FxHashMap::default(), + prim_tys: FxHashMap::default(), } } fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { match res { Res::Def(_, did) => { - if self.def_ids.contains(did) { - emit(cx, &cx.tcx.def_path_str(*did), span); + if let Some(reason) = self.def_ids.get(did) { + emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref()); } }, Res::PrimTy(prim) => { - if self.prim_tys.contains(prim) { - emit(cx, prim.name_str(), span); + if let Some(reason) = self.prim_tys.get(prim) { + emit(cx, prim.name_str(), span, reason.as_deref()); } }, _ => {}, @@ -76,14 +75,21 @@ impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]); impl<'tcx> LateLintPass<'tcx> for DisallowedType { fn check_crate(&mut self, cx: &LateContext<'_>) { - for path in &self.disallowed { - let segs = path.iter().map(ToString::to_string).collect::>(); - match clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::>()) { + for conf in &self.disallowed { + let (path, reason) = match conf { + conf::DisallowedType::Simple(path) => (path, None), + conf::DisallowedType::WithReason { path, reason } => ( + path, + reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)), + ), + }; + let segs: Vec<_> = path.split("::").collect(); + match clippy_utils::path_to_res(cx, &segs) { Res::Def(_, id) => { - self.def_ids.insert(id); + self.def_ids.insert(id, reason); }, Res::PrimTy(ty) => { - self.prim_tys.insert(ty); + self.prim_tys.insert(ty, reason); }, _ => {}, } @@ -107,11 +113,16 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedType { } } -fn emit(cx: &LateContext<'_>, name: &str, span: Span) { - span_lint( +fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) { + span_lint_and_then( cx, DISALLOWED_TYPE, span, &format!("`{}` is not allowed according to config", name), + |diag| { + if let Some(reason) = reason { + diag.note(reason); + } + }, ); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7e1bcbbd0ed04..8af76cbed6e44 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -757,8 +757,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); store.register_late_pass(|| Box::new(unused_async::UnusedAsync)); - let disallowed_types = conf.disallowed_types.iter().cloned().collect::>(); - store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(&disallowed_types))); + let disallowed_types = conf.disallowed_types.clone(); + store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(disallowed_types.clone()))); let import_renames = conf.enforced_import_renames.clone(); store.register_late_pass(move || Box::new(missing_enforced_import_rename::ImportRename::new(import_renames.clone()))); let scripts = conf.allowed_scripts.clone(); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 6cbada4c1505b..d05c52122d5ee 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -23,6 +23,14 @@ pub enum DisallowedMethod { WithReason { path: String, reason: Option }, } +/// A single disallowed type, used by the `DISALLOWED_TYPE` lint. +#[derive(Clone, Debug, Deserialize)] +#[serde(untagged)] +pub enum DisallowedType { + Simple(String), + WithReason { path: String, reason: Option }, +} + /// Conf with parse errors #[derive(Default)] pub struct TryConf { @@ -255,7 +263,7 @@ define_Conf! { /// Lint: DISALLOWED_TYPE. /// /// The list of disallowed types, written as fully qualified paths. - (disallowed_types: Vec = Vec::new()), + (disallowed_types: Vec = Vec::new()), /// Lint: UNREADABLE_LITERAL. /// /// Should the fraction of a decimal be linted to include separators. diff --git a/tests/ui-toml/toml_disallowed_type/clippy.toml b/tests/ui-toml/toml_disallowed_type/clippy.toml index dac4446703b0f..6cb9e2ef95467 100644 --- a/tests/ui-toml/toml_disallowed_type/clippy.toml +++ b/tests/ui-toml/toml_disallowed_type/clippy.toml @@ -7,5 +7,9 @@ disallowed-types = [ "std::time::Instant", "std::io::Read", "std::primitive::usize", - "bool" + "bool", + # can give path and reason with an inline table + { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" }, + # can use an inline table but omit reason + { path = "std::net::TcpListener" }, ] diff --git a/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs index 0871a3073abd3..410f076505511 100644 --- a/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs +++ b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs @@ -25,6 +25,10 @@ struct GenArg([u8; U]); static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut()); +fn ip(_: std::net::Ipv4Addr) {} + +fn listener(_: std::net::TcpListener) {} + #[allow(clippy::diverging_sub_expression)] fn main() { let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); diff --git a/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr index 90ce7db2cc4e6..08a400a83675b 100644 --- a/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr +++ b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr @@ -60,59 +60,73 @@ error: `usize` is not allowed according to config LL | struct GenArg([u8; U]); | ^^^^^ +error: `std::net::Ipv4Addr` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:28:10 + | +LL | fn ip(_: std::net::Ipv4Addr) {} + | ^^^^^^^^^^^^^^^^^^ + | + = note: no IPv4 allowed (from clippy.toml) + +error: `std::net::TcpListener` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:30:16 + | +LL | fn listener(_: std::net::TcpListener) {} + | ^^^^^^^^^^^^^^^^^^^^^ + error: `std::collections::HashMap` is not allowed according to config - --> $DIR/conf_disallowed_type.rs:30:48 + --> $DIR/conf_disallowed_type.rs:34:48 | LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `std::collections::HashMap` is not allowed according to config - --> $DIR/conf_disallowed_type.rs:30:12 + --> $DIR/conf_disallowed_type.rs:34:12 | LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `std::time::Instant` is not allowed according to config - --> $DIR/conf_disallowed_type.rs:31:13 + --> $DIR/conf_disallowed_type.rs:35:13 | LL | let _ = Sneaky::now(); | ^^^^^^ error: `std::sync::atomic::AtomicU32` is not allowed according to config - --> $DIR/conf_disallowed_type.rs:32:13 + --> $DIR/conf_disallowed_type.rs:36:13 | LL | let _ = foo::atomic::AtomicU32::new(0); | ^^^^^^^^^^^^^^^^^^^^^^ error: `std::sync::atomic::AtomicU32` is not allowed according to config - --> $DIR/conf_disallowed_type.rs:33:17 + --> $DIR/conf_disallowed_type.rs:37:17 | LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `std::sync::atomic::AtomicU32` is not allowed according to config - --> $DIR/conf_disallowed_type.rs:33:48 + --> $DIR/conf_disallowed_type.rs:37:48 | LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); | ^^^^^^^^^^^^^^^^^^^^^^ error: `syn::TypePath` is not allowed according to config - --> $DIR/conf_disallowed_type.rs:34:43 + --> $DIR/conf_disallowed_type.rs:38:43 | LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default(); | ^^^^^^^^^^^^^ error: `syn::Ident` is not allowed according to config - --> $DIR/conf_disallowed_type.rs:35:13 + --> $DIR/conf_disallowed_type.rs:39:13 | LL | let _ = syn::Ident::new("", todo!()); | ^^^^^^^^^^ error: `usize` is not allowed according to config - --> $DIR/conf_disallowed_type.rs:37:12 + --> $DIR/conf_disallowed_type.rs:41:12 | LL | let _: usize = 64_usize; | ^^^^^ -error: aborting due to 19 previous errors +error: aborting due to 21 previous errors From da76c06209451fa01d5248374869b689116ae05e Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sat, 9 Oct 2021 05:58:05 +0200 Subject: [PATCH 009/101] Add `--msrv` option to `new_lint` command --- clippy_dev/src/main.rs | 6 +++ clippy_dev/src/new_lint.rs | 108 +++++++++++++++++++++++++++++++------ 2 files changed, 98 insertions(+), 16 deletions(-) diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 8fdeba9842af3..b5c04efce3bc9 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -28,6 +28,7 @@ fn main() { matches.value_of("pass"), matches.value_of("name"), matches.value_of("category"), + matches.is_present("msrv"), ) { Ok(_) => update_lints::run(update_lints::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {}", e), @@ -147,6 +148,11 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { "internal_warn", ]) .takes_value(true), + ) + .arg( + Arg::with_name("msrv") + .long("msrv") + .help("Add MSRV config code to the lint"), ), ) .subcommand( diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 3a81aaba6de04..ebbe26c46f7fd 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -32,7 +32,7 @@ impl Context for io::Result { /// # Errors /// /// This function errors out if the files couldn't be created or written to. -pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> io::Result<()> { +pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>, msrv: bool) -> io::Result<()> { let lint = LintData { pass: pass.expect("`pass` argument is validated by clap"), name: lint_name.expect("`name` argument is validated by clap"), @@ -40,11 +40,11 @@ pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str project_root: clippy_project_root(), }; - create_lint(&lint).context("Unable to create lint implementation")?; + create_lint(&lint, msrv).context("Unable to create lint implementation")?; create_test(&lint).context("Unable to create a test for the new lint") } -fn create_lint(lint: &LintData<'_>) -> io::Result<()> { +fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass { "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"), @@ -55,6 +55,7 @@ fn create_lint(lint: &LintData<'_>) -> io::Result<()> { let camel_case_name = to_camel_case(lint.name); let lint_contents = get_lint_file_contents( + lint.pass, pass_type, pass_lifetimes, lint.name, @@ -62,6 +63,7 @@ fn create_lint(lint: &LintData<'_>) -> io::Result<()> { lint.category, pass_import, context_import, + enable_msrv, ); let lint_path = format!("clippy_lints/src/{}.rs", lint.name); @@ -155,6 +157,7 @@ publish = false } fn get_lint_file_contents( + pass_name: &str, pass_type: &str, pass_lifetimes: &str, lint_name: &str, @@ -162,13 +165,41 @@ fn get_lint_file_contents( category: &str, pass_import: &str, context_import: &str, + enable_msrv: bool, ) -> String { - format!( - "use rustc_lint::{{{type}, {context_import}}}; -use rustc_session::{{declare_lint_pass, declare_tool_lint}}; + let mut result = String::new(); + + let name_camel = camel_case_name; + let name_upper = lint_name.to_uppercase(); + + result.push_str(&if enable_msrv { + format!( + "use clippy_utils::msrvs; {pass_import} +use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; +use rustc_semver::RustcVersion; +use rustc_session::{{declare_tool_lint, impl_lint_pass}}; + +", + pass_type = pass_type, + pass_import = pass_import, + context_import = context_import, + ) + } else { + format!( + "{pass_import} +use rustc_lint::{{{context_import}, {pass_type}}}; +use rustc_session::{{declare_lint_pass, declare_tool_lint}}; + +", + pass_import = pass_import, + pass_type = pass_type, + context_import = context_import + ) + }); -declare_clippy_lint! {{ + result.push_str(&format!( + "declare_clippy_lint! {{ /// ### What it does /// /// ### Why is this bad? @@ -184,20 +215,65 @@ declare_clippy_lint! {{ pub {name_upper}, {category}, \"default lint description\" +}}", + name_upper = name_upper, + category = category, + )); + + result.push_str(&if enable_msrv { + format!( + " +pub struct {name_camel} {{ + msrv: Option, +}} + +impl {name_camel} {{ + #[must_use] + pub fn new(msrv: Option) -> Self {{ + Self {{ msrv }} + }} +}} + +impl_lint_pass!({name_camel} => [{name_upper}]); + +impl {pass_type}{pass_lifetimes} for {name_camel} {{ + extract_msrv_attr!({context_import}); }} +// TODO: Register the lint pass in `clippy_lints/src/lib.rs`, +// e.g. store.register_{pass_name}_pass(move || Box::new({module_name}::{name_camel}::new(msrv))); +// TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. +// TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`. +// TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` +", + pass_type = pass_type, + pass_lifetimes = pass_lifetimes, + pass_name = pass_name, + name_upper = name_upper, + name_camel = name_camel, + module_name = lint_name, + context_import = context_import, + ) + } else { + format!( + " declare_lint_pass!({name_camel} => [{name_upper}]); -impl {type}{lifetimes} for {name_camel} {{}} +impl {pass_type}{pass_lifetimes} for {name_camel} {{}} +// +// TODO: Register the lint pass in `clippy_lints/src/lib.rs`, +// e.g. store.register_{pass_name}_pass(|| Box::new({module_name}::{name_camel})); ", - type=pass_type, - lifetimes=pass_lifetimes, - name_upper=lint_name.to_uppercase(), - name_camel=camel_case_name, - category=category, - pass_import=pass_import, - context_import=context_import - ) + pass_type = pass_type, + pass_lifetimes = pass_lifetimes, + pass_name = pass_name, + name_upper = name_upper, + name_camel = name_camel, + module_name = lint_name, + ) + }); + + result } #[test] From b8782257ae54674101b08caeee59c0d51ff760c6 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sat, 9 Oct 2021 05:58:05 +0200 Subject: [PATCH 010/101] Fix `clippy::too-many-arguments` violation --- clippy_dev/src/new_lint.rs | 46 ++++++++++++-------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index ebbe26c46f7fd..50297efde21e5 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -45,26 +45,7 @@ pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str } fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { - let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass { - "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), - "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"), - _ => { - unreachable!("`pass_type` should only ever be `early` or `late`!"); - }, - }; - - let camel_case_name = to_camel_case(lint.name); - let lint_contents = get_lint_file_contents( - lint.pass, - pass_type, - pass_lifetimes, - lint.name, - &camel_case_name, - lint.category, - pass_import, - context_import, - enable_msrv, - ); + let lint_contents = get_lint_file_contents(lint, enable_msrv); let lint_path = format!("clippy_lints/src/{}.rs", lint.name); write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes()) @@ -156,20 +137,21 @@ publish = false ) } -fn get_lint_file_contents( - pass_name: &str, - pass_type: &str, - pass_lifetimes: &str, - lint_name: &str, - camel_case_name: &str, - category: &str, - pass_import: &str, - context_import: &str, - enable_msrv: bool, -) -> String { +fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { let mut result = String::new(); - let name_camel = camel_case_name; + let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass { + "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), + "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"), + _ => { + unreachable!("`pass_type` should only ever be `early` or `late`!"); + }, + }; + + let lint_name = lint.name; + let pass_name = lint.pass; + let category = lint.category; + let name_camel = to_camel_case(lint.name); let name_upper = lint_name.to_uppercase(); result.push_str(&if enable_msrv { From 7d9b21b90bc77ab26c8afefc78d6087aff34e4ad Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sat, 9 Oct 2021 05:58:05 +0200 Subject: [PATCH 011/101] Use `indoc` for formatting --- clippy_dev/Cargo.toml | 1 + clippy_dev/src/new_lint.rs | 152 +++++++++++++++++++------------------ 2 files changed, 80 insertions(+), 73 deletions(-) diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 4a13a45240976..affb283017c8c 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] bytecount = "0.6" clap = "2.33" +indoc = "1.0" itertools = "0.10" opener = "0.5" regex = "1.5" diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 50297efde21e5..25320907bb492 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,4 +1,5 @@ use crate::clippy_project_root; +use indoc::indoc; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::io::{self, ErrorKind}; @@ -105,12 +106,13 @@ fn to_camel_case(name: &str) -> String { fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { let mut contents = format!( - "#![warn(clippy::{})] + indoc! {" + #![warn(clippy::{})] -fn main() {{ - // test code goes here -}} -", + fn main() {{ + // test code goes here + }} + "}, lint_name ); @@ -123,16 +125,16 @@ fn main() {{ fn get_manifest_contents(lint_name: &str, hint: &str) -> String { format!( - r#" -# {} + indoc! {r#" + # {} -[package] -name = "{}" -version = "0.1.0" -publish = false + [package] + name = "{}" + version = "0.1.0" + publish = false -[workspace] -"#, + [workspace] + "#}, hint, lint_name ) } @@ -156,24 +158,26 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result.push_str(&if enable_msrv { format!( - "use clippy_utils::msrvs; -{pass_import} -use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; -use rustc_semver::RustcVersion; -use rustc_session::{{declare_tool_lint, impl_lint_pass}}; - -", + indoc! {" + use clippy_utils::msrvs; + {pass_import} + use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; + use rustc_semver::RustcVersion; + use rustc_session::{{declare_tool_lint, impl_lint_pass}}; + + "}, pass_type = pass_type, pass_import = pass_import, context_import = context_import, ) } else { format!( - "{pass_import} -use rustc_lint::{{{context_import}, {pass_type}}}; -use rustc_session::{{declare_lint_pass, declare_tool_lint}}; + indoc! {" + {pass_import} + use rustc_lint::{{{context_import}, {pass_type}}}; + use rustc_session::{{declare_lint_pass, declare_tool_lint}}; -", + "}, pass_import = pass_import, pass_type = pass_type, context_import = context_import @@ -181,53 +185,55 @@ use rustc_session::{{declare_lint_pass, declare_tool_lint}}; }); result.push_str(&format!( - "declare_clippy_lint! {{ - /// ### What it does - /// - /// ### Why is this bad? - /// - /// ### Example - /// ```rust - /// // example code where clippy issues a warning - /// ``` - /// Use instead: - /// ```rust - /// // example code which does not raise clippy warning - /// ``` - pub {name_upper}, - {category}, - \"default lint description\" -}}", + indoc! {" + declare_clippy_lint! {{ + /// ### What it does + /// + /// ### Why is this bad? + /// + /// ### Example + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub {name_upper}, + {category}, + \"default lint description\" + }} + "}, name_upper = name_upper, category = category, )); result.push_str(&if enable_msrv { format!( - " -pub struct {name_camel} {{ - msrv: Option, -}} - -impl {name_camel} {{ - #[must_use] - pub fn new(msrv: Option) -> Self {{ - Self {{ msrv }} - }} -}} - -impl_lint_pass!({name_camel} => [{name_upper}]); - -impl {pass_type}{pass_lifetimes} for {name_camel} {{ - extract_msrv_attr!({context_import}); -}} - -// TODO: Register the lint pass in `clippy_lints/src/lib.rs`, -// e.g. store.register_{pass_name}_pass(move || Box::new({module_name}::{name_camel}::new(msrv))); -// TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. -// TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`. -// TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` -", + indoc! {" + pub struct {name_camel} {{ + msrv: Option, + }} + + impl {name_camel} {{ + #[must_use] + pub fn new(msrv: Option) -> Self {{ + Self {{ msrv }} + }} + }} + + impl_lint_pass!({name_camel} => [{name_upper}]); + + impl {pass_type}{pass_lifetimes} for {name_camel} {{ + extract_msrv_attr!({context_import}); + }} + + // TODO: Register the lint pass in `clippy_lints/src/lib.rs`, + // e.g. store.register_{pass_name}_pass(move || Box::new({module_name}::{name_camel}::new(msrv))); + // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. + // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`. + // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` + "}, pass_type = pass_type, pass_lifetimes = pass_lifetimes, pass_name = pass_name, @@ -238,14 +244,14 @@ impl {pass_type}{pass_lifetimes} for {name_camel} {{ ) } else { format!( - " -declare_lint_pass!({name_camel} => [{name_upper}]); - -impl {pass_type}{pass_lifetimes} for {name_camel} {{}} -// -// TODO: Register the lint pass in `clippy_lints/src/lib.rs`, -// e.g. store.register_{pass_name}_pass(|| Box::new({module_name}::{name_camel})); -", + indoc! {" + declare_lint_pass!({name_camel} => [{name_upper}]); + + impl {pass_type}{pass_lifetimes} for {name_camel} {{}} + // + // TODO: Register the lint pass in `clippy_lints/src/lib.rs`, + // e.g. store.register_{pass_name}_pass(|| Box::new({module_name}::{name_camel})); + "}, pass_type = pass_type, pass_lifetimes = pass_lifetimes, pass_name = pass_name, From 452181c69d2a106d9d5a1262b69e68a765c4b3e3 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Fri, 17 Sep 2021 02:55:26 -0400 Subject: [PATCH 012/101] Implement uninit_vec lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_correctness.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/methods/uninit_assumed_init.rs | 14 +- clippy_lints/src/uninit_vec.rs | 174 ++++++++++++++++++ clippy_utils/src/lib.rs | 10 + clippy_utils/src/paths.rs | 3 + tests/ui/uninit_vec.rs | 32 ++++ tests/ui/uninit_vec.stderr | 51 +++++ 11 files changed, 278 insertions(+), 12 deletions(-) create mode 100644 clippy_lints/src/uninit_vec.rs create mode 100644 tests/ui/uninit_vec.rs create mode 100644 tests/ui/uninit_vec.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 13067e4e92ae3..2c69432f31c3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3038,6 +3038,7 @@ Released 2018-09-13 [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init +[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp [`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 3e6e0244754fb..f0ef0befea037 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -278,6 +278,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ 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_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(unit_types::UNIT_ARG), LintId::of(unit_types::UNIT_CMP), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index e0ef7b3b8af9f..8eb662195e231 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -63,6 +63,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve 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_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(unit_types::UNIT_CMP), LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 3c4b720671a66..401c5b78b3830 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -470,6 +470,7 @@ store.register_lints(&[ unicode::INVISIBLE_CHARACTERS, unicode::NON_ASCII_LITERAL, unicode::UNICODE_NOT_NFC, + uninit_vec::UNINIT_VEC, unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, unit_types::LET_UNIT_VALUE, unit_types::UNIT_ARG, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7e1bcbbd0ed04..829d5acdfde36 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -361,6 +361,7 @@ mod types; mod undocumented_unsafe_blocks; mod undropped_manually_drops; mod unicode; +mod uninit_vec; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; @@ -518,6 +519,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions)); store.register_late_pass(|| Box::new(collapsible_match::CollapsibleMatch)); store.register_late_pass(|| Box::new(unicode::Unicode)); + store.register_late_pass(|| Box::new(uninit_vec::UninitVec)); store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)); store.register_late_pass(|| Box::new(strings::StringAdd)); store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn)); diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index 1a5894e48d14c..dbd91fb6d9de1 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_expr_path_def_path, match_def_path, paths}; +use clippy_utils::{is_expr_path_def_path, is_uninit_ty_valid, paths}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; use super::UNINIT_ASSUMED_INIT; @@ -13,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if let hir::ExprKind::Call(callee, args) = recv.kind; if args.is_empty(); if is_expr_path_def_path(cx, callee, &paths::MEM_MAYBEUNINIT_UNINIT); - if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr)); + if !is_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr)); then { span_lint( cx, @@ -24,12 +23,3 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr } } } - -fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Array(component, _) => is_maybe_uninit_ty_valid(cx, component), - ty::Tuple(types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)), - ty::Adt(adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), - _ => false, - } -} diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs new file mode 100644 index 0000000000000..ff302289a1fe8 --- /dev/null +++ b/clippy_lints/src/uninit_vec.rs @@ -0,0 +1,174 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::{is_uninit_ty_valid, match_def_path, path_to_local_id, paths, peel_hir_expr_while, SpanlessEq}; +use rustc_hir::def::Res; +use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the creation of uninitialized `Vec` by calling `set_len()` + /// immediately after `with_capacity()` or `reserve()`. + /// + /// ### Why is this bad? + /// It creates `Vec` that contains uninitialized data, which leads to an + /// undefined behavior with most safe operations. + /// Notably, using uninitialized `Vec` with generic `Read` is unsound. + /// + /// ### Example + /// ```rust,ignore + /// let mut vec: Vec = Vec::with_capacity(1000); + /// unsafe { vec.set_len(1000); } + /// reader.read(&mut vec); // undefined behavior! + /// ``` + /// Use an initialized buffer: + /// ```rust,ignore + /// let mut vec: Vec = vec![0; 1000]; + /// reader.read(&mut vec); + /// ``` + /// Or, wrap the content in `MaybeUninit`: + /// ```rust,ignore + /// let mut vec: Vec> = Vec::with_capacity(1000); + /// unsafe { vec.set_len(1000); } + /// ``` + pub UNINIT_VEC, + correctness, + "Vec with uninitialized data" +} + +declare_lint_pass!(UninitVec => [UNINIT_VEC]); + +impl<'tcx> LateLintPass<'tcx> for UninitVec { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + for w in block.stmts.windows(2) { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = w[1].kind { + handle_pair(cx, &w[0], expr); + } + } + + if let (Some(stmt), Some(expr)) = (block.stmts.last(), block.expr) { + handle_pair(cx, stmt, expr); + } + } +} + +fn handle_pair(cx: &LateContext<'tcx>, first: &'tcx Stmt<'tcx>, second: &'tcx Expr<'tcx>) { + if_chain! { + if let Some(vec) = extract_with_capacity_or_reserve_target(cx, first); + if let Some((set_len_self, call_span)) = extract_set_len_self(cx, second); + if vec.eq_expr(cx, set_len_self); + if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind(); + if let ty::Adt(_, substs) = vec_ty.kind(); + // Check T of Vec + if !is_uninit_ty_valid(cx, substs.type_at(0)); + then { + span_lint_and_note( + cx, + UNINIT_VEC, + call_span, + "calling `set_len()` immediately after reserving a buffer creates uninitialized values", + Some(first.span), + "the buffer is reserved here" + ); + } + } +} + +#[derive(Clone, Copy, Debug)] +enum LocalOrExpr<'tcx> { + Local(HirId), + Expr(&'tcx Expr<'tcx>), +} + +impl<'tcx> LocalOrExpr<'tcx> { + fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + match self { + LocalOrExpr::Local(hir_id) => path_to_local_id(expr, hir_id), + LocalOrExpr::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr), + } + } +} + +/// Returns the target vec of Vec::with_capacity() or Vec::reserve() +fn extract_with_capacity_or_reserve_target(cx: &LateContext<'_>, stmt: &'tcx Stmt<'_>) -> Option> { + match stmt.kind { + StmtKind::Local(local) => { + // let mut x = Vec::with_capacity() + if_chain! { + if let Some(init_expr) = local.init; + if let PatKind::Binding(_, hir_id, _, None) = local.pat.kind; + if is_with_capacity(cx, init_expr); + then { + Some(LocalOrExpr::Local(hir_id)) + } else { + None + } + } + }, + StmtKind::Expr(expr) | StmtKind::Semi(expr) => { + match expr.kind { + ExprKind::Assign(lhs, rhs, _span) if is_with_capacity(cx, rhs) => { + // self.vec = Vec::with_capacity() + Some(LocalOrExpr::Expr(lhs)) + }, + ExprKind::MethodCall(_, _, [vec_expr, _], _) => { + // self.vec.reserve() + if_chain! { + if let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, id, &paths::VEC_RESERVE); + then { + Some(LocalOrExpr::Expr(vec_expr)) + } else { + None + } + } + }, + _ => None, + } + }, + _ => None, + } +} + +fn is_with_capacity(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> bool { + if_chain! { + if let ExprKind::Call(path_expr, _) = &expr.kind; + if let ExprKind::Path(qpath) = &path_expr.kind; + if let Res::Def(_, def_id) = cx.qpath_res(qpath, path_expr.hir_id); + then { + match_def_path(cx, def_id, &paths::VEC_WITH_CAPACITY) + } else { + false + } + } +} + +/// Returns self if the expression is Vec::set_len() +fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> { + // peel unsafe blocks in `unsafe { vec.set_len() }` + let expr = peel_hir_expr_while(expr, |e| { + if let ExprKind::Block(block, _) = e.kind { + match (block.stmts.get(0).map(|stmt| &stmt.kind), block.expr) { + (None, Some(expr)) => Some(expr), + (Some(StmtKind::Expr(expr) | StmtKind::Semi(expr)), None) => Some(expr), + _ => None, + } + } else { + None + } + }); + match expr.kind { + ExprKind::MethodCall(_, _, [vec_expr, _], _) => { + cx.typeck_results().type_dependent_def_id(expr.hir_id).and_then(|id| { + if match_def_path(cx, id, &paths::VEC_SET_LEN) { + Some((vec_expr, expr.span)) + } else { + None + } + }) + }, + _ => None, + } +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 09eee78f0d1ff..00d2098a021b6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1470,6 +1470,16 @@ pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool { false } +/// Checks if a given type looks safe to be uninitialized. +pub fn is_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + match ty.kind() { + rustc_ty::Array(component, _) => is_uninit_ty_valid(cx, component), + rustc_ty::Tuple(types) => types.types().all(|ty| is_uninit_ty_valid(cx, ty)), + rustc_ty::Adt(adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), + _ => false, + } +} + pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator> { (0..decl.inputs.len()).map(move |i| &body.params[i]) } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index e43c575602145..dffe30910dd51 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -183,5 +183,8 @@ pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"]; pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"]; +pub const VEC_RESERVE: [&str; 4] = ["alloc", "vec", "Vec", "reserve"]; +pub const VEC_WITH_CAPACITY: [&str; 4] = ["alloc", "vec", "Vec", "with_capacity"]; +pub const VEC_SET_LEN: [&str; 4] = ["alloc", "vec", "Vec", "set_len"]; pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"]; diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs new file mode 100644 index 0000000000000..5e67288405adc --- /dev/null +++ b/tests/ui/uninit_vec.rs @@ -0,0 +1,32 @@ +#![warn(clippy::uninit_vec)] + +use std::mem::MaybeUninit; + +fn main() { + // with_capacity() -> set_len() should be detected + let mut vec: Vec = Vec::with_capacity(1000); + unsafe { + vec.set_len(200); + } + + // reserve() -> set_len() should be detected + vec.reserve(1000); + unsafe { + vec.set_len(200); + } + + // test when both calls are enclosed in the same unsafe block + unsafe { + let mut vec: Vec = Vec::with_capacity(1000); + vec.set_len(200); + + vec.reserve(1000); + vec.set_len(200); + } + + // MaybeUninit-wrapped types should not be detected + let mut vec: Vec> = Vec::with_capacity(1000); + unsafe { + vec.set_len(200); + } +} diff --git a/tests/ui/uninit_vec.stderr b/tests/ui/uninit_vec.stderr new file mode 100644 index 0000000000000..1d7807fcf7d0e --- /dev/null +++ b/tests/ui/uninit_vec.stderr @@ -0,0 +1,51 @@ +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:9:9 + | +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninit-vec` implied by `-D warnings` +note: the buffer is reserved here + --> $DIR/uninit_vec.rs:7:5 + | +LL | let mut vec: Vec = Vec::with_capacity(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:15:9 + | +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | +note: the buffer is reserved here + --> $DIR/uninit_vec.rs:13:5 + | +LL | vec.reserve(1000); + | ^^^^^^^^^^^^^^^^^^ + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:21:9 + | +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | +note: the buffer is reserved here + --> $DIR/uninit_vec.rs:20:9 + | +LL | let mut vec: Vec = Vec::with_capacity(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:24:9 + | +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | +note: the buffer is reserved here + --> $DIR/uninit_vec.rs:23:9 + | +LL | vec.reserve(1000); + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + From b8ba7269cd10843e4aebf855c4f72e980a9c0ed6 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Fri, 17 Sep 2021 03:27:31 -0400 Subject: [PATCH 013/101] Fix clippy lints --- clippy_lints/src/uninit_vec.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index ff302289a1fe8..8b1254b60f911 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -91,7 +91,7 @@ impl<'tcx> LocalOrExpr<'tcx> { } } -/// Returns the target vec of Vec::with_capacity() or Vec::reserve() +/// Returns the target vec of `Vec::with_capacity()` or `Vec::reserve()` fn extract_with_capacity_or_reserve_target(cx: &LateContext<'_>, stmt: &'tcx Stmt<'_>) -> Option> { match stmt.kind { StmtKind::Local(local) => { @@ -128,7 +128,7 @@ fn extract_with_capacity_or_reserve_target(cx: &LateContext<'_>, stmt: &'tcx Stm _ => None, } }, - _ => None, + StmtKind::Item(_) => None, } } @@ -145,7 +145,7 @@ fn is_with_capacity(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> bool { } } -/// Returns self if the expression is Vec::set_len() +/// Returns self if the expression is `Vec::set_len()` fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> { // peel unsafe blocks in `unsafe { vec.set_len() }` let expr = peel_hir_expr_while(expr, |e| { From 759200b6991b5dac5fdb12bc6c366b16850add00 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Fri, 17 Sep 2021 14:42:32 -0400 Subject: [PATCH 014/101] Handle PR feedbacks first round --- .../src/methods/uninit_assumed_init.rs | 4 +- clippy_lints/src/uninit_vec.rs | 41 +++++++++++-------- clippy_utils/src/lib.rs | 10 ----- clippy_utils/src/paths.rs | 1 - clippy_utils/src/ty.rs | 10 +++++ tests/ui/uninit_vec.rs | 8 +++- 6 files changed, 42 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index dbd91fb6d9de1..ce89189bce977 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_expr_path_def_path, is_uninit_ty_valid, paths}; +use clippy_utils::{is_expr_path_def_path, paths, ty::is_uninit_value_valid_for_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if let hir::ExprKind::Call(callee, args) = recv.kind; if args.is_empty(); if is_expr_path_def_path(cx, callee, &paths::MEM_MAYBEUNINIT_UNINIT); - if !is_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr)); + if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr)); then { span_lint( cx, diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 8b1254b60f911..37084f57043b5 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,11 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::{is_uninit_ty_valid, match_def_path, path_to_local_id, paths, peel_hir_expr_while, SpanlessEq}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{ + match_def_path, path_to_local_id, paths, peel_hir_expr_while, ty::is_uninit_value_valid_for_ty, SpanlessEq, +}; use rustc_hir::def::Res; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does @@ -44,32 +47,36 @@ impl<'tcx> LateLintPass<'tcx> for UninitVec { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { for w in block.stmts.windows(2) { if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = w[1].kind { - handle_pair(cx, &w[0], expr); + handle_uninit_vec_pair(cx, &w[0], expr); } } if let (Some(stmt), Some(expr)) = (block.stmts.last(), block.expr) { - handle_pair(cx, stmt, expr); + handle_uninit_vec_pair(cx, stmt, expr); } } } -fn handle_pair(cx: &LateContext<'tcx>, first: &'tcx Stmt<'tcx>, second: &'tcx Expr<'tcx>) { +fn handle_uninit_vec_pair( + cx: &LateContext<'tcx>, + maybe_with_capacity_or_reserve: &'tcx Stmt<'tcx>, + maybe_set_len: &'tcx Expr<'tcx>, +) { if_chain! { - if let Some(vec) = extract_with_capacity_or_reserve_target(cx, first); - if let Some((set_len_self, call_span)) = extract_set_len_self(cx, second); + if let Some(vec) = extract_with_capacity_or_reserve_target(cx, maybe_with_capacity_or_reserve); + if let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len); if vec.eq_expr(cx, set_len_self); if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind(); if let ty::Adt(_, substs) = vec_ty.kind(); // Check T of Vec - if !is_uninit_ty_valid(cx, substs.type_at(0)); + if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)); then { span_lint_and_note( cx, UNINIT_VEC, call_span, "calling `set_len()` immediately after reserving a buffer creates uninitialized values", - Some(first.span), + Some(maybe_with_capacity_or_reserve.span), "the buffer is reserved here" ); } @@ -113,16 +120,14 @@ fn extract_with_capacity_or_reserve_target(cx: &LateContext<'_>, stmt: &'tcx Stm // self.vec = Vec::with_capacity() Some(LocalOrExpr::Expr(lhs)) }, - ExprKind::MethodCall(_, _, [vec_expr, _], _) => { + ExprKind::MethodCall(path, _, [self_expr, _], _) => { // self.vec.reserve() - if_chain! { - if let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if match_def_path(cx, id, &paths::VEC_RESERVE); - then { - Some(LocalOrExpr::Expr(vec_expr)) - } else { - None - } + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::vec_type) + && path.ident.name.as_str() == "reserve" + { + Some(LocalOrExpr::Expr(self_expr)) + } else { + None } }, _ => None, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 00d2098a021b6..09eee78f0d1ff 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1470,16 +1470,6 @@ pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool { false } -/// Checks if a given type looks safe to be uninitialized. -pub fn is_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - match ty.kind() { - rustc_ty::Array(component, _) => is_uninit_ty_valid(cx, component), - rustc_ty::Tuple(types) => types.types().all(|ty| is_uninit_ty_valid(cx, ty)), - rustc_ty::Adt(adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), - _ => false, - } -} - pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator> { (0..decl.inputs.len()).map(move |i| &body.params[i]) } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index dffe30910dd51..63ec10a31786d 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -183,7 +183,6 @@ pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"]; pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"]; -pub const VEC_RESERVE: [&str; 4] = ["alloc", "vec", "Vec", "reserve"]; pub const VEC_WITH_CAPACITY: [&str; 4] = ["alloc", "vec", "Vec", "with_capacity"]; pub const VEC_SET_LEN: [&str; 4] = ["alloc", "vec", "Vec", "set_len"]; pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 6ebe1a0028a31..ca64ac7de3eea 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -367,3 +367,13 @@ pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { _ => a == b, } } + +/// Checks if a given type looks safe to be uninitialized. +pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component), + ty::Tuple(types) => types.types().all(|ty| is_uninit_value_valid_for_ty(cx, ty)), + ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did), + _ => false, + } +} diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index 5e67288405adc..34b9e07ef1d4f 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -25,8 +25,14 @@ fn main() { } // MaybeUninit-wrapped types should not be detected - let mut vec: Vec> = Vec::with_capacity(1000); unsafe { + let mut vec: Vec> = Vec::with_capacity(1000); + vec.set_len(200); + + let mut vec: Vec<(MaybeUninit, MaybeUninit)> = Vec::with_capacity(1000); + vec.set_len(200); + + let mut vec: Vec<(MaybeUninit, [MaybeUninit; 2])> = Vec::with_capacity(1000); vec.set_len(200); } } From fdc06d9b2b3e858878b6fd8f6bd815d947e9950a Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Mon, 20 Sep 2021 15:32:53 -0400 Subject: [PATCH 015/101] Improve error messages --- clippy_lints/src/uninit_vec.rs | 14 ++++--- tests/ui/uninit_vec.rs | 32 ++++++++++++++++ tests/ui/uninit_vec.stderr | 69 +++++++++++++++++++++++----------- 3 files changed, 88 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 37084f57043b5..b9b575a452b12 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ match_def_path, path_to_local_id, paths, peel_hir_expr_while, ty::is_uninit_value_valid_for_ty, SpanlessEq, @@ -71,13 +71,14 @@ fn handle_uninit_vec_pair( // Check T of Vec if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)); then { - span_lint_and_note( + span_lint_and_then( cx, UNINIT_VEC, - call_span, + vec![call_span, maybe_with_capacity_or_reserve.span], "calling `set_len()` immediately after reserving a buffer creates uninitialized values", - Some(maybe_with_capacity_or_reserve.span), - "the buffer is reserved here" + |diag| { + diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); + }, ); } } @@ -155,9 +156,10 @@ fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(& // peel unsafe blocks in `unsafe { vec.set_len() }` let expr = peel_hir_expr_while(expr, |e| { if let ExprKind::Block(block, _) = e.kind { + // Extract the first statement/expression match (block.stmts.get(0).map(|stmt| &stmt.kind), block.expr) { (None, Some(expr)) => Some(expr), - (Some(StmtKind::Expr(expr) | StmtKind::Semi(expr)), None) => Some(expr), + (Some(StmtKind::Expr(expr) | StmtKind::Semi(expr)), _) => Some(expr), _ => None, } } else { diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index 34b9e07ef1d4f..e60b73a1e3089 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -2,6 +2,11 @@ use std::mem::MaybeUninit; +#[derive(Default)] +struct MyVec { + vec: Vec, +} + fn main() { // with_capacity() -> set_len() should be detected let mut vec: Vec = Vec::with_capacity(1000); @@ -24,6 +29,25 @@ fn main() { vec.set_len(200); } + let mut vec: Vec = Vec::with_capacity(1000); + unsafe { + // test the case where there are other statements in the following unsafe block + vec.set_len(200); + assert!(vec.len() == 200); + } + + // handle vec stored in the field of a struct + let mut my_vec = MyVec::default(); + my_vec.vec.reserve(1000); + unsafe { + my_vec.vec.set_len(200); + } + + my_vec.vec = Vec::with_capacity(1000); + unsafe { + my_vec.vec.set_len(200); + } + // MaybeUninit-wrapped types should not be detected unsafe { let mut vec: Vec> = Vec::with_capacity(1000); @@ -35,4 +59,12 @@ fn main() { let mut vec: Vec<(MaybeUninit, [MaybeUninit; 2])> = Vec::with_capacity(1000); vec.set_len(200); } + + // known false negative + let mut vec1: Vec = Vec::with_capacity(1000); + let mut vec2: Vec = Vec::with_capacity(1000); + unsafe { + vec1.reserve(200); + vec2.reserve(200); + } } diff --git a/tests/ui/uninit_vec.stderr b/tests/ui/uninit_vec.stderr index 1d7807fcf7d0e..31f8ae40350ce 100644 --- a/tests/ui/uninit_vec.stderr +++ b/tests/ui/uninit_vec.stderr @@ -1,51 +1,78 @@ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:9:9 + --> $DIR/uninit_vec.rs:12:5 | +LL | let mut vec: Vec = Vec::with_capacity(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ | = note: `-D clippy::uninit-vec` implied by `-D warnings` -note: the buffer is reserved here - --> $DIR/uninit_vec.rs:7:5 - | -LL | let mut vec: Vec = Vec::with_capacity(1000); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:15:9 + --> $DIR/uninit_vec.rs:18:5 | +LL | vec.reserve(1000); + | ^^^^^^^^^^^^^^^^^^ +LL | unsafe { LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ | -note: the buffer is reserved here - --> $DIR/uninit_vec.rs:13:5 - | -LL | vec.reserve(1000); - | ^^^^^^^^^^^^^^^^^^ + = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:21:9 + --> $DIR/uninit_vec.rs:32:5 | +LL | let mut vec: Vec = Vec::with_capacity(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ | -note: the buffer is reserved here - --> $DIR/uninit_vec.rs:20:9 + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:41:5 + | +LL | my_vec.vec.reserve(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | my_vec.vec.set_len(200); + | ^^^^^^^^^^^^^^^^^^^^^^^ | -LL | let mut vec: Vec = Vec::with_capacity(1000); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:46:5 + | +LL | my_vec.vec = Vec::with_capacity(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | my_vec.vec.set_len(200); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:24:9 + --> $DIR/uninit_vec.rs:25:9 | +LL | let mut vec: Vec = Vec::with_capacity(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ | -note: the buffer is reserved here - --> $DIR/uninit_vec.rs:23:9 + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:28:9 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 4 previous errors +error: aborting due to 7 previous errors From fec20bf61799764fab2b0d00d4666f4a9a9632c2 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Tue, 21 Sep 2021 09:56:56 -0400 Subject: [PATCH 016/101] Add #allow attribute to suppress FP #7698 --- clippy_lints/src/uninit_vec.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index b9b575a452b12..4d556d62c9bda 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -71,6 +71,8 @@ fn handle_uninit_vec_pair( // Check T of Vec if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)); then { + // FIXME: false positive #7698 + #[allow(clippy::collapsible_span_lint_calls)] span_lint_and_then( cx, UNINIT_VEC, From dd9c8d32f2d7984545816ee07e1e7213b36013f0 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Sun, 3 Oct 2021 00:23:57 -0400 Subject: [PATCH 017/101] Extract get_vec_init_kind and share it --- clippy_lints/src/uninit_vec.rs | 116 +++++++++++-------------- clippy_lints/src/vec_init_then_push.rs | 51 ++--------- clippy_utils/src/lib.rs | 49 ++++++++++- clippy_utils/src/paths.rs | 1 - 4 files changed, 105 insertions(+), 112 deletions(-) diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 4d556d62c9bda..833c95f494032 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,24 +1,24 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_vec_init_kind; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{ - match_def_path, path_to_local_id, paths, peel_hir_expr_while, ty::is_uninit_value_valid_for_ty, SpanlessEq, -}; -use rustc_hir::def::Res; -use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; +use clippy_utils::{path_to_local_id, peel_hir_expr_while, ty::is_uninit_value_valid_for_ty, SpanlessEq}; +use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; +// TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std declare_clippy_lint! { /// ### What it does - /// Checks for the creation of uninitialized `Vec` by calling `set_len()` - /// immediately after `with_capacity()` or `reserve()`. + /// Checks for `set_len()` call that creates `Vec` with uninitialized elements. + /// This is commonly caused by calling `set_len()` right after after calling + /// `with_capacity()` or `reserve()`. /// /// ### Why is this bad? - /// It creates `Vec` that contains uninitialized data, which leads to an + /// It creates a `Vec` with uninitialized data, which leads to an /// undefined behavior with most safe operations. - /// Notably, using uninitialized `Vec` with generic `Read` is unsound. + /// Notably, uninitialized `Vec` must not be used with generic `Read`. /// /// ### Example /// ```rust,ignore @@ -26,16 +26,25 @@ declare_clippy_lint! { /// unsafe { vec.set_len(1000); } /// reader.read(&mut vec); // undefined behavior! /// ``` - /// Use an initialized buffer: - /// ```rust,ignore - /// let mut vec: Vec = vec![0; 1000]; - /// reader.read(&mut vec); - /// ``` - /// Or, wrap the content in `MaybeUninit`: - /// ```rust,ignore - /// let mut vec: Vec> = Vec::with_capacity(1000); - /// unsafe { vec.set_len(1000); } - /// ``` + /// + /// ### How to fix? + /// 1. Use an initialized buffer: + /// ```rust,ignore + /// let mut vec: Vec = vec![0; 1000]; + /// reader.read(&mut vec); + /// ``` + /// 2. Wrap the content in `MaybeUninit`: + /// ```rust,ignore + /// let mut vec: Vec> = Vec::with_capacity(1000); + /// vec.set_len(1000); // `MaybeUninit` can be uninitialized + /// ``` + /// 3. If you are on nightly, `Vec::spare_capacity_mut()` is available: + /// ```rust,ignore + /// let mut vec: Vec = Vec::with_capacity(1000); + /// let remaining = vec.spare_capacity_mut(); // `&mut [MaybeUninit]` + /// // perform initialization with `remaining` + /// vec.set_len(...); // Safe to call `set_len()` on initialized part + /// ``` pub UNINIT_VEC, correctness, "Vec with uninitialized data" @@ -59,11 +68,11 @@ impl<'tcx> LateLintPass<'tcx> for UninitVec { fn handle_uninit_vec_pair( cx: &LateContext<'tcx>, - maybe_with_capacity_or_reserve: &'tcx Stmt<'tcx>, + maybe_init_or_reserve: &'tcx Stmt<'tcx>, maybe_set_len: &'tcx Expr<'tcx>, ) { if_chain! { - if let Some(vec) = extract_with_capacity_or_reserve_target(cx, maybe_with_capacity_or_reserve); + if let Some(vec) = extract_init_or_reserve_target(cx, maybe_init_or_reserve); if let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len); if vec.eq_expr(cx, set_len_self); if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind(); @@ -71,12 +80,12 @@ fn handle_uninit_vec_pair( // Check T of Vec if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)); then { - // FIXME: false positive #7698 + // FIXME: #7698, false positive of the internal lints #[allow(clippy::collapsible_span_lint_calls)] span_lint_and_then( cx, UNINIT_VEC, - vec![call_span, maybe_with_capacity_or_reserve.span], + vec![call_span, maybe_init_or_reserve.span], "calling `set_len()` immediately after reserving a buffer creates uninitialized values", |diag| { diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); @@ -101,15 +110,15 @@ impl<'tcx> LocalOrExpr<'tcx> { } } -/// Returns the target vec of `Vec::with_capacity()` or `Vec::reserve()` -fn extract_with_capacity_or_reserve_target(cx: &LateContext<'_>, stmt: &'tcx Stmt<'_>) -> Option> { +/// Finds the target location where the result of `Vec` initialization is stored +/// or `self` expression for `Vec::reserve()`. +fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option> { match stmt.kind { StmtKind::Local(local) => { - // let mut x = Vec::with_capacity() if_chain! { if let Some(init_expr) = local.init; if let PatKind::Binding(_, hir_id, _, None) = local.pat.kind; - if is_with_capacity(cx, init_expr); + if get_vec_init_kind(cx, init_expr).is_some(); then { Some(LocalOrExpr::Local(hir_id)) } else { @@ -117,40 +126,20 @@ fn extract_with_capacity_or_reserve_target(cx: &LateContext<'_>, stmt: &'tcx Stm } } }, - StmtKind::Expr(expr) | StmtKind::Semi(expr) => { - match expr.kind { - ExprKind::Assign(lhs, rhs, _span) if is_with_capacity(cx, rhs) => { - // self.vec = Vec::with_capacity() - Some(LocalOrExpr::Expr(lhs)) - }, - ExprKind::MethodCall(path, _, [self_expr, _], _) => { - // self.vec.reserve() - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::vec_type) - && path.ident.name.as_str() == "reserve" - { - Some(LocalOrExpr::Expr(self_expr)) - } else { - None - } - }, - _ => None, - } + StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind { + ExprKind::Assign(lhs, rhs, _span) if get_vec_init_kind(cx, rhs).is_some() => Some(LocalOrExpr::Expr(lhs)), + ExprKind::MethodCall(path, _, [self_expr, _], _) if is_reserve(cx, path, self_expr) => { + Some(LocalOrExpr::Expr(self_expr)) + }, + _ => None, }, StmtKind::Item(_) => None, } } -fn is_with_capacity(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> bool { - if_chain! { - if let ExprKind::Call(path_expr, _) = &expr.kind; - if let ExprKind::Path(qpath) = &path_expr.kind; - if let Res::Def(_, def_id) = cx.qpath_res(qpath, path_expr.hir_id); - then { - match_def_path(cx, def_id, &paths::VEC_WITH_CAPACITY) - } else { - false - } - } +fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_>) -> bool { + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::Vec) + && path.ident.name.as_str() == "reserve" } /// Returns self if the expression is `Vec::set_len()` @@ -169,14 +158,13 @@ fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(& } }); match expr.kind { - ExprKind::MethodCall(_, _, [vec_expr, _], _) => { - cx.typeck_results().type_dependent_def_id(expr.hir_id).and_then(|id| { - if match_def_path(cx, id, &paths::VEC_SET_LEN) { - Some((vec_expr, expr.span)) - } else { - None - } - }) + ExprKind::MethodCall(path, _, [self_expr, _], _) => { + let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs(); + if is_type_diagnostic_item(cx, self_type, sym::Vec) && path.ident.name.as_str() == "set_len" { + Some((self_expr, expr.span)) + } else { + None + } }, _ => None, } diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index d8e241d72af48..478314c08368e 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,16 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_def_path, path_to_local, path_to_local_id, paths}; +use clippy_utils::{get_vec_init_kind, path_to_local, path_to_local_id, VecInitKind}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{symbol::sym, Span}; -use std::convert::TryInto; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -41,11 +38,6 @@ pub struct VecInitThenPush { searcher: Option, } -#[derive(Clone, Copy)] -enum VecInitKind { - New, - WithCapacity(u64), -} struct VecPushSearcher { local_id: HirId, init: VecInitKind, @@ -58,7 +50,8 @@ impl VecPushSearcher { fn display_err(&self, cx: &LateContext<'_>) { match self.init { _ if self.found == 0 => return, - VecInitKind::WithCapacity(x) if x > self.found => return, + VecInitKind::WithLiteralCapacity(x) if x > self.found => return, + VecInitKind::WithExprCapacity(_) => return, _ => (), }; @@ -152,37 +145,3 @@ impl LateLintPass<'_> for VecInitThenPush { } } } - -fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { - if let ExprKind::Call(func, args) = expr.kind { - match func.kind { - ExprKind::Path(QPath::TypeRelative(ty, name)) - if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) => - { - if name.ident.name == sym::new { - return Some(VecInitKind::New); - } else if name.ident.name.as_str() == "with_capacity" { - return args.get(0).and_then(|arg| { - if_chain! { - if let ExprKind::Lit(lit) = &arg.kind; - if let LitKind::Int(num, _) = lit.node; - then { - Some(VecInitKind::WithCapacity(num.try_into().ok()?)) - } else { - None - } - } - }); - } - } - ExprKind::Path(QPath::Resolved(_, path)) - if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) => - { - return Some(VecInitKind::New); - } - _ => (), - } - } - None -} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 09eee78f0d1ff..b450059e18a93 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -93,7 +93,7 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; -use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type}; +use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type, is_type_diagnostic_item}; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { @@ -1789,6 +1789,53 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool } } +#[derive(Clone, Copy)] +pub enum VecInitKind { + /// `Vec::new()` + New, + /// `Vec::default()` or `Default::default()` + Default, + /// `Vec::with_capacity(123)` + WithLiteralCapacity(u64), + /// `Vec::with_capacity(slice.len())` + WithExprCapacity(HirId), +} + +/// Checks if given expression is an initialization of `Vec` and returns its kind. +pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { + if let ExprKind::Call(func, args) = expr.kind { + match func.kind { + ExprKind::Path(QPath::TypeRelative(ty, name)) + if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) => + { + if name.ident.name == sym::new { + return Some(VecInitKind::New); + } else if name.ident.name.as_str() == "with_capacity" { + return args.get(0).and_then(|arg| { + if_chain! { + if let ExprKind::Lit(lit) = &arg.kind; + if let LitKind::Int(num, _) = lit.node; + then { + Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?)) + } else { + Some(VecInitKind::WithExprCapacity(arg.hir_id)) + } + } + }); + } + } + ExprKind::Path(QPath::Resolved(_, path)) + if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) => + { + return Some(VecInitKind::Default); + } + _ => (), + } + } + None +} + /// Gets the node where an expression is either used, or it's type is unified with another branch. pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option> { let mut child_id = expr.hir_id; diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 63ec10a31786d..a39c0fc0b10cd 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -183,7 +183,6 @@ pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"]; pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"]; -pub const VEC_WITH_CAPACITY: [&str; 4] = ["alloc", "vec", "Vec", "with_capacity"]; pub const VEC_SET_LEN: [&str; 4] = ["alloc", "vec", "Vec", "set_len"]; pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"]; From b1aa3064b6d86c5ae90a67e420d88d826bafe8be Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Tue, 5 Oct 2021 11:43:44 -0400 Subject: [PATCH 018/101] Address PR comments --- clippy_lints/src/uninit_vec.rs | 31 ++++++++++----- clippy_lints/src/vec_init_then_push.rs | 3 +- clippy_utils/src/higher.rs | 53 +++++++++++++++++++++++++- clippy_utils/src/lib.rs | 49 +----------------------- clippy_utils/src/paths.rs | 1 - tests/ui/uninit_vec.rs | 11 +++++- 6 files changed, 85 insertions(+), 63 deletions(-) diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 833c95f494032..5d6409874d8f9 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_vec_init_kind; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{path_to_local_id, peel_hir_expr_while, ty::is_uninit_value_valid_for_ty, SpanlessEq}; +use clippy_utils::higher::get_vec_init_kind; +use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; +use clippy_utils::{is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; @@ -16,10 +17,14 @@ declare_clippy_lint! { /// `with_capacity()` or `reserve()`. /// /// ### Why is this bad? - /// It creates a `Vec` with uninitialized data, which leads to an + /// It creates a `Vec` with uninitialized data, which leads to /// undefined behavior with most safe operations. + /// /// Notably, uninitialized `Vec` must not be used with generic `Read`. /// + /// ### Known Problems + /// This lint only checks directly adjacent statements. + /// /// ### Example /// ```rust,ignore /// let mut vec: Vec = Vec::with_capacity(1000); @@ -52,16 +57,20 @@ declare_clippy_lint! { declare_lint_pass!(UninitVec => [UNINIT_VEC]); +// FIXME: update to a visitor-based implementation. +// Threads: https://github.com/rust-lang/rust-clippy/pull/7682#discussion_r710998368 impl<'tcx> LateLintPass<'tcx> for UninitVec { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { - for w in block.stmts.windows(2) { - if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = w[1].kind { - handle_uninit_vec_pair(cx, &w[0], expr); + if !in_external_macro(cx.tcx.sess, block.span) { + for w in block.stmts.windows(2) { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = w[1].kind { + handle_uninit_vec_pair(cx, &w[0], expr); + } } - } - if let (Some(stmt), Some(expr)) = (block.stmts.last(), block.expr) { - handle_uninit_vec_pair(cx, stmt, expr); + if let (Some(stmt), Some(expr)) = (block.stmts.last(), block.expr) { + handle_uninit_vec_pair(cx, stmt, expr); + } } } } @@ -79,6 +88,8 @@ fn handle_uninit_vec_pair( if let ty::Adt(_, substs) = vec_ty.kind(); // Check T of Vec if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)); + // `#[allow(...)]` attribute can be set on enclosing unsafe block of `set_len()` + if !is_lint_allowed(cx, UNINIT_VEC, maybe_set_len.hir_id); then { // FIXME: #7698, false positive of the internal lints #[allow(clippy::collapsible_span_lint_calls)] diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 478314c08368e..b92b6ca4f4380 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::source::snippet; -use clippy_utils::{get_vec_init_kind, path_to_local, path_to_local_id, VecInitKind}; +use clippy_utils::{path_to_local, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, Stmt, StmtKind}; diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index ba4d50bf74469..d60ddbd3c562d 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -2,11 +2,14 @@ #![deny(clippy::missing_docs_in_private_items)] +use crate::ty::is_type_diagnostic_item; use crate::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{self, LitKind}; use rustc_hir as hir; -use rustc_hir::{Arm, Block, BorrowKind, Expr, ExprKind, LoopSource, MatchSource, Node, Pat, StmtKind, UnOp}; +use rustc_hir::{ + Arm, Block, BorrowKind, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StmtKind, UnOp, +}; use rustc_lint::LateContext; use rustc_span::{sym, ExpnKind, Span, Symbol}; @@ -632,3 +635,51 @@ impl PanicExpn<'tcx> { } } } + +/// A parsed `Vec` initialization expression +#[derive(Clone, Copy)] +pub enum VecInitKind { + /// `Vec::new()` + New, + /// `Vec::default()` or `Default::default()` + Default, + /// `Vec::with_capacity(123)` + WithLiteralCapacity(u64), + /// `Vec::with_capacity(slice.len())` + WithExprCapacity(HirId), +} + +/// Checks if given expression is an initialization of `Vec` and returns its kind. +pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { + if let ExprKind::Call(func, args) = expr.kind { + match func.kind { + ExprKind::Path(QPath::TypeRelative(ty, name)) + if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) => + { + if name.ident.name == sym::new { + return Some(VecInitKind::New); + } else if name.ident.name.as_str() == "with_capacity" { + return args.get(0).and_then(|arg| { + if_chain! { + if let ExprKind::Lit(lit) = &arg.kind; + if let LitKind::Int(num, _) = lit.node; + then { + Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?)) + } else { + Some(VecInitKind::WithExprCapacity(arg.hir_id)) + } + } + }); + } + } + ExprKind::Path(QPath::Resolved(_, path)) + if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) => + { + return Some(VecInitKind::Default); + } + _ => (), + } + } + None +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b450059e18a93..09eee78f0d1ff 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -93,7 +93,7 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; -use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type, is_type_diagnostic_item}; +use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type}; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { @@ -1789,53 +1789,6 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool } } -#[derive(Clone, Copy)] -pub enum VecInitKind { - /// `Vec::new()` - New, - /// `Vec::default()` or `Default::default()` - Default, - /// `Vec::with_capacity(123)` - WithLiteralCapacity(u64), - /// `Vec::with_capacity(slice.len())` - WithExprCapacity(HirId), -} - -/// Checks if given expression is an initialization of `Vec` and returns its kind. -pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { - if let ExprKind::Call(func, args) = expr.kind { - match func.kind { - ExprKind::Path(QPath::TypeRelative(ty, name)) - if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) => - { - if name.ident.name == sym::new { - return Some(VecInitKind::New); - } else if name.ident.name.as_str() == "with_capacity" { - return args.get(0).and_then(|arg| { - if_chain! { - if let ExprKind::Lit(lit) = &arg.kind; - if let LitKind::Int(num, _) = lit.node; - then { - Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?)) - } else { - Some(VecInitKind::WithExprCapacity(arg.hir_id)) - } - } - }); - } - } - ExprKind::Path(QPath::Resolved(_, path)) - if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) => - { - return Some(VecInitKind::Default); - } - _ => (), - } - } - None -} - /// Gets the node where an expression is either used, or it's type is unified with another branch. pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option> { let mut child_id = expr.hir_id; diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index a39c0fc0b10cd..e43c575602145 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -183,6 +183,5 @@ pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"]; pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"]; -pub const VEC_SET_LEN: [&str; 4] = ["alloc", "vec", "Vec", "set_len"]; pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"]; diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index e60b73a1e3089..c8f9067e549f0 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -48,6 +48,13 @@ fn main() { my_vec.vec.set_len(200); } + // Test `#[allow(...)]` attributes on inner unsafe block (shouldn't trigger) + let mut vec: Vec = Vec::with_capacity(1000); + #[allow(clippy::uninit_vec)] + unsafe { + vec.set_len(200); + } + // MaybeUninit-wrapped types should not be detected unsafe { let mut vec: Vec> = Vec::with_capacity(1000); @@ -64,7 +71,7 @@ fn main() { let mut vec1: Vec = Vec::with_capacity(1000); let mut vec2: Vec = Vec::with_capacity(1000); unsafe { - vec1.reserve(200); - vec2.reserve(200); + vec1.set_len(200); + vec2.set_len(200); } } From de0d2b15008f0333e432aab9a7b7045ab0157897 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Thu, 7 Oct 2021 11:18:01 -0400 Subject: [PATCH 019/101] Add testcases --- clippy_utils/src/higher.rs | 20 ++++++++--------- tests/ui/uninit_vec.rs | 17 ++++++++++++++ tests/ui/uninit_vec.stderr | 45 +++++++++++++++++++++++++++++++++----- 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index d60ddbd3c562d..30f6831dcf402 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -658,18 +658,18 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - { if name.ident.name == sym::new { return Some(VecInitKind::New); + } else if name.ident.name.as_str() == "default" { + return Some(VecInitKind::Default); } else if name.ident.name.as_str() == "with_capacity" { - return args.get(0).and_then(|arg| { - if_chain! { - if let ExprKind::Lit(lit) = &arg.kind; - if let LitKind::Int(num, _) = lit.node; - then { - Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?)) - } else { - Some(VecInitKind::WithExprCapacity(arg.hir_id)) - } + let arg = args.get(0)?; + if_chain! { + if let ExprKind::Lit(lit) = &arg.kind; + if let LitKind::Int(num, _) = lit.node; + then { + return Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?)) } - }); + } + return Some(VecInitKind::WithExprCapacity(arg.hir_id)); } } ExprKind::Path(QPath::Resolved(_, path)) diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index c8f9067e549f0..dc150cf28f2cc 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -20,6 +20,23 @@ fn main() { vec.set_len(200); } + // new() -> set_len() should be detected + let mut vec: Vec = Vec::new(); + unsafe { + vec.set_len(200); + } + + // default() -> set_len() should be detected + let mut vec: Vec = Default::default(); + unsafe { + vec.set_len(200); + } + + let mut vec: Vec = Vec::default(); + unsafe { + vec.set_len(200); + } + // test when both calls are enclosed in the same unsafe block unsafe { let mut vec: Vec = Vec::with_capacity(1000); diff --git a/tests/ui/uninit_vec.stderr b/tests/ui/uninit_vec.stderr index 31f8ae40350ce..81b91cef92abf 100644 --- a/tests/ui/uninit_vec.stderr +++ b/tests/ui/uninit_vec.stderr @@ -22,7 +22,40 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:32:5 + --> $DIR/uninit_vec.rs:24:5 + | +LL | let mut vec: Vec = Vec::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:30:5 + | +LL | let mut vec: Vec = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:35:5 + | +LL | let mut vec: Vec = Vec::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:49:5 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +66,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:41:5 + --> $DIR/uninit_vec.rs:58:5 | LL | my_vec.vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +77,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:46:5 + --> $DIR/uninit_vec.rs:63:5 | LL | my_vec.vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -55,7 +88,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:25:9 + --> $DIR/uninit_vec.rs:42:9 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,7 +98,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> $DIR/uninit_vec.rs:28:9 + --> $DIR/uninit_vec.rs:45:9 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -74,5 +107,5 @@ LL | vec.set_len(200); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 7 previous errors +error: aborting due to 10 previous errors From 2181387c3ab4d7dc4d6e2c46c8158201a1b1045e Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Thu, 7 Oct 2021 11:53:08 -0400 Subject: [PATCH 020/101] Improved error message for set_len() on empty Vec --- clippy_lints/src/uninit_vec.rs | 101 +++++++++++++++++++++++---------- tests/ui/uninit_vec.stderr | 12 +--- 2 files changed, 73 insertions(+), 40 deletions(-) diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 5d6409874d8f9..15c79da6fa990 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::higher::get_vec_init_kind; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; use clippy_utils::{is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; @@ -83,69 +83,108 @@ fn handle_uninit_vec_pair( if_chain! { if let Some(vec) = extract_init_or_reserve_target(cx, maybe_init_or_reserve); if let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len); - if vec.eq_expr(cx, set_len_self); + if vec.location.eq_expr(cx, set_len_self); if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind(); if let ty::Adt(_, substs) = vec_ty.kind(); - // Check T of Vec - if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)); // `#[allow(...)]` attribute can be set on enclosing unsafe block of `set_len()` if !is_lint_allowed(cx, UNINIT_VEC, maybe_set_len.hir_id); then { - // FIXME: #7698, false positive of the internal lints - #[allow(clippy::collapsible_span_lint_calls)] - span_lint_and_then( - cx, - UNINIT_VEC, - vec![call_span, maybe_init_or_reserve.span], - "calling `set_len()` immediately after reserving a buffer creates uninitialized values", - |diag| { - diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); - }, - ); + if vec.has_capacity() { + // with_capacity / reserve -> set_len + + // Check T of Vec + if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)) { + // FIXME: #7698, false positive of the internal lints + #[allow(clippy::collapsible_span_lint_calls)] + span_lint_and_then( + cx, + UNINIT_VEC, + vec![call_span, maybe_init_or_reserve.span], + "calling `set_len()` immediately after reserving a buffer creates uninitialized values", + |diag| { + diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); + }, + ); + } + } else { + // new / default -> set_len + span_lint( + cx, + UNINIT_VEC, + vec![call_span, maybe_init_or_reserve.span], + "calling `set_len()` on empty `Vec` creates out-of-bound values", + ) + } } } } -#[derive(Clone, Copy, Debug)] -enum LocalOrExpr<'tcx> { +/// The target `Vec` that is initialized or reserved +#[derive(Clone, Copy)] +struct TargetVec<'tcx> { + location: VecLocation<'tcx>, + /// `None` if `reserve()` + init_kind: Option, +} + +impl TargetVec<'_> { + pub fn has_capacity(self) -> bool { + !matches!(self.init_kind, Some(VecInitKind::New | VecInitKind::Default)) + } +} + +#[derive(Clone, Copy)] +enum VecLocation<'tcx> { Local(HirId), Expr(&'tcx Expr<'tcx>), } -impl<'tcx> LocalOrExpr<'tcx> { - fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { +impl<'tcx> VecLocation<'tcx> { + pub fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { match self { - LocalOrExpr::Local(hir_id) => path_to_local_id(expr, hir_id), - LocalOrExpr::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr), + VecLocation::Local(hir_id) => path_to_local_id(expr, hir_id), + VecLocation::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr), } } } /// Finds the target location where the result of `Vec` initialization is stored /// or `self` expression for `Vec::reserve()`. -fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option> { +fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option> { match stmt.kind { StmtKind::Local(local) => { if_chain! { if let Some(init_expr) = local.init; if let PatKind::Binding(_, hir_id, _, None) = local.pat.kind; - if get_vec_init_kind(cx, init_expr).is_some(); + if let Some(init_kind) = get_vec_init_kind(cx, init_expr); then { - Some(LocalOrExpr::Local(hir_id)) - } else { - None + return Some(TargetVec { + location: VecLocation::Local(hir_id), + init_kind: Some(init_kind), + }) } } }, StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind { - ExprKind::Assign(lhs, rhs, _span) if get_vec_init_kind(cx, rhs).is_some() => Some(LocalOrExpr::Expr(lhs)), + ExprKind::Assign(lhs, rhs, _span) => { + if let Some(init_kind) = get_vec_init_kind(cx, rhs) { + return Some(TargetVec { + location: VecLocation::Expr(lhs), + init_kind: Some(init_kind), + }); + } + }, ExprKind::MethodCall(path, _, [self_expr, _], _) if is_reserve(cx, path, self_expr) => { - Some(LocalOrExpr::Expr(self_expr)) + return Some(TargetVec { + location: VecLocation::Expr(self_expr), + init_kind: None, + }); }, - _ => None, + _ => (), }, - StmtKind::Item(_) => None, + StmtKind::Item(_) => (), } + None } fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_>) -> bool { diff --git a/tests/ui/uninit_vec.stderr b/tests/ui/uninit_vec.stderr index 81b91cef92abf..520bfb26b62e1 100644 --- a/tests/ui/uninit_vec.stderr +++ b/tests/ui/uninit_vec.stderr @@ -21,7 +21,7 @@ LL | vec.set_len(200); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: calling `set_len()` immediately after reserving a buffer creates uninitialized values +error: calling `set_len()` on empty `Vec` creates out-of-bound values --> $DIR/uninit_vec.rs:24:5 | LL | let mut vec: Vec = Vec::new(); @@ -29,10 +29,8 @@ LL | let mut vec: Vec = Vec::new(); LL | unsafe { LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ - | - = help: initialize the buffer or wrap the content in `MaybeUninit` -error: calling `set_len()` immediately after reserving a buffer creates uninitialized values +error: calling `set_len()` on empty `Vec` creates out-of-bound values --> $DIR/uninit_vec.rs:30:5 | LL | let mut vec: Vec = Default::default(); @@ -40,10 +38,8 @@ LL | let mut vec: Vec = Default::default(); LL | unsafe { LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ - | - = help: initialize the buffer or wrap the content in `MaybeUninit` -error: calling `set_len()` immediately after reserving a buffer creates uninitialized values +error: calling `set_len()` on empty `Vec` creates out-of-bound values --> $DIR/uninit_vec.rs:35:5 | LL | let mut vec: Vec = Vec::default(); @@ -51,8 +47,6 @@ LL | let mut vec: Vec = Vec::default(); LL | unsafe { LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ - | - = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values --> $DIR/uninit_vec.rs:49:5 From 03fed75c89880e5ed919eec1ba93dbe769d463a2 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Sat, 9 Oct 2021 05:59:53 -0400 Subject: [PATCH 021/101] Address internal lints --- clippy_lints/src/uninit_vec.rs | 2 +- clippy_utils/src/higher.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 15c79da6fa990..2573209d1b628 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -113,7 +113,7 @@ fn handle_uninit_vec_pair( UNINIT_VEC, vec![call_span, maybe_init_or_reserve.span], "calling `set_len()` on empty `Vec` creates out-of-bound values", - ) + ); } } } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 30f6831dcf402..1ff282de95097 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -11,7 +11,7 @@ use rustc_hir::{ Arm, Block, BorrowKind, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StmtKind, UnOp, }; use rustc_lint::LateContext; -use rustc_span::{sym, ExpnKind, Span, Symbol}; +use rustc_span::{sym, symbol, ExpnKind, Span, Symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`. @@ -658,7 +658,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - { if name.ident.name == sym::new { return Some(VecInitKind::New); - } else if name.ident.name.as_str() == "default" { + } else if name.ident.name == symbol::kw::Default { return Some(VecInitKind::Default); } else if name.ident.name.as_str() == "with_capacity" { let arg = args.get(0)?; From 7aaed02b8c4cad724506a44a53b5205132ff4d20 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 9 Oct 2021 14:56:33 +0200 Subject: [PATCH 022/101] Fix false positive when `Drop` and `Copy` involved --- clippy_lints/src/default.rs | 8 +++ tests/ui/field_reassign_with_default.rs | 64 +++++++++++++++++++++ tests/ui/field_reassign_with_default.stderr | 26 ++++++++- 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index db8f2171348f7..cde27d3ad2a0c 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::ty::{has_drop, is_copy}; use clippy_utils::{any_parent_is_automatically_derived, contains_name, in_macro, match_def_path, paths}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; @@ -139,6 +140,13 @@ impl LateLintPass<'_> for Default { .fields .iter() .all(|field| field.vis.is_accessible_from(module_did, cx.tcx)); + let all_fields_are_copy = variant + .fields + .iter() + .all(|field| { + is_copy(cx, cx.tcx.type_of(field.did)) + }); + if !has_drop(cx, binding_type) || all_fields_are_copy; then { (local, variant, ident.name, binding_type, expr.span) } else { diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 787053fb00064..7367910eaa126 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -183,3 +183,67 @@ struct WrapperMulti { i: T, j: U, } + +mod issue6312 { + use std::sync::atomic::AtomicBool; + use std::sync::Arc; + + // do not lint: type implements `Drop` but not all fields are `Copy` + #[derive(Clone, Default)] + pub struct ImplDropNotAllCopy { + name: String, + delay_data_sync: Arc, + } + + impl Drop for ImplDropNotAllCopy { + fn drop(&mut self) { + self.close() + } + } + + impl ImplDropNotAllCopy { + fn new(name: &str) -> Self { + let mut f = ImplDropNotAllCopy::default(); + f.name = name.to_owned(); + f + } + fn close(&self) {} + } + + // lint: type implements `Drop` and all fields are `Copy` + #[derive(Clone, Default)] + pub struct ImplDropAllCopy { + name: usize, + delay_data_sync: bool, + } + + impl Drop for ImplDropAllCopy { + fn drop(&mut self) { + self.close() + } + } + + impl ImplDropAllCopy { + fn new(name: &str) -> Self { + let mut f = ImplDropAllCopy::default(); + f.name = name.len(); + f + } + fn close(&self) {} + } + + // lint: type does not implement `Drop` though all fields are `Copy` + #[derive(Clone, Default)] + pub struct NoDropAllCopy { + name: usize, + delay_data_sync: bool, + } + + impl NoDropAllCopy { + fn new(name: &str) -> Self { + let mut f = NoDropAllCopy::default(); + f.name = name.len(); + f + } + } +} diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index b56db08ec8a78..3ce4b91a54869 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -107,5 +107,29 @@ note: consider initializing the variable with `WrapperMulti:: { i: 42, LL | let mut a: WrapperMulti = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:229:13 + | +LL | f.name = name.len(); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `issue6312::ImplDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:228:13 + | +LL | let mut f = ImplDropAllCopy::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:245:13 + | +LL | f.name = name.len(); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `issue6312::NoDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:244:13 + | +LL | let mut f = NoDropAllCopy::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors From 1e18b8a269f58b15da39a1ffa9f4c29b2a55bae8 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 9 Oct 2021 15:54:16 +0200 Subject: [PATCH 023/101] Fix FP in external macros for `mut_mut` lint --- clippy_lints/src/mut_mut.rs | 4 ++++ tests/ui/auxiliary/macro_rules.rs | 7 +++++++ tests/ui/mut_mut.rs | 10 ++++++++++ tests/ui/mut_mut.stderr | 18 +++++++++--------- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 610152a217f1e..7c4cac29ba8e8 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -82,6 +82,10 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { } fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { + if in_external_macro(self.cx.sess(), ty.span) { + return; + } + if let hir::TyKind::Rptr( _, hir::MutTy { diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index 170955e726cc5..0251fada9e85a 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -113,3 +113,10 @@ macro_rules! default_numeric_fallback { let x = 22; }; } + +#[macro_export] +macro_rules! mut_mut { + () => { + let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32; + }; +} diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index 8965cef66dedd..be854d9418332 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -1,6 +1,11 @@ +// aux-build:macro_rules.rs + #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::mut_mut)] +#[macro_use] +extern crate macro_rules; + fn fun(x: &mut &mut u32) -> bool { **x > 0 } @@ -47,3 +52,8 @@ fn issue939() { println!(":{}", arg); } } + +fn issue6922() { + // do not lint from an external macro + mut_mut!(); +} diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 0fed6953cb85c..6820a85aa5433 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -1,5 +1,5 @@ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:4:11 + --> $DIR/mut_mut.rs:9:11 | LL | fn fun(x: &mut &mut u32) -> bool { | ^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | fn fun(x: &mut &mut u32) -> bool { = note: `-D clippy::mut-mut` implied by `-D warnings` error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:20:17 + --> $DIR/mut_mut.rs:25:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:14:9 + --> $DIR/mut_mut.rs:19:9 | LL | &mut $p | ^^^^^^^ @@ -24,37 +24,37 @@ LL | let mut z = mut_ptr!(&mut 3u32); = note: this error originates in the macro `mut_ptr` (in Nightly builds, run with -Z macro-backtrace for more info) error: this expression mutably borrows a mutable reference. Consider reborrowing - --> $DIR/mut_mut.rs:22:21 + --> $DIR/mut_mut.rs:27:21 | LL | let mut y = &mut x; | ^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:26:32 + --> $DIR/mut_mut.rs:31:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:26:16 + --> $DIR/mut_mut.rs:31:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:31:37 + --> $DIR/mut_mut.rs:36:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:31:16 + --> $DIR/mut_mut.rs:36:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:31:21 + --> $DIR/mut_mut.rs:36:21 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^ From fc324255215066afd4f4dfff6f39e76e2c80fc4b Mon Sep 17 00:00:00 2001 From: 1nF0rmed Date: Sat, 9 Oct 2021 22:30:12 +0530 Subject: [PATCH 024/101] Refactor to check for multiple reference patterns --- clippy_lints/src/matches.rs | 10 ++-- tests/ui/match_expr_like_matches_macro.stderr | 35 +---------- tests/ui/match_ref_pats.stderr | 58 +------------------ 3 files changed, 7 insertions(+), 96 deletions(-) diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 56d4163a6b343..b643fba5d3288 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1187,7 +1187,7 @@ where 'b: 'a, I: Clone + Iterator>, { - if !has_only_ref_pats(pats.clone()) { + if !has_multiple_ref_pats(pats.clone()) { return; } @@ -1693,12 +1693,12 @@ fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option(pats: I) -> bool +fn has_multiple_ref_pats<'a, 'b, I>(pats: I) -> bool where 'b: 'a, I: Iterator>, { - let mut at_least_one_is_true = false; + let mut ref_count = 0; for opt in pats.map(|pat| match pat.kind { PatKind::Ref(..) => Some(true), // &-patterns PatKind::Wild => Some(false), // an "anything" wildcard is also fine @@ -1706,13 +1706,13 @@ where }) { if let Some(inner) = opt { if inner { - at_least_one_is_true = true; + ref_count += 1; } } else { return false; } } - at_least_one_is_true + ref_count > 1 } pub fn overlapping(ranges: &[SpannedRange]) -> Option<(&SpannedRange, &SpannedRange)> diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index 366ef36c367bf..d7cedf9f9f159 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -110,23 +110,6 @@ LL | | _ => false, LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` -error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_expr_like_matches_macro.rs:166:20 - | -LL | let _res = match &val { - | ____________________^ -LL | | &Some(ref _a) => true, -LL | | _ => false, -LL | | }; - | |_________^ - | - = note: `-D clippy::match-ref-pats` implied by `-D warnings` -help: try - | -LL ~ let _res = match val { -LL ~ Some(ref _a) => true, - | - error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:178:20 | @@ -137,21 +120,5 @@ LL | | _ => false, LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` -error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_expr_like_matches_macro.rs:178:20 - | -LL | let _res = match &val { - | ____________________^ -LL | | &Some(ref _a) => true, -LL | | _ => false, -LL | | }; - | |_________^ - | -help: try - | -LL ~ let _res = match val { -LL ~ Some(ref _a) => true, - | - -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index 072aff445e97f..e4e13c32d74f2 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -15,21 +15,6 @@ LL ~ Some(v) => println!("{:?}", v), LL ~ None => println!("none"), | -error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:18:5 - | -LL | / match tup { -LL | | &(v, 1) => println!("{}", v), -LL | | _ => println!("none"), -LL | | } - | |_____^ - | -help: instead of prefixing all patterns with `&`, you can dereference the expression - | -LL ~ match *tup { -LL ~ (v, 1) => println!("{}", v), - | - error: you don't need to add `&` to both the expression and the patterns --> $DIR/match_ref_pats.rs:24:5 | @@ -54,52 +39,11 @@ LL | if let &None = a { | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` -error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:36:5 - | -LL | / if let &None = a { -LL | | println!("none"); -LL | | } - | |_____^ - | -help: instead of prefixing all patterns with `&`, you can dereference the expression - | -LL | if let None = *a { - | ~~~~ ~~ - error: redundant pattern matching, consider using `is_none()` --> $DIR/match_ref_pats.rs:41:12 | LL | if let &None = &b { | -------^^^^^----- help: try this: `if b.is_none()` -error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_ref_pats.rs:41:5 - | -LL | / if let &None = &b { -LL | | println!("none"); -LL | | } - | |_____^ - | -help: try - | -LL | if let None = b { - | ~~~~ ~ - -error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:68:9 - | -LL | / match foo_variant!(0) { -LL | | &Foo::A => println!("A"), -LL | | _ => println!("Wild"), -LL | | } - | |_________^ - | -help: instead of prefixing all patterns with `&`, you can dereference the expression - | -LL ~ match *foo_variant!(0) { -LL ~ Foo::A => println!("A"), - | - -error: aborting due to 8 previous errors +error: aborting due to 4 previous errors From 1080f6c70c132ef20bf0310c1845c92f55a3d5b5 Mon Sep 17 00:00:00 2001 From: 1nF0rmed Date: Sun, 10 Oct 2021 14:46:28 +0530 Subject: [PATCH 025/101] Adds additional tests for lint --- tests/ui/match_ref_pats.rs | 42 ++++++++++++++++++++++++++++++++++ tests/ui/match_ref_pats.stderr | 21 ++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/tests/ui/match_ref_pats.rs b/tests/ui/match_ref_pats.rs index 6cbb4d32b0d71..50246486bb6fc 100644 --- a/tests/ui/match_ref_pats.rs +++ b/tests/ui/match_ref_pats.rs @@ -72,4 +72,46 @@ mod ice_3719 { } } +mod issue_7740 { + macro_rules! foobar_variant( + ($idx:expr) => (FooBar::get($idx).unwrap()) + ); + + enum FooBar { + Foo, + Bar, + FooBar, + BarFoo, + } + + impl FooBar { + fn get(idx: u8) -> Option<&'static Self> { + match idx { + 0 => Some(&FooBar::Foo), + 1 => Some(&FooBar::Bar), + 2 => Some(&FooBar::FooBar), + 3 => Some(&FooBar::BarFoo), + _ => None, + } + } + } + + fn issue_7740() { + // Issue #7740 + match foobar_variant!(0) { + &FooBar::Foo => println!("Foo"), + &FooBar::Bar => println!("Bar"), + &FooBar::FooBar => println!("FooBar"), + _ => println!("Wild"), + } + + // This shouldn't trigger + if let &FooBar::BarFoo = foobar_variant!(3) { + println!("BarFoo"); + } else { + println!("Wild"); + } + } +} + fn main() {} diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index e4e13c32d74f2..901820077e20e 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -45,5 +45,24 @@ error: redundant pattern matching, consider using `is_none()` LL | if let &None = &b { | -------^^^^^----- help: try this: `if b.is_none()` -error: aborting due to 4 previous errors +error: you don't need to add `&` to all patterns + --> $DIR/match_ref_pats.rs:101:9 + | +LL | / match foobar_variant!(0) { +LL | | &FooBar::Foo => println!("Foo"), +LL | | &FooBar::Bar => println!("Bar"), +LL | | &FooBar::FooBar => println!("FooBar"), +LL | | _ => println!("Wild"), +LL | | } + | |_________^ + | +help: instead of prefixing all patterns with `&`, you can dereference the expression + | +LL ~ match *foobar_variant!(0) { +LL ~ FooBar::Foo => println!("Foo"), +LL ~ FooBar::Bar => println!("Bar"), +LL ~ FooBar::FooBar => println!("FooBar"), + | + +error: aborting due to 5 previous errors From 857a4073b8b76bd29ff9504b5f015e5021b5d9b2 Mon Sep 17 00:00:00 2001 From: James Hinshelwood Date: Mon, 11 Oct 2021 08:25:30 +0100 Subject: [PATCH 026/101] Add reason config example for disallowed_type Co-authored-by: James Hinshelwood --- clippy_lints/src/disallowed_type.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/disallowed_type.rs b/clippy_lints/src/disallowed_type.rs index 261e9af11c821..8113c388018c1 100644 --- a/clippy_lints/src/disallowed_type.rs +++ b/clippy_lints/src/disallowed_type.rs @@ -21,7 +21,15 @@ declare_clippy_lint! { /// An example clippy.toml configuration: /// ```toml /// # clippy.toml - /// disallowed-types = ["std::collections::BTreeMap"] + /// disallowed-types = [ + /// # Can use a string as the path of the disallowed type. + /// "std::collections::BTreeMap", + /// # Can also use an inline table with a `path` key. + /// { path = "std::net::TcpListener" }, + /// # When using an inline table, can add a `reason` for why the type + /// # is disallowed. + /// { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" }, + /// ] /// ``` /// /// ```rust,ignore From 886cbb18821bdd9a5548dedbe6caa0ccb2dafb3f Mon Sep 17 00:00:00 2001 From: James Hinshelwood Date: Mon, 11 Oct 2021 08:28:32 +0100 Subject: [PATCH 027/101] Rename `disallowed` to `conf_disallowed` Co-authored-by: James Hinshelwood --- clippy_lints/src/disallowed_type.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/disallowed_type.rs b/clippy_lints/src/disallowed_type.rs index 8113c388018c1..48f781516f422 100644 --- a/clippy_lints/src/disallowed_type.rs +++ b/clippy_lints/src/disallowed_type.rs @@ -48,15 +48,15 @@ declare_clippy_lint! { } #[derive(Clone, Debug)] pub struct DisallowedType { - disallowed: Vec, + conf_disallowed: Vec, def_ids: FxHashMap>, prim_tys: FxHashMap>, } impl DisallowedType { - pub fn new(disallowed: Vec) -> Self { + pub fn new(conf_disallowed: Vec) -> Self { Self { - disallowed, + conf_disallowed, def_ids: FxHashMap::default(), prim_tys: FxHashMap::default(), } @@ -83,7 +83,7 @@ impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]); impl<'tcx> LateLintPass<'tcx> for DisallowedType { fn check_crate(&mut self, cx: &LateContext<'_>) { - for conf in &self.disallowed { + for conf in &self.conf_disallowed { let (path, reason) = match conf { conf::DisallowedType::Simple(path) => (path, None), conf::DisallowedType::WithReason { path, reason } => ( From f44a904a56b92415c189be16e8d628e6c1473935 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 11 Oct 2021 10:10:16 +0200 Subject: [PATCH 028/101] Deprecate mem_discriminant_non_enum This lint has been uplifted and is now included in enum_intrinsics_non_enums. --- CHANGELOG.md | 5 +- clippy_lints/src/lib.register_all.rs | 1 - clippy_lints/src/lib.register_correctness.rs | 1 - clippy_lints/src/lib.register_lints.rs | 1 - clippy_lints/src/lib.rs | 3 +- clippy_lints/src/mem_discriminant.rs | 82 ----------------- tests/ui/mem_discriminant.fixed | 45 ---------- tests/ui/mem_discriminant.rs | 45 ---------- tests/ui/mem_discriminant.stderr | 94 -------------------- tests/ui/mem_discriminant_unfixable.rs | 16 ---- tests/ui/mem_discriminant_unfixable.stderr | 20 ----- tests/ui/rename.fixed | 1 + tests/ui/rename.rs | 1 + tests/ui/rename.stderr | 14 ++- 14 files changed, 15 insertions(+), 314 deletions(-) delete mode 100644 clippy_lints/src/mem_discriminant.rs delete mode 100644 tests/ui/mem_discriminant.fixed delete mode 100644 tests/ui/mem_discriminant.rs delete mode 100644 tests/ui/mem_discriminant.stderr delete mode 100644 tests/ui/mem_discriminant_unfixable.rs delete mode 100644 tests/ui/mem_discriminant_unfixable.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fdb300c97741..e700e5f0d736e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1873,10 +1873,10 @@ Released 2019-01-17 [2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be) -* New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`], +* New lints: [`slow_vector_initialization`], `mem_discriminant_non_enum`, [`redundant_clone`], [`wildcard_dependencies`], [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`], - [`mem_discriminant_non_enum`], [`cargo_common_metadata`] + [`cargo_common_metadata`] * Add support for `u128` and `i128` to integer related lints * Add float support to `mistyped_literal_suffixes` * Fix false positives in `use_self` @@ -2839,7 +2839,6 @@ Released 2018-09-13 [`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm [`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants [`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter -[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum [`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget [`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none [`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 3e6e0244754fb..6a3ee35b41a4b 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -127,7 +127,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(matches::REDUNDANT_PATTERN_MATCHING), LintId::of(matches::SINGLE_MATCH), LintId::of(matches::WILDCARD_IN_OR_PATTERNS), - LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index e0ef7b3b8af9f..bbe47a0e772f7 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -36,7 +36,6 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::NEVER_LOOP), LintId::of(loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::ITERATOR_STEP_BY_ZERO), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 2ba2b3da55cd1..b0be3b653664c 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -241,7 +241,6 @@ store.register_lints(&[ matches::SINGLE_MATCH_ELSE, matches::WILDCARD_ENUM_MATCH_ARM, matches::WILDCARD_IN_OR_PATTERNS, - mem_discriminant::MEM_DISCRIMINANT_NON_ENUM, mem_forget::MEM_FORGET, mem_replace::MEM_REPLACE_OPTION_WITH_NONE, mem_replace::MEM_REPLACE_WITH_DEFAULT, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9fc6a9e0ccca0..5534f9c94f367 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -266,7 +266,6 @@ mod map_unit_fn; mod match_on_vec_items; mod match_result_ok; mod matches; -mod mem_discriminant; mod mem_forget; mod mem_replace; mod methods; @@ -600,7 +599,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::>(); store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone()))); store.register_late_pass(|| Box::new(neg_multiply::NegMultiply)); - store.register_late_pass(|| Box::new(mem_discriminant::MemDiscriminant)); store.register_late_pass(|| Box::new(mem_forget::MemForget)); store.register_late_pass(|| Box::new(arithmetic::Arithmetic::default())); store.register_late_pass(|| Box::new(assign_ops::AssignOps)); @@ -850,6 +848,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { 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"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/mem_discriminant.rs b/clippy_lints/src/mem_discriminant.rs deleted file mode 100644 index 59176c4b84663..0000000000000 --- a/clippy_lints/src/mem_discriminant.rs +++ /dev/null @@ -1,82 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet; -use clippy_utils::ty::walk_ptrs_ty_depth; -use clippy_utils::{match_def_path, paths}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for calls of `mem::discriminant()` on a non-enum type. - /// - /// ### Why is this bad? - /// The value of `mem::discriminant()` on non-enum types - /// is unspecified. - /// - /// ### Example - /// ```rust - /// use std::mem; - /// - /// mem::discriminant(&"hello"); - /// mem::discriminant(&&Some(2)); - /// ``` - pub MEM_DISCRIMINANT_NON_ENUM, - correctness, - "calling `mem::descriminant` on non-enum type" -} - -declare_lint_pass!(MemDiscriminant => [MEM_DISCRIMINANT_NON_ENUM]); - -impl<'tcx> LateLintPass<'tcx> for MemDiscriminant { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(func, func_args) = expr.kind; - // is `mem::discriminant` - if let ExprKind::Path(ref func_qpath) = func.kind; - if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::MEM_DISCRIMINANT); - // type is non-enum - let ty_param = cx.typeck_results().node_substs(func.hir_id).type_at(0); - if !ty_param.is_enum(); - - then { - span_lint_and_then( - cx, - MEM_DISCRIMINANT_NON_ENUM, - expr.span, - &format!("calling `mem::discriminant` on non-enum type `{}`", ty_param), - |diag| { - // if this is a reference to an enum, suggest dereferencing - let (base_ty, ptr_depth) = walk_ptrs_ty_depth(ty_param); - if ptr_depth >= 1 && base_ty.is_enum() { - let param = &func_args[0]; - - // cancel out '&'s first - let mut derefs_needed = ptr_depth; - let mut cur_expr = param; - while derefs_needed > 0 { - if let ExprKind::AddrOf(BorrowKind::Ref, _, inner_expr) = cur_expr.kind { - derefs_needed -= 1; - cur_expr = inner_expr; - } else { - break; - } - } - - let derefs = "*".repeat(derefs_needed); - diag.span_suggestion( - param.span, - "try dereferencing", - format!("{}{}", derefs, snippet(cx, cur_expr.span, "")), - Applicability::MachineApplicable, - ); - } - }, - ) - } - } - } -} diff --git a/tests/ui/mem_discriminant.fixed b/tests/ui/mem_discriminant.fixed deleted file mode 100644 index 69a8f286d050d..0000000000000 --- a/tests/ui/mem_discriminant.fixed +++ /dev/null @@ -1,45 +0,0 @@ -// run-rustfix - -#![deny(clippy::mem_discriminant_non_enum)] - -use std::mem; - -enum Foo { - One(usize), - Two(u8), -} - -fn main() { - // bad - mem::discriminant(&Some(2)); - mem::discriminant(&None::); - mem::discriminant(&Foo::One(5)); - mem::discriminant(&Foo::Two(5)); - - let ro = &Some(3); - let rro = &ro; - mem::discriminant(ro); - mem::discriminant(*rro); - mem::discriminant(*rro); - - macro_rules! mem_discriminant_but_in_a_macro { - ($param:expr) => { - mem::discriminant($param) - }; - } - - mem_discriminant_but_in_a_macro!(*rro); - - let rrrrro = &&&rro; - mem::discriminant(****rrrrro); - mem::discriminant(****rrrrro); - - // ok - mem::discriminant(&Some(2)); - mem::discriminant(&None::); - mem::discriminant(&Foo::One(5)); - mem::discriminant(&Foo::Two(5)); - mem::discriminant(ro); - mem::discriminant(*rro); - mem::discriminant(****rrrrro); -} diff --git a/tests/ui/mem_discriminant.rs b/tests/ui/mem_discriminant.rs deleted file mode 100644 index 55db50fcdc733..0000000000000 --- a/tests/ui/mem_discriminant.rs +++ /dev/null @@ -1,45 +0,0 @@ -// run-rustfix - -#![deny(clippy::mem_discriminant_non_enum)] - -use std::mem; - -enum Foo { - One(usize), - Two(u8), -} - -fn main() { - // bad - mem::discriminant(&&Some(2)); - mem::discriminant(&&None::); - mem::discriminant(&&Foo::One(5)); - mem::discriminant(&&Foo::Two(5)); - - let ro = &Some(3); - let rro = &ro; - mem::discriminant(&ro); - mem::discriminant(rro); - mem::discriminant(&rro); - - macro_rules! mem_discriminant_but_in_a_macro { - ($param:expr) => { - mem::discriminant($param) - }; - } - - mem_discriminant_but_in_a_macro!(&rro); - - let rrrrro = &&&rro; - mem::discriminant(&rrrrro); - mem::discriminant(*rrrrro); - - // ok - mem::discriminant(&Some(2)); - mem::discriminant(&None::); - mem::discriminant(&Foo::One(5)); - mem::discriminant(&Foo::Two(5)); - mem::discriminant(ro); - mem::discriminant(*rro); - mem::discriminant(****rrrrro); -} diff --git a/tests/ui/mem_discriminant.stderr b/tests/ui/mem_discriminant.stderr deleted file mode 100644 index 36a225b759484..0000000000000 --- a/tests/ui/mem_discriminant.stderr +++ /dev/null @@ -1,94 +0,0 @@ -error: calling `mem::discriminant` on non-enum type `&std::option::Option` - --> $DIR/mem_discriminant.rs:14:5 - | -LL | mem::discriminant(&&Some(2)); - | ^^^^^^^^^^^^^^^^^^---------^ - | | - | help: try dereferencing: `&Some(2)` - | -note: the lint level is defined here - --> $DIR/mem_discriminant.rs:3:9 - | -LL | #![deny(clippy::mem_discriminant_non_enum)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: calling `mem::discriminant` on non-enum type `&std::option::Option` - --> $DIR/mem_discriminant.rs:15:5 - | -LL | mem::discriminant(&&None::); - | ^^^^^^^^^^^^^^^^^^------------^ - | | - | help: try dereferencing: `&None::` - -error: calling `mem::discriminant` on non-enum type `&Foo` - --> $DIR/mem_discriminant.rs:16:5 - | -LL | mem::discriminant(&&Foo::One(5)); - | ^^^^^^^^^^^^^^^^^^-------------^ - | | - | help: try dereferencing: `&Foo::One(5)` - -error: calling `mem::discriminant` on non-enum type `&Foo` - --> $DIR/mem_discriminant.rs:17:5 - | -LL | mem::discriminant(&&Foo::Two(5)); - | ^^^^^^^^^^^^^^^^^^-------------^ - | | - | help: try dereferencing: `&Foo::Two(5)` - -error: calling `mem::discriminant` on non-enum type `&std::option::Option` - --> $DIR/mem_discriminant.rs:21:5 - | -LL | mem::discriminant(&ro); - | ^^^^^^^^^^^^^^^^^^---^ - | | - | help: try dereferencing: `ro` - -error: calling `mem::discriminant` on non-enum type `&std::option::Option` - --> $DIR/mem_discriminant.rs:22:5 - | -LL | mem::discriminant(rro); - | ^^^^^^^^^^^^^^^^^^---^ - | | - | help: try dereferencing: `*rro` - -error: calling `mem::discriminant` on non-enum type `&&std::option::Option` - --> $DIR/mem_discriminant.rs:23:5 - | -LL | mem::discriminant(&rro); - | ^^^^^^^^^^^^^^^^^^----^ - | | - | help: try dereferencing: `*rro` - -error: calling `mem::discriminant` on non-enum type `&&std::option::Option` - --> $DIR/mem_discriminant.rs:27:13 - | -LL | mem::discriminant($param) - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | mem_discriminant_but_in_a_macro!(&rro); - | --------------------------------------- - | | | - | | help: try dereferencing: `*rro` - | in this macro invocation - | - = note: this error originates in the macro `mem_discriminant_but_in_a_macro` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: calling `mem::discriminant` on non-enum type `&&&&&std::option::Option` - --> $DIR/mem_discriminant.rs:34:5 - | -LL | mem::discriminant(&rrrrro); - | ^^^^^^^^^^^^^^^^^^-------^ - | | - | help: try dereferencing: `****rrrrro` - -error: calling `mem::discriminant` on non-enum type `&&&std::option::Option` - --> $DIR/mem_discriminant.rs:35:5 - | -LL | mem::discriminant(*rrrrro); - | ^^^^^^^^^^^^^^^^^^-------^ - | | - | help: try dereferencing: `****rrrrro` - -error: aborting due to 10 previous errors - diff --git a/tests/ui/mem_discriminant_unfixable.rs b/tests/ui/mem_discriminant_unfixable.rs deleted file mode 100644 index e245d3257d55d..0000000000000 --- a/tests/ui/mem_discriminant_unfixable.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![deny(clippy::mem_discriminant_non_enum)] - -use std::mem; - -enum Foo { - One(usize), - Two(u8), -} - -struct A(Foo); - -fn main() { - // bad - mem::discriminant(&"hello"); - mem::discriminant(&A(Foo::One(0))); -} diff --git a/tests/ui/mem_discriminant_unfixable.stderr b/tests/ui/mem_discriminant_unfixable.stderr deleted file mode 100644 index e2de3776f2c91..0000000000000 --- a/tests/ui/mem_discriminant_unfixable.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: calling `mem::discriminant` on non-enum type `&str` - --> $DIR/mem_discriminant_unfixable.rs:14:5 - | -LL | mem::discriminant(&"hello"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/mem_discriminant_unfixable.rs:1:9 - | -LL | #![deny(clippy::mem_discriminant_non_enum)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: calling `mem::discriminant` on non-enum type `A` - --> $DIR/mem_discriminant_unfixable.rs:15:5 - | -LL | mem::discriminant(&A(Foo::One(0))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 13fbb6e2a6eed..a66c2e587c873 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -8,6 +8,7 @@ #![allow(clippy::redundant_static_lifetimes)] // warn for the old lint name here, to test if the renaming worked #![warn(clippy::cognitive_complexity)] +#![warn(enum_intrinsics_non_enums)] #[warn(clippy::module_name_repetitions)] fn main() {} diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index cbd3b1e91666a..fa81201a2daf3 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -8,6 +8,7 @@ #![allow(clippy::redundant_static_lifetimes)] // warn for the old lint name here, to test if the renaming worked #![warn(clippy::cyclomatic_complexity)] +#![warn(clippy::mem_discriminant_non_enum)] #[warn(clippy::stutter)] fn main() {} diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index c5d633ff86bfc..05c7854074c60 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -6,23 +6,29 @@ LL | #![warn(clippy::cyclomatic_complexity)] | = note: `-D renamed-and-removed-lints` implied by `-D warnings` +error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` + --> $DIR/rename.rs:11:9 + | +LL | #![warn(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` + error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:12:8 + --> $DIR/rename.rs:13:8 | LL | #[warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:15:8 + --> $DIR/rename.rs:16:8 | 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` - --> $DIR/rename.rs:18:8 + --> $DIR/rename.rs:19:8 | LL | #[warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors From 44db27127249cf30a4d7a2e6e62762762e38a3cb Mon Sep 17 00:00:00 2001 From: John Kugelman Date: Mon, 11 Oct 2021 10:13:50 -0400 Subject: [PATCH 029/101] Add #[must_use] to From::from and Into::into --- tests/ui/cast_lossless_float.fixed | 22 ++--- tests/ui/cast_lossless_float.rs | 22 ++--- tests/ui/cast_lossless_float.stderr | 66 +++++++-------- tests/ui/cast_lossless_integer.fixed | 38 ++++----- tests/ui/cast_lossless_integer.rs | 38 ++++----- tests/ui/cast_lossless_integer.stderr | 114 +++++++++++++------------- 6 files changed, 150 insertions(+), 150 deletions(-) diff --git a/tests/ui/cast_lossless_float.fixed b/tests/ui/cast_lossless_float.fixed index 709d58b596c83..32a9c1c4ae1af 100644 --- a/tests/ui/cast_lossless_float.fixed +++ b/tests/ui/cast_lossless_float.fixed @@ -6,24 +6,24 @@ fn main() { // Test clippy::cast_lossless with casts to floating-point types let x0 = 1i8; - f32::from(x0); - f64::from(x0); + let _ = f32::from(x0); + let _ = f64::from(x0); let x1 = 1u8; - f32::from(x1); - f64::from(x1); + let _ = f32::from(x1); + let _ = f64::from(x1); let x2 = 1i16; - f32::from(x2); - f64::from(x2); + let _ = f32::from(x2); + let _ = f64::from(x2); let x3 = 1u16; - f32::from(x3); - f64::from(x3); + let _ = f32::from(x3); + let _ = f64::from(x3); let x4 = 1i32; - f64::from(x4); + let _ = f64::from(x4); let x5 = 1u32; - f64::from(x5); + let _ = f64::from(x5); // Test with casts from floating-point types - f64::from(1.0f32); + let _ = f64::from(1.0f32); } // The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, diff --git a/tests/ui/cast_lossless_float.rs b/tests/ui/cast_lossless_float.rs index eb0aab8864290..6f5ddcfe09c8a 100644 --- a/tests/ui/cast_lossless_float.rs +++ b/tests/ui/cast_lossless_float.rs @@ -6,24 +6,24 @@ fn main() { // Test clippy::cast_lossless with casts to floating-point types let x0 = 1i8; - x0 as f32; - x0 as f64; + let _ = x0 as f32; + let _ = x0 as f64; let x1 = 1u8; - x1 as f32; - x1 as f64; + let _ = x1 as f32; + let _ = x1 as f64; let x2 = 1i16; - x2 as f32; - x2 as f64; + let _ = x2 as f32; + let _ = x2 as f64; let x3 = 1u16; - x3 as f32; - x3 as f64; + let _ = x3 as f32; + let _ = x3 as f64; let x4 = 1i32; - x4 as f64; + let _ = x4 as f64; let x5 = 1u32; - x5 as f64; + let _ = x5 as f64; // Test with casts from floating-point types - 1.0f32 as f64; + let _ = 1.0f32 as f64; } // The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, diff --git a/tests/ui/cast_lossless_float.stderr b/tests/ui/cast_lossless_float.stderr index 0ed09f3083c28..8326d40be7165 100644 --- a/tests/ui/cast_lossless_float.stderr +++ b/tests/ui/cast_lossless_float.stderr @@ -1,70 +1,70 @@ error: casting `i8` to `f32` may become silently lossy if you later change the type - --> $DIR/cast_lossless_float.rs:9:5 + --> $DIR/cast_lossless_float.rs:9:13 | -LL | x0 as f32; - | ^^^^^^^^^ help: try: `f32::from(x0)` +LL | let _ = x0 as f32; + | ^^^^^^^^^ help: try: `f32::from(x0)` | = note: `-D clippy::cast-lossless` implied by `-D warnings` error: casting `i8` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_float.rs:10:5 + --> $DIR/cast_lossless_float.rs:10:13 | -LL | x0 as f64; - | ^^^^^^^^^ help: try: `f64::from(x0)` +LL | let _ = x0 as f64; + | ^^^^^^^^^ help: try: `f64::from(x0)` error: casting `u8` to `f32` may become silently lossy if you later change the type - --> $DIR/cast_lossless_float.rs:12:5 + --> $DIR/cast_lossless_float.rs:12:13 | -LL | x1 as f32; - | ^^^^^^^^^ help: try: `f32::from(x1)` +LL | let _ = x1 as f32; + | ^^^^^^^^^ help: try: `f32::from(x1)` error: casting `u8` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_float.rs:13:5 + --> $DIR/cast_lossless_float.rs:13:13 | -LL | x1 as f64; - | ^^^^^^^^^ help: try: `f64::from(x1)` +LL | let _ = x1 as f64; + | ^^^^^^^^^ help: try: `f64::from(x1)` error: casting `i16` to `f32` may become silently lossy if you later change the type - --> $DIR/cast_lossless_float.rs:15:5 + --> $DIR/cast_lossless_float.rs:15:13 | -LL | x2 as f32; - | ^^^^^^^^^ help: try: `f32::from(x2)` +LL | let _ = x2 as f32; + | ^^^^^^^^^ help: try: `f32::from(x2)` error: casting `i16` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_float.rs:16:5 + --> $DIR/cast_lossless_float.rs:16:13 | -LL | x2 as f64; - | ^^^^^^^^^ help: try: `f64::from(x2)` +LL | let _ = x2 as f64; + | ^^^^^^^^^ help: try: `f64::from(x2)` error: casting `u16` to `f32` may become silently lossy if you later change the type - --> $DIR/cast_lossless_float.rs:18:5 + --> $DIR/cast_lossless_float.rs:18:13 | -LL | x3 as f32; - | ^^^^^^^^^ help: try: `f32::from(x3)` +LL | let _ = x3 as f32; + | ^^^^^^^^^ help: try: `f32::from(x3)` error: casting `u16` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_float.rs:19:5 + --> $DIR/cast_lossless_float.rs:19:13 | -LL | x3 as f64; - | ^^^^^^^^^ help: try: `f64::from(x3)` +LL | let _ = x3 as f64; + | ^^^^^^^^^ help: try: `f64::from(x3)` error: casting `i32` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_float.rs:21:5 + --> $DIR/cast_lossless_float.rs:21:13 | -LL | x4 as f64; - | ^^^^^^^^^ help: try: `f64::from(x4)` +LL | let _ = x4 as f64; + | ^^^^^^^^^ help: try: `f64::from(x4)` error: casting `u32` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_float.rs:23:5 + --> $DIR/cast_lossless_float.rs:23:13 | -LL | x5 as f64; - | ^^^^^^^^^ help: try: `f64::from(x5)` +LL | let _ = x5 as f64; + | ^^^^^^^^^ help: try: `f64::from(x5)` error: casting `f32` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_float.rs:26:5 + --> $DIR/cast_lossless_float.rs:26:13 | -LL | 1.0f32 as f64; - | ^^^^^^^^^^^^^ help: try: `f64::from(1.0f32)` +LL | let _ = 1.0f32 as f64; + | ^^^^^^^^^^^^^ help: try: `f64::from(1.0f32)` error: aborting due to 11 previous errors diff --git a/tests/ui/cast_lossless_integer.fixed b/tests/ui/cast_lossless_integer.fixed index 03e49adb117db..72a708b40737b 100644 --- a/tests/ui/cast_lossless_integer.fixed +++ b/tests/ui/cast_lossless_integer.fixed @@ -5,27 +5,27 @@ fn main() { // Test clippy::cast_lossless with casts to integer types - i16::from(1i8); - i32::from(1i8); - i64::from(1i8); - i16::from(1u8); - i32::from(1u8); - i64::from(1u8); - u16::from(1u8); - u32::from(1u8); - u64::from(1u8); - i32::from(1i16); - i64::from(1i16); - i32::from(1u16); - i64::from(1u16); - u32::from(1u16); - u64::from(1u16); - i64::from(1i32); - i64::from(1u32); - u64::from(1u32); + let _ = i16::from(1i8); + let _ = i32::from(1i8); + let _ = i64::from(1i8); + let _ = i16::from(1u8); + let _ = i32::from(1u8); + let _ = i64::from(1u8); + let _ = u16::from(1u8); + let _ = u32::from(1u8); + let _ = u64::from(1u8); + let _ = i32::from(1i16); + let _ = i64::from(1i16); + let _ = i32::from(1u16); + let _ = i64::from(1u16); + let _ = u32::from(1u16); + let _ = u64::from(1u16); + let _ = i64::from(1i32); + let _ = i64::from(1u32); + let _ = u64::from(1u32); // Test with an expression wrapped in parens - u16::from(1u8 + 1u8); + let _ = u16::from(1u8 + 1u8); } // The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, diff --git a/tests/ui/cast_lossless_integer.rs b/tests/ui/cast_lossless_integer.rs index 6a984d245963f..34bb47181e69d 100644 --- a/tests/ui/cast_lossless_integer.rs +++ b/tests/ui/cast_lossless_integer.rs @@ -5,27 +5,27 @@ fn main() { // Test clippy::cast_lossless with casts to integer types - 1i8 as i16; - 1i8 as i32; - 1i8 as i64; - 1u8 as i16; - 1u8 as i32; - 1u8 as i64; - 1u8 as u16; - 1u8 as u32; - 1u8 as u64; - 1i16 as i32; - 1i16 as i64; - 1u16 as i32; - 1u16 as i64; - 1u16 as u32; - 1u16 as u64; - 1i32 as i64; - 1u32 as i64; - 1u32 as u64; + let _ = 1i8 as i16; + let _ = 1i8 as i32; + let _ = 1i8 as i64; + let _ = 1u8 as i16; + let _ = 1u8 as i32; + let _ = 1u8 as i64; + let _ = 1u8 as u16; + let _ = 1u8 as u32; + let _ = 1u8 as u64; + let _ = 1i16 as i32; + let _ = 1i16 as i64; + let _ = 1u16 as i32; + let _ = 1u16 as i64; + let _ = 1u16 as u32; + let _ = 1u16 as u64; + let _ = 1i32 as i64; + let _ = 1u32 as i64; + let _ = 1u32 as u64; // Test with an expression wrapped in parens - (1u8 + 1u8) as u16; + let _ = (1u8 + 1u8) as u16; } // The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, diff --git a/tests/ui/cast_lossless_integer.stderr b/tests/ui/cast_lossless_integer.stderr index 8e2890f9c28d0..721b94876cb2c 100644 --- a/tests/ui/cast_lossless_integer.stderr +++ b/tests/ui/cast_lossless_integer.stderr @@ -1,118 +1,118 @@ error: casting `i8` to `i16` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:8:5 + --> $DIR/cast_lossless_integer.rs:8:13 | -LL | 1i8 as i16; - | ^^^^^^^^^^ help: try: `i16::from(1i8)` +LL | let _ = 1i8 as i16; + | ^^^^^^^^^^ help: try: `i16::from(1i8)` | = note: `-D clippy::cast-lossless` implied by `-D warnings` error: casting `i8` to `i32` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:9:5 + --> $DIR/cast_lossless_integer.rs:9:13 | -LL | 1i8 as i32; - | ^^^^^^^^^^ help: try: `i32::from(1i8)` +LL | let _ = 1i8 as i32; + | ^^^^^^^^^^ help: try: `i32::from(1i8)` error: casting `i8` to `i64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:10:5 + --> $DIR/cast_lossless_integer.rs:10:13 | -LL | 1i8 as i64; - | ^^^^^^^^^^ help: try: `i64::from(1i8)` +LL | let _ = 1i8 as i64; + | ^^^^^^^^^^ help: try: `i64::from(1i8)` error: casting `u8` to `i16` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:11:5 + --> $DIR/cast_lossless_integer.rs:11:13 | -LL | 1u8 as i16; - | ^^^^^^^^^^ help: try: `i16::from(1u8)` +LL | let _ = 1u8 as i16; + | ^^^^^^^^^^ help: try: `i16::from(1u8)` error: casting `u8` to `i32` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:12:5 + --> $DIR/cast_lossless_integer.rs:12:13 | -LL | 1u8 as i32; - | ^^^^^^^^^^ help: try: `i32::from(1u8)` +LL | let _ = 1u8 as i32; + | ^^^^^^^^^^ help: try: `i32::from(1u8)` error: casting `u8` to `i64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:13:5 + --> $DIR/cast_lossless_integer.rs:13:13 | -LL | 1u8 as i64; - | ^^^^^^^^^^ help: try: `i64::from(1u8)` +LL | let _ = 1u8 as i64; + | ^^^^^^^^^^ help: try: `i64::from(1u8)` error: casting `u8` to `u16` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:14:5 + --> $DIR/cast_lossless_integer.rs:14:13 | -LL | 1u8 as u16; - | ^^^^^^^^^^ help: try: `u16::from(1u8)` +LL | let _ = 1u8 as u16; + | ^^^^^^^^^^ help: try: `u16::from(1u8)` error: casting `u8` to `u32` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:15:5 + --> $DIR/cast_lossless_integer.rs:15:13 | -LL | 1u8 as u32; - | ^^^^^^^^^^ help: try: `u32::from(1u8)` +LL | let _ = 1u8 as u32; + | ^^^^^^^^^^ help: try: `u32::from(1u8)` error: casting `u8` to `u64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:16:5 + --> $DIR/cast_lossless_integer.rs:16:13 | -LL | 1u8 as u64; - | ^^^^^^^^^^ help: try: `u64::from(1u8)` +LL | let _ = 1u8 as u64; + | ^^^^^^^^^^ help: try: `u64::from(1u8)` error: casting `i16` to `i32` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:17:5 + --> $DIR/cast_lossless_integer.rs:17:13 | -LL | 1i16 as i32; - | ^^^^^^^^^^^ help: try: `i32::from(1i16)` +LL | let _ = 1i16 as i32; + | ^^^^^^^^^^^ help: try: `i32::from(1i16)` error: casting `i16` to `i64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:18:5 + --> $DIR/cast_lossless_integer.rs:18:13 | -LL | 1i16 as i64; - | ^^^^^^^^^^^ help: try: `i64::from(1i16)` +LL | let _ = 1i16 as i64; + | ^^^^^^^^^^^ help: try: `i64::from(1i16)` error: casting `u16` to `i32` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:19:5 + --> $DIR/cast_lossless_integer.rs:19:13 | -LL | 1u16 as i32; - | ^^^^^^^^^^^ help: try: `i32::from(1u16)` +LL | let _ = 1u16 as i32; + | ^^^^^^^^^^^ help: try: `i32::from(1u16)` error: casting `u16` to `i64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:20:5 + --> $DIR/cast_lossless_integer.rs:20:13 | -LL | 1u16 as i64; - | ^^^^^^^^^^^ help: try: `i64::from(1u16)` +LL | let _ = 1u16 as i64; + | ^^^^^^^^^^^ help: try: `i64::from(1u16)` error: casting `u16` to `u32` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:21:5 + --> $DIR/cast_lossless_integer.rs:21:13 | -LL | 1u16 as u32; - | ^^^^^^^^^^^ help: try: `u32::from(1u16)` +LL | let _ = 1u16 as u32; + | ^^^^^^^^^^^ help: try: `u32::from(1u16)` error: casting `u16` to `u64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:22:5 + --> $DIR/cast_lossless_integer.rs:22:13 | -LL | 1u16 as u64; - | ^^^^^^^^^^^ help: try: `u64::from(1u16)` +LL | let _ = 1u16 as u64; + | ^^^^^^^^^^^ help: try: `u64::from(1u16)` error: casting `i32` to `i64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:23:5 + --> $DIR/cast_lossless_integer.rs:23:13 | -LL | 1i32 as i64; - | ^^^^^^^^^^^ help: try: `i64::from(1i32)` +LL | let _ = 1i32 as i64; + | ^^^^^^^^^^^ help: try: `i64::from(1i32)` error: casting `u32` to `i64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:24:5 + --> $DIR/cast_lossless_integer.rs:24:13 | -LL | 1u32 as i64; - | ^^^^^^^^^^^ help: try: `i64::from(1u32)` +LL | let _ = 1u32 as i64; + | ^^^^^^^^^^^ help: try: `i64::from(1u32)` error: casting `u32` to `u64` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:25:5 + --> $DIR/cast_lossless_integer.rs:25:13 | -LL | 1u32 as u64; - | ^^^^^^^^^^^ help: try: `u64::from(1u32)` +LL | let _ = 1u32 as u64; + | ^^^^^^^^^^^ help: try: `u64::from(1u32)` error: casting `u8` to `u16` may become silently lossy if you later change the type - --> $DIR/cast_lossless_integer.rs:28:5 + --> $DIR/cast_lossless_integer.rs:28:13 | -LL | (1u8 + 1u8) as u16; - | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)` +LL | let _ = (1u8 + 1u8) as u16; + | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)` error: aborting due to 19 previous errors From 9e0ce14700070a5cf61ebdb542cefd70a1dd9b0c Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 11 Oct 2021 20:19:34 -0400 Subject: [PATCH 030/101] Add match_str_case_mismatch lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_correctness.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/match_str_case_mismatch.rs | 166 +++++++++++++++++++ tests/ui/match_str_case_mismatch.rs | 98 +++++++++++ tests/ui/match_str_case_mismatch.stderr | 53 ++++++ 8 files changed, 323 insertions(+) create mode 100644 clippy_lints/src/match_str_case_mismatch.rs create mode 100644 tests/ui/match_str_case_mismatch.rs create mode 100644 tests/ui/match_str_case_mismatch.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 13067e4e92ae3..1f79d9e73db56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2837,6 +2837,7 @@ Released 2018-09-13 [`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok [`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms [`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding +[`match_str_case_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_str_case_mismatch [`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm [`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants [`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 3e6e0244754fb..e544c6607e8d1 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -118,6 +118,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(match_result_ok::MATCH_RESULT_OK), + LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index e0ef7b3b8af9f..1c2441d1af251 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -36,6 +36,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::NEVER_LOOP), LintId::of(loops::WHILE_IMMUTABLE_CONDITION), + LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH), LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(methods::CLONE_DOUBLE_REF), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 3c4b720671a66..d5673ad2c7ceb 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -226,6 +226,7 @@ store.register_lints(&[ map_unit_fn::RESULT_MAP_UNIT_FN, match_on_vec_items::MATCH_ON_VEC_ITEMS, match_result_ok::MATCH_RESULT_OK, + match_str_case_mismatch::MATCH_STR_CASE_MISMATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, matches::MATCH_AS_REF, matches::MATCH_BOOL, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7e1bcbbd0ed04..73d21eb5b52f8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -265,6 +265,7 @@ mod map_err_ignore; mod map_unit_fn; mod match_on_vec_items; mod match_result_ok; +mod match_str_case_mismatch; mod matches; mod mem_discriminant; mod mem_forget; @@ -771,6 +772,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send; store.register_late_pass(move || Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic_for_send))); store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default())); + store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch)); } #[rustfmt::skip] diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/match_str_case_mismatch.rs new file mode 100644 index 0000000000000..dd8b5cb4a21a2 --- /dev/null +++ b/clippy_lints/src/match_str_case_mismatch.rs @@ -0,0 +1,166 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_ast::ast::LitKind; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `match` expressions modifying the case of a string with non-compliant arms + /// + /// ### Why is this bad? + /// The arm is unreachable, which is likely a mistake + /// + /// ### Example + /// ```rust + /// match &*text.to_ascii_lowercase() { + /// "foo" => {}, + /// "Bar" => {}, + /// _ => {}, + /// } + /// ``` + /// Use instead: + /// ```rust + /// match &*text.to_ascii_lowercase() { + /// "foo" => {}, + /// "bar" => {}, + /// _ => {}, + /// } + /// ``` + pub MATCH_STR_CASE_MISMATCH, + correctness, + "creation of a case altering match expression with non-compliant arms" +} + +declare_lint_pass!(MatchStrCaseMismatch => [MATCH_STR_CASE_MISMATCH]); + +#[derive(Debug)] +enum CaseMethod { + LowerCase, + AsciiLowerCase, + UpperCase, + AsciiUppercase, +} + +impl LateLintPass<'_> for MatchStrCaseMismatch { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if !in_external_macro(cx.tcx.sess, expr.span); + if let ExprKind::Match(match_expr, arms, MatchSource::Normal) = expr.kind; + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(match_expr).kind(); + if let ty::Str = ty.kind(); + then { + let mut visitor = MatchExprVisitor { + cx, + case_method: None, + }; + + visitor.visit_expr(match_expr); + + if let Some(case_method) = visitor.case_method { + if let Some(bad_case) = verify_case(&case_method, arms) { + lint(cx, expr.span, &case_method, bad_case); + } + } + } + } + } +} + +struct MatchExprVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + case_method: Option, +} + +impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { + match ex.kind { + ExprKind::MethodCall(segment, _, [receiver], _) + if self.case_altered(&*segment.ident.as_str(), receiver) => {}, + _ => walk_expr(self, ex), + } + } +} + +impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> { + fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool { + if let Some(case_method) = get_case_method(segment_ident) { + let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); + + if is_type_diagnostic_item(self.cx, ty, sym::String) || ty.kind() == &ty::Str { + self.case_method = Some(case_method); + return true; + } + } + + false + } +} + +fn get_case_method(segment_ident_str: &str) -> Option { + match segment_ident_str { + "to_lowercase" => Some(CaseMethod::LowerCase), + "to_ascii_lowercase" => Some(CaseMethod::AsciiLowerCase), + "to_uppercase" => Some(CaseMethod::UpperCase), + "to_ascii_uppercase" => Some(CaseMethod::AsciiUppercase), + _ => None, + } +} + +fn verify_case(case_method: &CaseMethod, arms: &'_ [Arm<'_>]) -> Option { + let mut bad_case = None; + + let case_check = match case_method { + CaseMethod::LowerCase => |input: &str| -> bool { input.chars().all(char::is_lowercase) }, + CaseMethod::AsciiLowerCase => |input: &str| -> bool { input.chars().all(|c| matches!(c, 'a'..='z')) }, + CaseMethod::UpperCase => |input: &str| -> bool { input.chars().all(char::is_uppercase) }, + CaseMethod::AsciiUppercase => |input: &str| -> bool { input.chars().all(|c| matches!(c, 'A'..='Z')) }, + }; + + for arm in arms { + if_chain! { + if let PatKind::Lit(Expr { + kind: ExprKind::Lit(lit), + .. + }) = arm.pat.kind; + if let LitKind::Str(symbol, _) = lit.node; + if !case_check(&symbol.as_str()); + then { + bad_case = Some(lit.span); + break; + } + } + } + + bad_case +} + +fn lint(cx: &LateContext<'_>, expr_span: Span, case_method: &CaseMethod, bad_case_span: Span) { + let method_str = match case_method { + CaseMethod::LowerCase => "to_lower_case", + CaseMethod::AsciiLowerCase => "to_ascii_lowercase", + CaseMethod::UpperCase => "to_uppercase", + CaseMethod::AsciiUppercase => "to_ascii_uppercase", + }; + + span_lint_and_help( + cx, + MATCH_STR_CASE_MISMATCH, + expr_span, + "this `match` expression alters case, but has non-compliant arms", + Some(bad_case_span), + &*format!("consider changing the case of this arm to respect `{}`", method_str), + ); +} diff --git a/tests/ui/match_str_case_mismatch.rs b/tests/ui/match_str_case_mismatch.rs new file mode 100644 index 0000000000000..208a4bba3d23c --- /dev/null +++ b/tests/ui/match_str_case_mismatch.rs @@ -0,0 +1,98 @@ +#![warn(clippy::match_str_case_mismatch)] + +// Valid + +fn as_str_match() { + let var = "BAR"; + + match var.to_ascii_lowercase().as_str() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn addrof_unary_match() { + let var = "BAR"; + + match &*var.to_ascii_lowercase() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn alternating_chain() { + let var = "BAR"; + + match &*var + .to_ascii_lowercase() + .to_uppercase() + .to_lowercase() + .to_ascii_uppercase() + { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +fn unrelated_method() { + struct Item { + a: String, + } + + impl Item { + #[allow(clippy::wrong_self_convention)] + fn to_lowercase(self) -> String { + self.a + } + } + + let item = Item { a: String::from("BAR") }; + + match &*item.to_lowercase() { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +// Invalid + +fn as_str_match_mismatch() { + let var = "BAR"; + + match var.to_ascii_lowercase().as_str() { + "foo" => {}, + "Bar" => {}, + _ => {}, + } +} + +fn addrof_unary_match_mismatch() { + let var = "BAR"; + + match &*var.to_ascii_lowercase() { + "foo" => {}, + "Bar" => {}, + _ => {}, + } +} + +fn alternating_chain_mismatch() { + let var = "BAR"; + + match &*var + .to_ascii_lowercase() + .to_uppercase() + .to_lowercase() + .to_ascii_uppercase() + { + "FOO" => {}, + "bAR" => {}, + _ => {}, + } +} + +fn main() {} diff --git a/tests/ui/match_str_case_mismatch.stderr b/tests/ui/match_str_case_mismatch.stderr new file mode 100644 index 0000000000000..a5eab1f72f39f --- /dev/null +++ b/tests/ui/match_str_case_mismatch.stderr @@ -0,0 +1,53 @@ +error: this `match` expression alters case, but has non-compliant arms + --> $DIR/match_str_case_mismatch.rs:66:5 + | +LL | / match var.to_ascii_lowercase().as_str() { +LL | | "foo" => {}, +LL | | "Bar" => {}, +LL | | _ => {}, +LL | | } + | |_____^ + | + = note: `-D clippy::match-str-case-mismatch` implied by `-D warnings` +help: consider changing the case of this arm to respect `to_ascii_lowercase` + --> $DIR/match_str_case_mismatch.rs:68:9 + | +LL | "Bar" => {}, + | ^^^^^ + +error: this `match` expression alters case, but has non-compliant arms + --> $DIR/match_str_case_mismatch.rs:76:5 + | +LL | / match &*var.to_ascii_lowercase() { +LL | | "foo" => {}, +LL | | "Bar" => {}, +LL | | _ => {}, +LL | | } + | |_____^ + | +help: consider changing the case of this arm to respect `to_ascii_lowercase` + --> $DIR/match_str_case_mismatch.rs:78:9 + | +LL | "Bar" => {}, + | ^^^^^ + +error: this `match` expression alters case, but has non-compliant arms + --> $DIR/match_str_case_mismatch.rs:86:5 + | +LL | / match &*var +LL | | .to_ascii_lowercase() +LL | | .to_uppercase() +LL | | .to_lowercase() +... | +LL | | _ => {}, +LL | | } + | |_____^ + | +help: consider changing the case of this arm to respect `to_ascii_uppercase` + --> $DIR/match_str_case_mismatch.rs:93:9 + | +LL | "bAR" => {}, + | ^^^^^ + +error: aborting due to 3 previous errors + From 99bfee720119240d8eb64c2fd395aeaecc683b9a Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 11 Oct 2021 20:40:13 -0400 Subject: [PATCH 031/101] Don't run examples --- clippy_lints/src/match_str_case_mismatch.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/match_str_case_mismatch.rs index dd8b5cb4a21a2..ae6d2b6c79da1 100644 --- a/clippy_lints/src/match_str_case_mismatch.rs +++ b/clippy_lints/src/match_str_case_mismatch.rs @@ -18,7 +18,7 @@ declare_clippy_lint! { /// The arm is unreachable, which is likely a mistake /// /// ### Example - /// ```rust + /// ```rust,no_run /// match &*text.to_ascii_lowercase() { /// "foo" => {}, /// "Bar" => {}, @@ -26,7 +26,7 @@ declare_clippy_lint! { /// } /// ``` /// Use instead: - /// ```rust + /// ```rust,no_run /// match &*text.to_ascii_lowercase() { /// "foo" => {}, /// "bar" => {}, From 5adf17cb43d62e7e02c13f32def548b724bf2148 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 12 Oct 2021 09:42:42 +0200 Subject: [PATCH 032/101] Bring `manual_split_once` docs in line with other lint docs --- clippy_lints/src/methods/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b26d11c0d6b0d..26c29fbb289cb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1777,14 +1777,13 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usages of `str::splitn(2, _)` - /// - /// **Why is this bad?** `split_once` is both clearer in intent and slightly more efficient. - /// - /// **Known problems:** None. + /// ### What it does + /// Checks for usages of `str::splitn(2, _)` /// - /// **Example:** + /// ### Why is this bad? + /// `split_once` is both clearer in intent and slightly more efficient. /// + /// ### Example /// ```rust,ignore /// // Bad /// let (key, value) = _.splitn(2, '=').next_tuple()?; From 0c99de0ab2a83740c921e4b4bfc67177c787ff36 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Tue, 12 Oct 2021 07:13:19 -0400 Subject: [PATCH 033/101] Add a suggestion --- clippy_lints/src/match_str_case_mismatch.rs | 49 ++++++++++--------- tests/ui/match_str_case_mismatch.stderr | 53 +++++++-------------- 2 files changed, 45 insertions(+), 57 deletions(-) diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/match_str_case_mismatch.rs index ae6d2b6c79da1..a83f38e3d516e 100644 --- a/clippy_lints/src/match_str_case_mismatch.rs +++ b/clippy_lints/src/match_str_case_mismatch.rs @@ -1,6 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -8,6 +9,7 @@ use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::SymbolStr; use rustc_span::{sym, Span}; declare_clippy_lint! { @@ -18,7 +20,9 @@ declare_clippy_lint! { /// The arm is unreachable, which is likely a mistake /// /// ### Example - /// ```rust,no_run + /// ```rust + /// # let text = "Foo"; + /// /// match &*text.to_ascii_lowercase() { /// "foo" => {}, /// "Bar" => {}, @@ -26,7 +30,9 @@ declare_clippy_lint! { /// } /// ``` /// Use instead: - /// ```rust,no_run + /// ```rust + /// # let text = "Foo"; + /// /// match &*text.to_ascii_lowercase() { /// "foo" => {}, /// "bar" => {}, @@ -64,8 +70,8 @@ impl LateLintPass<'_> for MatchStrCaseMismatch { visitor.visit_expr(match_expr); if let Some(case_method) = visitor.case_method { - if let Some(bad_case) = verify_case(&case_method, arms) { - lint(cx, expr.span, &case_method, bad_case); + if let Some((bad_case_span, bad_case_str)) = verify_case(&case_method, arms) { + lint(cx, &case_method, bad_case_span, &bad_case_str); } } } @@ -119,9 +125,7 @@ fn get_case_method(segment_ident_str: &str) -> Option { } } -fn verify_case(case_method: &CaseMethod, arms: &'_ [Arm<'_>]) -> Option { - let mut bad_case = None; - +fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(Span, SymbolStr)> { let case_check = match case_method { CaseMethod::LowerCase => |input: &str| -> bool { input.chars().all(char::is_lowercase) }, CaseMethod::AsciiLowerCase => |input: &str| -> bool { input.chars().all(|c| matches!(c, 'a'..='z')) }, @@ -136,31 +140,32 @@ fn verify_case(case_method: &CaseMethod, arms: &'_ [Arm<'_>]) -> Option { .. }) = arm.pat.kind; if let LitKind::Str(symbol, _) = lit.node; - if !case_check(&symbol.as_str()); + let input = symbol.as_str(); + if !case_check(&input); then { - bad_case = Some(lit.span); - break; + return Some((lit.span, input)); } } } - bad_case + None } -fn lint(cx: &LateContext<'_>, expr_span: Span, case_method: &CaseMethod, bad_case_span: Span) { - let method_str = match case_method { - CaseMethod::LowerCase => "to_lower_case", - CaseMethod::AsciiLowerCase => "to_ascii_lowercase", - CaseMethod::UpperCase => "to_uppercase", - CaseMethod::AsciiUppercase => "to_ascii_uppercase", +fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad_case_str: &str) { + let (method_str, suggestion) = match case_method { + CaseMethod::LowerCase => ("to_lower_case", bad_case_str.to_lowercase()), + CaseMethod::AsciiLowerCase => ("to_ascii_lowercase", bad_case_str.to_ascii_lowercase()), + CaseMethod::UpperCase => ("to_uppercase", bad_case_str.to_uppercase()), + CaseMethod::AsciiUppercase => ("to_ascii_uppercase", bad_case_str.to_ascii_uppercase()), }; - span_lint_and_help( + span_lint_and_sugg( cx, MATCH_STR_CASE_MISMATCH, - expr_span, - "this `match` expression alters case, but has non-compliant arms", - Some(bad_case_span), + bad_case_span, + "this `match` arm has a differing case than its expression", &*format!("consider changing the case of this arm to respect `{}`", method_str), + format!("\"{}\"", suggestion), + Applicability::MachineApplicable, ); } diff --git a/tests/ui/match_str_case_mismatch.stderr b/tests/ui/match_str_case_mismatch.stderr index a5eab1f72f39f..fa023477a9c33 100644 --- a/tests/ui/match_str_case_mismatch.stderr +++ b/tests/ui/match_str_case_mismatch.stderr @@ -1,53 +1,36 @@ -error: this `match` expression alters case, but has non-compliant arms - --> $DIR/match_str_case_mismatch.rs:66:5 - | -LL | / match var.to_ascii_lowercase().as_str() { -LL | | "foo" => {}, -LL | | "Bar" => {}, -LL | | _ => {}, -LL | | } - | |_____^ - | - = note: `-D clippy::match-str-case-mismatch` implied by `-D warnings` -help: consider changing the case of this arm to respect `to_ascii_lowercase` +error: this `match` arm has a differing case than its expression --> $DIR/match_str_case_mismatch.rs:68:9 | LL | "Bar" => {}, | ^^^^^ - -error: this `match` expression alters case, but has non-compliant arms - --> $DIR/match_str_case_mismatch.rs:76:5 - | -LL | / match &*var.to_ascii_lowercase() { -LL | | "foo" => {}, -LL | | "Bar" => {}, -LL | | _ => {}, -LL | | } - | |_____^ | + = note: `-D clippy::match-str-case-mismatch` implied by `-D warnings` help: consider changing the case of this arm to respect `to_ascii_lowercase` + | +LL | "bar" => {}, + | ~~~~~ + +error: this `match` arm has a differing case than its expression --> $DIR/match_str_case_mismatch.rs:78:9 | LL | "Bar" => {}, | ^^^^^ - -error: this `match` expression alters case, but has non-compliant arms - --> $DIR/match_str_case_mismatch.rs:86:5 - | -LL | / match &*var -LL | | .to_ascii_lowercase() -LL | | .to_uppercase() -LL | | .to_lowercase() -... | -LL | | _ => {}, -LL | | } - | |_____^ | -help: consider changing the case of this arm to respect `to_ascii_uppercase` +help: consider changing the case of this arm to respect `to_ascii_lowercase` + | +LL | "bar" => {}, + | ~~~~~ + +error: this `match` arm has a differing case than its expression --> $DIR/match_str_case_mismatch.rs:93:9 | LL | "bAR" => {}, | ^^^^^ + | +help: consider changing the case of this arm to respect `to_ascii_uppercase` + | +LL | "BAR" => {}, + | ~~~~~ error: aborting due to 3 previous errors From 4ed3a4fe2f681660cac9a4fad6385c6d92a89de1 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Tue, 12 Oct 2021 16:01:58 -0400 Subject: [PATCH 034/101] Update lint description for new() and default() --- clippy_lints/src/uninit_vec.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 2573209d1b628..f3e8b6881058f 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -13,14 +13,16 @@ use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does /// Checks for `set_len()` call that creates `Vec` with uninitialized elements. - /// This is commonly caused by calling `set_len()` right after after calling - /// `with_capacity()` or `reserve()`. + /// This is commonly caused by calling `set_len()` right after allocating or + /// reserving a buffer with `new()`, `default()`, `with_capacity()`, or `reserve()`. /// /// ### Why is this bad? /// It creates a `Vec` with uninitialized data, which leads to - /// undefined behavior with most safe operations. + /// undefined behavior with most safe operations. Notably, uninitialized + /// `Vec` must not be used with generic `Read`. /// - /// Notably, uninitialized `Vec` must not be used with generic `Read`. + /// Moreover, calling `set_len()` on a `Vec` created with `new()` or `default()` + /// creates out-of-bound values that lead to heap memory corruption when used. /// /// ### Known Problems /// This lint only checks directly adjacent statements. From 049ab82662d4f63bd88bdcba97b172421f906dd5 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 13 Oct 2021 11:06:14 +0000 Subject: [PATCH 035/101] Update clippy ui output --- tests/ui/crashes/ice-6256.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/crashes/ice-6256.stderr b/tests/ui/crashes/ice-6256.stderr index d35d459168f23..ae4e6cad3328b 100644 --- a/tests/ui/crashes/ice-6256.stderr +++ b/tests/ui/crashes/ice-6256.stderr @@ -6,7 +6,7 @@ LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types | = note: expected reference `&(dyn TT + 'static)` found reference `&dyn TT` -note: the anonymous lifetime #1 defined on the body at 13:13... +note: the anonymous lifetime #1 defined here... --> $DIR/ice-6256.rs:13:13 | LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types From 58969807ab7a79caecf33ad8a03df8a9793a23ad Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Mon, 11 Oct 2021 23:46:49 +0100 Subject: [PATCH 036/101] Add lint transmute_num_to_bytes Closes #7803 changelog: [`transmute_num_to_bytes`] new lint --- 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/transmute/mod.rs | 25 +++++ .../src/transmute/transmute_num_to_bytes.rs | 49 ++++++++++ tests/ui/transmute.rs | 27 ++++++ tests/ui/transmute.stderr | 92 ++++++++++++++++++- 8 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 clippy_lints/src/transmute/transmute_num_to_bytes.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 13067e4e92ae3..4c0b1991fd405 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3024,6 +3024,7 @@ Released 2018-09-13 [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool [`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char [`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float +[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes [`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr [`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref [`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 3e6e0244754fb..8d0a3e7423b86 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -266,6 +266,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), + LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 64b82fc0faac8..c51341bdf0c23 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -82,6 +82,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), + LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(types::BORROWED_BOX), LintId::of(types::TYPE_COMPLEXITY), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 3c4b720671a66..8d17317c38e56 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -449,6 +449,7 @@ store.register_lints(&[ transmute::TRANSMUTE_INT_TO_BOOL, transmute::TRANSMUTE_INT_TO_CHAR, transmute::TRANSMUTE_INT_TO_FLOAT, + transmute::TRANSMUTE_NUM_TO_BYTES, transmute::TRANSMUTE_PTR_TO_PTR, transmute::TRANSMUTE_PTR_TO_REF, transmute::UNSOUND_COLLECTION_TRANSMUTE, diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 33ec9c331ce56..374999473a49a 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -3,6 +3,7 @@ mod transmute_float_to_int; mod transmute_int_to_bool; mod transmute_int_to_char; mod transmute_int_to_float; +mod transmute_num_to_bytes; mod transmute_ptr_to_ptr; mod transmute_ptr_to_ref; mod transmute_ref_to_ref; @@ -261,6 +262,28 @@ declare_clippy_lint! { "transmutes from a float to an integer" } +declare_clippy_lint! { + /// # What it does + /// Checks for transmutes from a number to an array of `u8` + /// + /// ### Why this is bad? + /// Transmutes are dangerous and error-prone, whereas `to_ne_bytes` + /// is intuitive and safe. + /// + /// ### Example + /// ```rust + /// unsafe { + /// let x: [u8; 8] = std::mem::transmute(1i64); + /// } + /// + /// // should be + /// let x: [u8; 8] = 0i64.to_ne_bytes(); + /// ``` + pub TRANSMUTE_NUM_TO_BYTES, + complexity, + "transmutes from a number to an array of `u8`" +} + declare_clippy_lint! { /// ### What it does /// Checks for transmutes from a pointer to a pointer, or @@ -330,6 +353,7 @@ declare_lint_pass!(Transmute => [ TRANSMUTE_INT_TO_BOOL, TRANSMUTE_INT_TO_FLOAT, TRANSMUTE_FLOAT_TO_INT, + TRANSMUTE_NUM_TO_BYTES, UNSOUND_COLLECTION_TRANSMUTE, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, ]); @@ -365,6 +389,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { linted |= transmute_int_to_bool::check(cx, e, from_ty, to_ty, args); linted |= transmute_int_to_float::check(cx, e, from_ty, to_ty, args, const_context); linted |= transmute_float_to_int::check(cx, e, from_ty, to_ty, args, const_context); + linted |= transmute_num_to_bytes::check(cx, e, from_ty, to_ty, args, const_context); linted |= unsound_collection_transmute::check(cx, e, from_ty, to_ty); if !linted { diff --git a/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/clippy_lints/src/transmute/transmute_num_to_bytes.rs new file mode 100644 index 0000000000000..dd5ded1c91a17 --- /dev/null +++ b/clippy_lints/src/transmute/transmute_num_to_bytes.rs @@ -0,0 +1,49 @@ +use super::TRANSMUTE_NUM_TO_BYTES; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty, UintTy}; + +/// Checks for `transmute_int_to_float` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + args: &'tcx [Expr<'_>], + const_context: bool, +) -> bool { + match (&from_ty.kind(), &to_ty.kind()) { + (ty::Int(_) | ty::Uint(_) | ty::Float(_), ty::Array(arr_ty, _)) => { + if !matches!(arr_ty.kind(), ty::Uint(UintTy::U8)) { + return false; + } + if matches!(from_ty.kind(), ty::Float(_)) && const_context { + // TODO: Remove when const_float_bits_conv is stabilized + // rust#72447 + return false; + } + + span_lint_and_then( + cx, + TRANSMUTE_NUM_TO_BYTES, + e.span, + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + |diag| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + diag.span_suggestion( + e.span, + "consider using `to_ne_bytes()`", + format!("{}.to_ne_bytes()", arg.to_string()), + Applicability::Unspecified, + ); + }, + ); + true + }, + _ => false, + } +} diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index bce4c81b78aa1..6a7037d8f3826 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -103,6 +103,33 @@ mod int_to_float { } } +mod num_to_bytes { + fn test() { + unsafe { + let _: [u8; 1] = std::mem::transmute(0u8); + let _: [u8; 4] = std::mem::transmute(0u32); + let _: [u8; 16] = std::mem::transmute(0u128); + let _: [u8; 1] = std::mem::transmute(0i8); + let _: [u8; 4] = std::mem::transmute(0i32); + let _: [u8; 16] = std::mem::transmute(0i128); + let _: [u8; 4] = std::mem::transmute(0.0f32); + let _: [u8; 8] = std::mem::transmute(0.0f64); + } + } + const fn test_const() { + unsafe { + let _: [u8; 1] = std::mem::transmute(0u8); + let _: [u8; 4] = std::mem::transmute(0u32); + let _: [u8; 16] = std::mem::transmute(0u128); + let _: [u8; 1] = std::mem::transmute(0i8); + let _: [u8; 4] = std::mem::transmute(0i32); + let _: [u8; 16] = std::mem::transmute(0i128); + let _: [u8; 4] = std::mem::transmute(0.0f32); + let _: [u8; 8] = std::mem::transmute(0.0f64); + } + } +} + fn bytes_to_str(b: &[u8], mb: &mut [u8]) { let _: &str = unsafe { std::mem::transmute(b) }; let _: &mut str = unsafe { std::mem::transmute(mb) }; diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index e31accb982af3..86537153e3228 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -140,8 +140,94 @@ error: transmute from a `i64` to a `f64` 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 + | +LL | let _: [u8; 1] = std::mem::transmute(0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` + | + = 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 + | +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 + | +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 + | +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 + | +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 + | +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 + | +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 + | +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 + | +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 + | +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 + | +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 + | +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 + | +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 + | +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:107:28 + --> $DIR/transmute.rs:134:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -149,10 +235,10 @@ LL | let _: &str = unsafe { std::mem::transmute(b) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:108:32 + --> $DIR/transmute.rs:135:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 24 previous errors +error: aborting due to 38 previous errors From 09aa4ad6dca75be43ef782884fab36176cb785f0 Mon Sep 17 00:00:00 2001 From: nhamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Wed, 13 Oct 2021 12:12:46 -0700 Subject: [PATCH 037/101] Fix typo in example for `match_result_ok` --- clippy_lints/src/match_result_ok.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index 3db1f0421ea70..ecf6ad316a461 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// } /// /// if let Ok(value) = iter.next() { - /// vec.push_value) + /// vec.push(value) /// } /// ``` pub MATCH_RESULT_OK, From e664a76add1636f6a629b947e884790e526b178f Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 15 Oct 2021 06:20:28 +0200 Subject: [PATCH 038/101] Refactor overlapping arm tests Make the `println!`s match the patterns. Currently they are using the deprecated syntax for inclusive ranges and extra spacing. --- tests/ui/match_overlapping_arm.rs | 48 +++++++++++++-------------- tests/ui/match_overlapping_arm.stderr | 20 +++++------ 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 846d665d1d864..d8a65496b2581 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -10,81 +10,81 @@ fn overlapping() { const FOO: u64 = 2; match 42 { - 0..=10 => println!("0 ... 10"), - 0..=11 => println!("0 ... 11"), + 0..=10 => println!("0..=10"), + 0..=11 => println!("0..=11"), _ => (), } match 42 { - 0..=5 => println!("0 ... 5"), - 6..=7 => println!("6 ... 7"), - FOO..=11 => println!("0 ... 11"), + 0..=5 => println!("0..=5"), + 6..=7 => println!("6..=7"), + FOO..=11 => println!("FOO..=11"), _ => (), } match 42 { 2 => println!("2"), - 0..=5 => println!("0 ... 5"), + 0..=5 => println!("0..=5"), _ => (), } match 42 { 2 => println!("2"), - 0..=2 => println!("0 ... 2"), + 0..=2 => println!("0..=2"), _ => (), } match 42 { - 0..=10 => println!("0 ... 10"), - 11..=50 => println!("11 ... 50"), + 0..=10 => println!("0..=10"), + 11..=50 => println!("11..=50"), _ => (), } match 42 { 2 => println!("2"), - 0..2 => println!("0 .. 2"), + 0..2 => println!("0..2"), _ => (), } match 42 { - 0..10 => println!("0 .. 10"), - 10..50 => println!("10 .. 50"), + 0..10 => println!("0..10"), + 10..50 => println!("10..50"), _ => (), } match 42 { - 0..11 => println!("0 .. 11"), - 0..=11 => println!("0 ... 11"), + 0..11 => println!("0..11"), + 0..=11 => println!("0..=11"), _ => (), } match 42 { - 5..7 => println!("5 .. 7"), - 0..10 => println!("0 .. 10"), + 5..7 => println!("5..7"), + 0..10 => println!("0..10"), _ => (), } match 42 { - 5..10 => println!("5 .. 10"), - 0..=10 => println!("0 ... 10"), + 5..10 => println!("5..10"), + 0..=10 => println!("0..=10"), _ => (), } match 42 { - 0..14 => println!("0 .. 14"), - 5..10 => println!("5 .. 10"), + 0..14 => println!("0..14"), + 5..10 => println!("5..10"), _ => (), } match 42 { - 5..14 => println!("5 .. 14"), - 0..=10 => println!("0 ... 10"), + 5..14 => println!("5..14"), + 0..=10 => println!("0..=10"), _ => (), } match 42 { - 0..7 => println!("0 .. 7"), - 0..=10 => println!("0 ... 10"), + 0..7 => println!("0..7"), + 0..=10 => println!("0..=10"), _ => (), } diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr index 359fa49f51be7..5d3f1637e9410 100644 --- a/tests/ui/match_overlapping_arm.stderr +++ b/tests/ui/match_overlapping_arm.stderr @@ -1,62 +1,62 @@ error: some ranges overlap --> $DIR/match_overlapping_arm.rs:13:9 | -LL | 0..=10 => println!("0 ... 10"), +LL | 0..=10 => println!("0..=10"), | ^^^^^^ | = note: `-D clippy::match-overlapping-arm` implied by `-D warnings` note: overlaps with this --> $DIR/match_overlapping_arm.rs:14:9 | -LL | 0..=11 => println!("0 ... 11"), +LL | 0..=11 => println!("0..=11"), | ^^^^^^ error: some ranges overlap --> $DIR/match_overlapping_arm.rs:19:9 | -LL | 0..=5 => println!("0 ... 5"), +LL | 0..=5 => println!("0..=5"), | ^^^^^ | note: overlaps with this --> $DIR/match_overlapping_arm.rs:21:9 | -LL | FOO..=11 => println!("0 ... 11"), +LL | FOO..=11 => println!("FOO..=11"), | ^^^^^^^^ error: some ranges overlap --> $DIR/match_overlapping_arm.rs:56:9 | -LL | 0..11 => println!("0 .. 11"), +LL | 0..11 => println!("0..11"), | ^^^^^ | note: overlaps with this --> $DIR/match_overlapping_arm.rs:57:9 | -LL | 0..=11 => println!("0 ... 11"), +LL | 0..=11 => println!("0..=11"), | ^^^^^^ error: some ranges overlap --> $DIR/match_overlapping_arm.rs:81:9 | -LL | 0..=10 => println!("0 ... 10"), +LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this --> $DIR/match_overlapping_arm.rs:80:9 | -LL | 5..14 => println!("5 .. 14"), +LL | 5..14 => println!("5..14"), | ^^^^^ error: some ranges overlap --> $DIR/match_overlapping_arm.rs:86:9 | -LL | 0..7 => println!("0 .. 7"), +LL | 0..7 => println!("0..7"), | ^^^^ | note: overlaps with this --> $DIR/match_overlapping_arm.rs:87:9 | -LL | 0..=10 => println!("0 ... 10"), +LL | 0..=10 => println!("0..=10"), | ^^^^^^ error: aborting due to 5 previous errors From 28a249b53e0874e87ba53f491acf11bbddff0789 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 15 Oct 2021 06:25:11 +0200 Subject: [PATCH 039/101] Add unbounded pats to `match_overlapping_arm` tests --- tests/ui/match_overlapping_arm.rs | 11 ++++------- tests/ui/match_overlapping_arm.stderr | 14 +++++++++++++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index d8a65496b2581..ff91c4498ec62 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -88,20 +88,17 @@ fn overlapping() { _ => (), } - /* - // FIXME(JohnTitor): uncomment this once rustfmt knows half-open patterns match 42 { - 0.. => println!("0 .. 42"), - 3.. => println!("3 .. 42"), + 3.. => println!("3.."), + 0.. => println!("0.."), _ => (), } match 42 { - ..=23 => println!("0 ... 23"), - ..26 => println!("0 .. 26"), + ..=23 => println!("..=23"), + ..26 => println!("..26"), _ => (), } - */ if let None = Some(42) { // nothing diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr index 5d3f1637e9410..c2b3f173c2b80 100644 --- a/tests/ui/match_overlapping_arm.stderr +++ b/tests/ui/match_overlapping_arm.stderr @@ -59,5 +59,17 @@ note: overlaps with this LL | 0..=10 => println!("0..=10"), | ^^^^^^ -error: aborting due to 5 previous errors +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:98:9 + | +LL | ..=23 => println!("..=23"), + | ^^^^^ + | +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:99:9 + | +LL | ..26 => println!("..26"), + | ^^^^ + +error: aborting due to 6 previous errors From 0a23fff82d44153c5756ec81fc8fe47f595dc2ee Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 14 Oct 2021 13:28:30 -0500 Subject: [PATCH 040/101] Fix clippy with changed macro statement spans --- clippy_lints/src/copies.rs | 18 ++++---- clippy_lints/src/format.rs | 7 +-- clippy_lints/src/needless_continue.rs | 13 +++--- .../conf_nonstandard_macro_braces.stderr | 6 +-- tests/ui/asm_syntax.stderr | 10 ++--- tests/ui/assertions_on_constants.stderr | 18 ++++---- tests/ui/bool_assert_comparison.stderr | 44 +++++++++---------- .../checked_unwrap/simple_conditionals.stderr | 2 +- tests/ui/collapsible_match2.stderr | 2 +- tests/ui/crashes/ice-6255.stderr | 2 +- .../others.stderr | 2 +- .../traits.stderr | 2 +- tests/ui/default_numeric_fallback_f64.stderr | 2 +- tests/ui/default_numeric_fallback_i32.stderr | 2 +- tests/ui/doc_unsafe.stderr | 2 +- tests/ui/eq_op_macros.stderr | 8 ++-- tests/ui/fallible_impl_from.stderr | 8 ++-- tests/ui/format.stderr | 26 +++++------ tests/ui/implicit_hasher.stderr | 6 +-- tests/ui/item_after_statement.stderr | 2 +- tests/ui/mem_replace_macro.stderr | 2 +- tests/ui/missing_panics_doc.stderr | 4 +- tests/ui/panic_in_result_fn.stderr | 12 ++--- tests/ui/panic_in_result_fn_assertions.stderr | 6 +-- tests/ui/panicking_macros.stderr | 32 +++++++------- tests/ui/pattern_type_mismatch/syntax.stderr | 2 +- tests/ui/toplevel_ref_arg.stderr | 2 +- tests/ui/toplevel_ref_arg_non_rustfix.stderr | 2 +- tests/ui/try_err.stderr | 4 +- tests/ui/unit_cmp.stderr | 8 ++-- 30 files changed, 126 insertions(+), 130 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 6ded2f233efea..b7385dcfbca19 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -9,7 +9,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{Block, Expr, ExprKind, HirId}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{source_map::Span, symbol::Symbol, BytePos}; @@ -432,10 +432,11 @@ fn emit_branches_sharing_code_lint( let mut add_expr_note = false; // Construct suggestions + let sm = cx.sess().source_map(); if start_stmts > 0 { let block = blocks[0]; let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo(); - let span_end = block.stmts[start_stmts - 1].span.source_callsite(); + let span_end = sm.stmt_span(block.stmts[start_stmts - 1].span, block.span); let cond_span = first_line_of_span(cx, if_expr.span).until(block.span); let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None); @@ -454,15 +455,16 @@ fn emit_branches_sharing_code_lint( let span_end = block.span.shrink_to_hi(); let moved_start = if end_stmts == 0 && block.expr.is_some() { - block.expr.unwrap().span + block.expr.unwrap().span.source_callsite() } else { - block.stmts[block.stmts.len() - end_stmts].span - } - .source_callsite(); + sm.stmt_span(block.stmts[block.stmts.len() - end_stmts].span, block.span) + }; let moved_end = block .expr - .map_or_else(|| block.stmts[block.stmts.len() - 1].span, |expr| expr.span) - .source_callsite(); + .map_or_else( + || sm.stmt_span(block.stmts[block.stmts.len() - 1].span, block.span), + |expr| expr.span.source_callsite(), + ); let moved_span = moved_start.to(moved_end); let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None); diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 8df7f91ce59f5..37d9ea3bdc117 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -90,12 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { } } -fn span_useless_format(cx: &LateContext<'_>, span: Span, mut sugg: String, mut applicability: Applicability) { - // The callsite span contains the statement semicolon for some reason. - if snippet_with_applicability(cx, span, "..", &mut applicability).ends_with(';') { - sugg.push(';'); - } - +fn span_useless_format(cx: &LateContext<'_>, span: Span, sugg: String, applicability: Applicability) { span_lint_and_sugg( cx, USELESS_FORMAT, diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 5a50cc48d61bf..7aa93ed783920 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -36,9 +36,8 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{indent_of, snippet, snippet_block}; use rustc_ast::ast; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::{original_sp, DUMMY_SP}; use rustc_span::Span; declare_clippy_lint! { @@ -270,7 +269,7 @@ struct LintData<'a> { /// The 0-based index of the `if` statement in the containing loop block. stmt_idx: usize, /// The statements of the loop block. - block_stmts: &'a [ast::Stmt], + loop_block: &'a ast::Block, } const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant"; @@ -343,10 +342,10 @@ fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: let indent = span_of_first_expr_in_block(data.if_block) .and_then(|span| indent_of(cx, span)) .unwrap_or(0); - let to_annex = data.block_stmts[data.stmt_idx + 1..] + let to_annex = data.loop_block.stmts[data.stmt_idx + 1..] .iter() - .map(|stmt| original_sp(stmt.span, DUMMY_SP)) - .map(|span| { + .map(|stmt| { + let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span); let snip = snippet_block(cx, span, "..", None).into_owned(); snip.lines() .map(|line| format!("{}{}", " ".repeat(indent), line)) @@ -393,7 +392,7 @@ fn check_and_warn<'a>(cx: &EarlyContext<'_>, expr: &'a ast::Expr) { if_cond: cond, if_block: then_block, else_expr, - block_stmts: &loop_block.stmts, + loop_block, }; if needless_continue_in_else(else_expr, label) { emit_warning( diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr index 87e962b9228c4..039b23b1bdb2f 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -82,13 +82,13 @@ error: use of irregular braces for `eprint!` macro --> $DIR/conf_nonstandard_macro_braces.rs:57:5 | LL | eprint!("test if user config overrides defaults"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: consider writing `eprint!["test if user config overrides defaults"];` +help: consider writing `eprint!["test if user config overrides defaults"]` --> $DIR/conf_nonstandard_macro_braces.rs:57:5 | LL | eprint!("test if user config overrides defaults"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 7 previous errors diff --git a/tests/ui/asm_syntax.stderr b/tests/ui/asm_syntax.stderr index e3abbe086586e..409f4db76bc25 100644 --- a/tests/ui/asm_syntax.stderr +++ b/tests/ui/asm_syntax.stderr @@ -2,7 +2,7 @@ error: Intel x86 assembly syntax used --> $DIR/asm_syntax.rs:9:9 | LL | asm!(""); - | ^^^^^^^^^ + | ^^^^^^^^ | = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings` = help: use AT&T x86 assembly syntax @@ -11,7 +11,7 @@ error: Intel x86 assembly syntax used --> $DIR/asm_syntax.rs:10:9 | LL | asm!("", options()); - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ | = help: use AT&T x86 assembly syntax @@ -19,7 +19,7 @@ error: Intel x86 assembly syntax used --> $DIR/asm_syntax.rs:11:9 | LL | asm!("", options(nostack)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use AT&T x86 assembly syntax @@ -27,7 +27,7 @@ error: AT&T x86 assembly syntax used --> $DIR/asm_syntax.rs:23:9 | LL | asm!("", options(att_syntax)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings` = help: use Intel x86 assembly syntax @@ -36,7 +36,7 @@ error: AT&T x86 assembly syntax used --> $DIR/asm_syntax.rs:24:9 | LL | asm!("", options(nostack, att_syntax)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use Intel x86 assembly syntax diff --git a/tests/ui/assertions_on_constants.stderr b/tests/ui/assertions_on_constants.stderr index 1eb87d89fad02..4ca1e6f6e88cc 100644 --- a/tests/ui/assertions_on_constants.stderr +++ b/tests/ui/assertions_on_constants.stderr @@ -2,7 +2,7 @@ error: `assert!(true)` will be optimized out by the compiler --> $DIR/assertions_on_constants.rs:11:5 | LL | assert!(true); - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ | = note: `-D clippy::assertions-on-constants` implied by `-D warnings` = help: remove it @@ -12,7 +12,7 @@ error: `assert!(false)` should probably be replaced --> $DIR/assertions_on_constants.rs:12:5 | LL | assert!(false); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ | = help: use `panic!()` or `unreachable!()` = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -21,7 +21,7 @@ error: `assert!(true)` will be optimized out by the compiler --> $DIR/assertions_on_constants.rs:13:5 | LL | assert!(true, "true message"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: remove it = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -30,7 +30,7 @@ error: `assert!(false, "false message")` should probably be replaced --> $DIR/assertions_on_constants.rs:14:5 | LL | assert!(false, "false message"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `panic!("false message")` or `unreachable!("false message")` = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -39,7 +39,7 @@ error: `assert!(false, msg.to_uppercase())` should probably be replaced --> $DIR/assertions_on_constants.rs:17:5 | LL | assert!(false, msg.to_uppercase()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `panic!(msg.to_uppercase())` or `unreachable!(msg.to_uppercase())` = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -48,7 +48,7 @@ error: `assert!(true)` will be optimized out by the compiler --> $DIR/assertions_on_constants.rs:20:5 | LL | assert!(B); - | ^^^^^^^^^^^ + | ^^^^^^^^^^ | = help: remove it = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -57,7 +57,7 @@ error: `assert!(false)` should probably be replaced --> $DIR/assertions_on_constants.rs:23:5 | LL | assert!(C); - | ^^^^^^^^^^^ + | ^^^^^^^^^^ | = help: use `panic!()` or `unreachable!()` = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -66,7 +66,7 @@ error: `assert!(false, "C message")` should probably be replaced --> $DIR/assertions_on_constants.rs:24:5 | LL | assert!(C, "C message"); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `panic!("C message")` or `unreachable!("C message")` = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -75,7 +75,7 @@ error: `debug_assert!(true)` will be optimized out by the compiler --> $DIR/assertions_on_constants.rs:26:5 | LL | debug_assert!(true); - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ | = help: remove it = note: this error originates in the macro `$crate::assert` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/bool_assert_comparison.stderr b/tests/ui/bool_assert_comparison.stderr index da9b56aa7795d..377d51be4cde7 100644 --- a/tests/ui/bool_assert_comparison.stderr +++ b/tests/ui/bool_assert_comparison.stderr @@ -2,7 +2,7 @@ error: used `assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:69:5 | LL | assert_eq!("a".is_empty(), false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` | = note: `-D clippy::bool-assert-comparison` implied by `-D warnings` @@ -10,127 +10,127 @@ error: used `assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:70:5 | LL | assert_eq!("".is_empty(), true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:71:5 | LL | assert_eq!(true, "".is_empty()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:76:5 | LL | assert_eq!(b, true); - | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_ne!` with a literal bool --> $DIR/bool_assert_comparison.rs:79:5 | LL | assert_ne!("a".is_empty(), false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_ne!` with a literal bool --> $DIR/bool_assert_comparison.rs:80:5 | LL | assert_ne!("".is_empty(), true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_ne!` with a literal bool --> $DIR/bool_assert_comparison.rs:81:5 | LL | assert_ne!(true, "".is_empty()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_ne!` with a literal bool --> $DIR/bool_assert_comparison.rs:86:5 | LL | assert_ne!(b, true); - | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `debug_assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:89:5 | LL | debug_assert_eq!("a".is_empty(), false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:90:5 | LL | debug_assert_eq!("".is_empty(), true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:91:5 | LL | debug_assert_eq!(true, "".is_empty()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:96:5 | LL | debug_assert_eq!(b, true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_ne!` with a literal bool --> $DIR/bool_assert_comparison.rs:99:5 | LL | debug_assert_ne!("a".is_empty(), false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_ne!` with a literal bool --> $DIR/bool_assert_comparison.rs:100:5 | LL | debug_assert_ne!("".is_empty(), true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_ne!` with a literal bool --> $DIR/bool_assert_comparison.rs:101:5 | LL | debug_assert_ne!(true, "".is_empty()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_ne!` with a literal bool --> $DIR/bool_assert_comparison.rs:106:5 | LL | debug_assert_ne!(b, true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:111:5 | LL | assert_eq!("a".is_empty(), false, "tadam {}", 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:112:5 | LL | assert_eq!("a".is_empty(), false, "tadam {}", true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:113:5 | LL | assert_eq!(false, "a".is_empty(), "tadam {}", true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `debug_assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:118:5 | LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:119:5 | LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool --> $DIR/bool_assert_comparison.rs:120:5 | LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: aborting due to 22 previous errors diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index 82f269543800f..3413159280217 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -71,7 +71,7 @@ LL | $a.unwrap(); // unnecessary | ^^^^^^^^^^^ ... LL | m!(x); - | ------ in this macro invocation + | ----- in this macro invocation | = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index 55e70dce208a0..46b645aea135c 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -46,7 +46,7 @@ LL | | }, | |_____________________^ ... LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); - | ------------------------------------------------- in this macro invocation + | ------------------------------------------------ in this macro invocation | help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:46:28 diff --git a/tests/ui/crashes/ice-6255.stderr b/tests/ui/crashes/ice-6255.stderr index 5dbf9d440dd75..db0cb25e34a0f 100644 --- a/tests/ui/crashes/ice-6255.stderr +++ b/tests/ui/crashes/ice-6255.stderr @@ -5,7 +5,7 @@ LL | extern crate std as core; | ^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | define_other_core!(); - | --------------------- in this macro invocation + | -------------------- in this macro invocation | = note: this error originates in the macro `define_other_core` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/declare_interior_mutable_const/others.stderr b/tests/ui/declare_interior_mutable_const/others.stderr index 7c9d705fa9895..fd0689dfc4c99 100644 --- a/tests/ui/declare_interior_mutable_const/others.stderr +++ b/tests/ui/declare_interior_mutable_const/others.stderr @@ -31,7 +31,7 @@ LL | const $name: $ty = $e; | ^^^^^^^^^^^^^^^^^^^^^^ ... LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable - | ------------------------------------------ in this macro invocation + | ----------------------------------------- in this macro invocation | = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/declare_interior_mutable_const/traits.stderr b/tests/ui/declare_interior_mutable_const/traits.stderr index bed385b5273a9..7debe059ff4ee 100644 --- a/tests/ui/declare_interior_mutable_const/traits.stderr +++ b/tests/ui/declare_interior_mutable_const/traits.stderr @@ -13,7 +13,7 @@ LL | const $name: $ty = $e; | ^^^^^^^^^^^^^^^^^^^^^^ ... LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable - | ----------------------------------------------------------- in this macro invocation + | ---------------------------------------------------------- in this macro invocation | = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/default_numeric_fallback_f64.stderr b/tests/ui/default_numeric_fallback_f64.stderr index 961c7cb57c523..f8a2407b6933d 100644 --- a/tests/ui/default_numeric_fallback_f64.stderr +++ b/tests/ui/default_numeric_fallback_f64.stderr @@ -139,7 +139,7 @@ LL | let x = 22.; | ^^^ help: consider adding suffix: `22.0_f64` ... LL | internal_macro!(); - | ------------------ in this macro invocation + | ----------------- in this macro invocation | = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr index 5edf48b202087..6f9e124704b2c 100644 --- a/tests/ui/default_numeric_fallback_i32.stderr +++ b/tests/ui/default_numeric_fallback_i32.stderr @@ -151,7 +151,7 @@ LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` ... LL | internal_macro!(); - | ------------------ in this macro invocation + | ----------------- in this macro invocation | = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/doc_unsafe.stderr b/tests/ui/doc_unsafe.stderr index 34ca37a6efdc0..d68b8a0c67be6 100644 --- a/tests/ui/doc_unsafe.stderr +++ b/tests/ui/doc_unsafe.stderr @@ -47,7 +47,7 @@ LL | | } | |_________^ ... LL | very_unsafe!(); - | --------------- in this macro invocation + | -------------- in this macro invocation | = note: this error originates in the macro `very_unsafe` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/eq_op_macros.stderr b/tests/ui/eq_op_macros.stderr index a28961e7568ed..885415b42c787 100644 --- a/tests/ui/eq_op_macros.stderr +++ b/tests/ui/eq_op_macros.stderr @@ -5,7 +5,7 @@ LL | assert_eq!(a, a); | ^^^^ ... LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation + | ---------------------- in this macro invocation | = note: `-D clippy::eq-op` implied by `-D warnings` = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -17,7 +17,7 @@ LL | assert_ne!(a, a); | ^^^^ ... LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation + | ---------------------- in this macro invocation | = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -52,7 +52,7 @@ LL | debug_assert_eq!(a, a); | ^^^^ ... LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation + | ---------------------- in this macro invocation | = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -63,7 +63,7 @@ LL | debug_assert_ne!(a, a); | ^^^^ ... LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation + | ---------------------- in this macro invocation | = note: this error originates in the macro `assert_in_macro_def` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/fallible_impl_from.stderr b/tests/ui/fallible_impl_from.stderr index 8b8054586e690..f5d0b98c10862 100644 --- a/tests/ui/fallible_impl_from.stderr +++ b/tests/ui/fallible_impl_from.stderr @@ -37,7 +37,7 @@ note: potential failure(s) --> $DIR/fallible_impl_from.rs:30:13 | LL | panic!(); - | ^^^^^^^^^ + | ^^^^^^^^ = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error: consider implementing `TryFrom` instead @@ -60,11 +60,11 @@ LL | let s = s.unwrap(); | ^^^^^^^^^^ LL | if !s.is_empty() { LL | panic!("42"); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ LL | } else if s.parse::().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^ LL | panic!("{:?}", s); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error: consider implementing `TryFrom` instead @@ -86,7 +86,7 @@ note: potential failure(s) LL | if s.parse::().ok().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | panic!("{:?}", s); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index 496a083497dfa..701399b32d628 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -2,7 +2,7 @@ error: useless use of `format!` --> $DIR/format.rs:13:5 | LL | format!("foo"); - | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();` + | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` | = note: `-D clippy::useless-format` implied by `-D warnings` @@ -10,13 +10,13 @@ error: useless use of `format!` --> $DIR/format.rs:14:5 | LL | format!("{{}}"); - | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string();` + | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()` error: useless use of `format!` --> $DIR/format.rs:15:5 | LL | format!("{{}} abc {{}}"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string();` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()` error: useless use of `format!` --> $DIR/format.rs:16:5 @@ -25,61 +25,61 @@ LL | / format!( LL | | r##"foo {{}} LL | | " bar"## LL | | ); - | |______^ + | |_____^ | help: consider using `.to_string()` | LL ~ r##"foo {} -LL + " bar"##.to_string(); +LL ~ " bar"##.to_string(); | error: useless use of `format!` --> $DIR/format.rs:21:5 | LL | format!("{}", "foo"); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` --> $DIR/format.rs:25:5 | LL | format!("{:+}", "foo"); // Warn when the format makes no difference. - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` --> $DIR/format.rs:26:5 | LL | format!("{:<}", "foo"); // Warn when the format makes no difference. - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` --> $DIR/format.rs:31:5 | LL | format!("{}", arg); - | ^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();` + | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` --> $DIR/format.rs:35:5 | LL | format!("{:+}", arg); // Warn when the format makes no difference. - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` --> $DIR/format.rs:36:5 | LL | format!("{:<}", arg); // Warn when the format makes no difference. - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` --> $DIR/format.rs:63:5 | LL | format!("{}", 42.to_string()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()` error: useless use of `format!` --> $DIR/format.rs:65:5 | LL | format!("{}", x.display().to_string()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()` error: useless use of `format!` --> $DIR/format.rs:69:18 diff --git a/tests/ui/implicit_hasher.stderr b/tests/ui/implicit_hasher.stderr index dad5ab71f157f..3f5f56b923fe2 100644 --- a/tests/ui/implicit_hasher.stderr +++ b/tests/ui/implicit_hasher.stderr @@ -107,7 +107,7 @@ LL | impl Foo for HashMap { | ^^^^^^^^^^^^^ ... LL | gen!(impl); - | ----------- in this macro invocation + | ---------- in this macro invocation | = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider adding a type parameter @@ -126,7 +126,7 @@ LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) | ^^^^^^^^^^^^^^^^^ ... LL | gen!(fn bar); - | ------------- in this macro invocation + | ------------ in this macro invocation | = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider adding a type parameter @@ -141,7 +141,7 @@ LL | pub fn $name(_map: &mut HashMap, _set: &mut HashSet) | ^^^^^^^^^^^^ ... LL | gen!(fn bar); - | ------------- in this macro invocation + | ------------ in this macro invocation | = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider adding a type parameter diff --git a/tests/ui/item_after_statement.stderr b/tests/ui/item_after_statement.stderr index bcb163d4bc124..ab4a6374c73ca 100644 --- a/tests/ui/item_after_statement.stderr +++ b/tests/ui/item_after_statement.stderr @@ -25,7 +25,7 @@ LL | | } | |_____________^ ... LL | b!(); - | ----- in this macro invocation + | ---- in this macro invocation | = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/mem_replace_macro.stderr b/tests/ui/mem_replace_macro.stderr index b4963acc4553c..dd69ab8b5efb8 100644 --- a/tests/ui/mem_replace_macro.stderr +++ b/tests/ui/mem_replace_macro.stderr @@ -5,7 +5,7 @@ LL | std::mem::replace($s, Default::default()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | take!(s); - | --------- in this macro invocation + | -------- in this macro invocation | = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` = note: this error originates in the macro `take` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr index 8d882cc6e0d07..b863063b626db 100644 --- a/tests/ui/missing_panics_doc.stderr +++ b/tests/ui/missing_panics_doc.stderr @@ -91,7 +91,7 @@ note: first possible panic found here --> $DIR/missing_panics_doc.rs:39:5 | LL | assert_eq!(x, 0); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for function which may panic missing `# Panics` section @@ -107,7 +107,7 @@ note: first possible panic found here --> $DIR/missing_panics_doc.rs:45:5 | LL | assert_ne!(x, 0); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 7 previous errors diff --git a/tests/ui/panic_in_result_fn.stderr b/tests/ui/panic_in_result_fn.stderr index 8d6e40c30a109..f56c2d03c664f 100644 --- a/tests/ui/panic_in_result_fn.stderr +++ b/tests/ui/panic_in_result_fn.stderr @@ -13,7 +13,7 @@ note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:9:9 | LL | panic!("error"); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` @@ -30,7 +30,7 @@ note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:14:9 | LL | unimplemented!(); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` @@ -47,7 +47,7 @@ note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:19:9 | LL | unreachable!(); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` @@ -64,7 +64,7 @@ note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:24:9 | LL | todo!("Finish this"); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` @@ -81,7 +81,7 @@ note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:55:5 | LL | panic!("error"); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` @@ -98,7 +98,7 @@ note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:69:5 | LL | todo!("finish main method"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors diff --git a/tests/ui/panic_in_result_fn_assertions.stderr b/tests/ui/panic_in_result_fn_assertions.stderr index 4c39b37d8798f..7501d6d85edd7 100644 --- a/tests/ui/panic_in_result_fn_assertions.stderr +++ b/tests/ui/panic_in_result_fn_assertions.stderr @@ -14,7 +14,7 @@ note: return Err() instead of panicking --> $DIR/panic_in_result_fn_assertions.rs:9:9 | LL | assert!(x == 5, "wrong argument"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` @@ -32,7 +32,7 @@ note: return Err() instead of panicking --> $DIR/panic_in_result_fn_assertions.rs:15:9 | LL | assert_eq!(x, 5); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` @@ -50,7 +50,7 @@ note: return Err() instead of panicking --> $DIR/panic_in_result_fn_assertions.rs:21:9 | LL | assert_ne!(x, 1); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr index 2e83c305a67e0..2b607ff588895 100644 --- a/tests/ui/panicking_macros.stderr +++ b/tests/ui/panicking_macros.stderr @@ -2,7 +2,7 @@ error: `panic` should not be present in production code --> $DIR/panicking_macros.rs:8:5 | LL | panic!(); - | ^^^^^^^^^ + | ^^^^^^^^ | = note: `-D clippy::panic` implied by `-D warnings` @@ -10,19 +10,19 @@ error: `panic` should not be present in production code --> $DIR/panicking_macros.rs:9:5 | LL | panic!("message"); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ error: `panic` should not be present in production code --> $DIR/panicking_macros.rs:10:5 | LL | panic!("{} {}", "panic with", "multiple arguments"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `todo` should not be present in production code --> $DIR/panicking_macros.rs:16:5 | LL | todo!(); - | ^^^^^^^^ + | ^^^^^^^ | = note: `-D clippy::todo` implied by `-D warnings` = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -31,7 +31,7 @@ error: `todo` should not be present in production code --> $DIR/panicking_macros.rs:17:5 | LL | todo!("message"); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -39,7 +39,7 @@ error: `todo` should not be present in production code --> $DIR/panicking_macros.rs:18:5 | LL | todo!("{} {}", "panic with", "multiple arguments"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -47,7 +47,7 @@ error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:24:5 | LL | unimplemented!(); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ | = note: `-D clippy::unimplemented` implied by `-D warnings` = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -56,7 +56,7 @@ error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:25:5 | LL | unimplemented!("message"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -64,7 +64,7 @@ error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:26:5 | LL | unimplemented!("{} {}", "panic with", "multiple arguments"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -72,7 +72,7 @@ error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:32:5 | LL | unreachable!(); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ | = note: `-D clippy::unreachable` implied by `-D warnings` = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -81,7 +81,7 @@ error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:33:5 | LL | unreachable!("message"); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `$crate::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -89,7 +89,7 @@ error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:34:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -97,13 +97,13 @@ error: `panic` should not be present in production code --> $DIR/panicking_macros.rs:40:5 | LL | panic!(); - | ^^^^^^^^^ + | ^^^^^^^^ error: `todo` should not be present in production code --> $DIR/panicking_macros.rs:41:5 | LL | todo!(); - | ^^^^^^^^ + | ^^^^^^^ | = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -111,7 +111,7 @@ error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:42:5 | LL | unimplemented!(); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -119,7 +119,7 @@ error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:43:5 | LL | unreachable!(); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ | = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/pattern_type_mismatch/syntax.stderr b/tests/ui/pattern_type_mismatch/syntax.stderr index f309b2739829f..12b3d3a8bd075 100644 --- a/tests/ui/pattern_type_mismatch/syntax.stderr +++ b/tests/ui/pattern_type_mismatch/syntax.stderr @@ -70,7 +70,7 @@ LL | Some(_) => (), | ^^^^^^^ ... LL | matching_macro!(value); - | ----------------------- in this macro invocation + | ---------------------- in this macro invocation | = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings = note: this error originates in the macro `matching_macro` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/toplevel_ref_arg.stderr b/tests/ui/toplevel_ref_arg.stderr index 48e7d9ddd5aee..9c853020ab019 100644 --- a/tests/ui/toplevel_ref_arg.stderr +++ b/tests/ui/toplevel_ref_arg.stderr @@ -37,7 +37,7 @@ LL | let ref _y = 42; | ----^^^^^^------ help: try: `let _y = &42;` ... LL | gen_binding!(); - | --------------- in this macro invocation + | -------------- in this macro invocation | = note: this error originates in the macro `gen_binding` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/tests/ui/toplevel_ref_arg_non_rustfix.stderr index 31f8c103ede57..e97011c7fd51f 100644 --- a/tests/ui/toplevel_ref_arg_non_rustfix.stderr +++ b/tests/ui/toplevel_ref_arg_non_rustfix.stderr @@ -13,7 +13,7 @@ LL | fn fun_example(ref _x: usize) {} | ^^^^^^ ... LL | gen_function!(); - | ---------------- in this macro invocation + | --------------- in this macro invocation | = note: this error originates in the macro `gen_function` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 09efc16c154ef..0cb1328fbfcf0 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -35,7 +35,7 @@ LL | Err(_) => Err(1)?, | ^^^^^^^ help: try this: `return Err(1)` ... LL | try_validation!(Ok::<_, i32>(5)); - | --------------------------------- in this macro invocation + | -------------------------------- in this macro invocation | = note: this error originates in the macro `try_validation` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -46,7 +46,7 @@ LL | Err(_) => Err(ret_one!())?, | ^^^^^^^^^^^^^^^^ help: try this: `return Err(ret_one!())` ... LL | try_validation_in_macro!(Ok::<_, i32>(5)); - | ------------------------------------------ in this macro invocation + | ----------------------------------------- in this macro invocation | = note: this error originates in the macro `try_validation_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/unit_cmp.stderr b/tests/ui/unit_cmp.stderr index 75017cab05776..2b5a7b348b982 100644 --- a/tests/ui/unit_cmp.stderr +++ b/tests/ui/unit_cmp.stderr @@ -32,7 +32,7 @@ LL | | }, ... | LL | | } LL | | ); - | |______^ + | |_____^ | = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -46,7 +46,7 @@ LL | | }, ... | LL | | } LL | | ); - | |______^ + | |_____^ | = note: this error originates in the macro `$crate::assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -60,7 +60,7 @@ LL | | }, ... | LL | | } LL | | ); - | |______^ + | |_____^ | = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -74,7 +74,7 @@ LL | | }, ... | LL | | } LL | | ); - | |______^ + | |_____^ | = note: this error originates in the macro `$crate::assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) From ec5071931eb68a981d14eefd7f6c39bb16c59596 Mon Sep 17 00:00:00 2001 From: dswij Date: Fri, 15 Oct 2021 18:00:02 +0800 Subject: [PATCH 041/101] `unnecessary_sort_by` only warns if argument impl `Ord` trait --- clippy_lints/src/unnecessary_sort_by.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index dd74bf367f3a5..26b56e0f2f316 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; @@ -193,10 +193,15 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; + if_chain! { if let ExprKind::Path(QPath::Resolved(_, Path { segments: [PathSegment { ident: left_name, .. }], .. - })) = &left_expr.kind { - if left_name == left_ident { + })) = &left_expr.kind; + if left_name == left_ident; + if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| { + implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]) + }); + then { return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })); } } From e4ac4c2e1a162eedd2f5ec1737fc3260a97268a2 Mon Sep 17 00:00:00 2001 From: dswij Date: Fri, 15 Oct 2021 18:06:02 +0800 Subject: [PATCH 042/101] Add test on `unnecessary_sort_by` when argument does not implement `Ord` --- tests/ui/unnecessary_sort_by.fixed | 5 +++++ tests/ui/unnecessary_sort_by.rs | 5 +++++ tests/ui/unnecessary_sort_by.stderr | 24 ++++++++++++------------ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index b45b27d8f23b1..d806d620b176a 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -2,6 +2,7 @@ #![allow(clippy::stable_sort_primitive)] +use std::cell::Ref; use std::cmp::Reverse; fn unnecessary_sort_by() { @@ -33,6 +34,10 @@ fn unnecessary_sort_by() { // `Reverse(b)` would borrow in the following cases, don't lint vec.sort_by(|a, b| b.cmp(a)); vec.sort_unstable_by(|a, b| b.cmp(a)); + + // No warning if element does not implement `Ord` + let mut vec: Vec> = Vec::new(); + vec.sort_unstable_by(|a, b| a.cmp(b)); } // Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index be2abe7f7014d..6ee9c3af455df 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -2,6 +2,7 @@ #![allow(clippy::stable_sort_primitive)] +use std::cell::Ref; use std::cmp::Reverse; fn unnecessary_sort_by() { @@ -33,6 +34,10 @@ fn unnecessary_sort_by() { // `Reverse(b)` would borrow in the following cases, don't lint vec.sort_by(|a, b| b.cmp(a)); vec.sort_unstable_by(|a, b| b.cmp(a)); + + // No warning if element does not implement `Ord` + let mut vec: Vec> = Vec::new(); + vec.sort_unstable_by(|a, b| a.cmp(b)); } // Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 50607933e18f7..ca9641e880316 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -1,5 +1,5 @@ error: use Vec::sort here instead - --> $DIR/unnecessary_sort_by.rs:14:5 + --> $DIR/unnecessary_sort_by.rs:15:5 | LL | vec.sort_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` @@ -7,67 +7,67 @@ LL | vec.sort_by(|a, b| a.cmp(b)); = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` error: use Vec::sort here instead - --> $DIR/unnecessary_sort_by.rs:15:5 + --> $DIR/unnecessary_sort_by.rs:16:5 | LL | vec.sort_unstable_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:16:5 + --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:17:5 + --> $DIR/unnecessary_sort_by.rs:18:5 | LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| id(-a))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:20:5 + --> $DIR/unnecessary_sort_by.rs:21:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:21:5 + --> $DIR/unnecessary_sort_by.rs:22:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|b| Reverse(id(-b)))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:31:5 + --> $DIR/unnecessary_sort_by.rs:32:5 | LL | vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (***a).abs())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:32:5 + --> $DIR/unnecessary_sort_by.rs:33:5 | LL | vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| (***a).abs())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:88:9 + --> $DIR/unnecessary_sort_by.rs:93:9 | LL | args.sort_by(|a, b| a.name().cmp(&b.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|a| a.name())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:89:9 + --> $DIR/unnecessary_sort_by.rs:94:9 | LL | args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|a| a.name())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:91:9 + --> $DIR/unnecessary_sort_by.rs:96:9 | LL | args.sort_by(|a, b| b.name().cmp(&a.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|b| Reverse(b.name()))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:92:9 + --> $DIR/unnecessary_sort_by.rs:97:9 | LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|b| Reverse(b.name()))` From c9599d79a3b741b7c42353dc45eab6f1c461891f Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Thu, 30 Sep 2021 07:45:03 -0400 Subject: [PATCH 043/101] Add `format_in_format_args` and `to_string_in_format_args` lints Fixes #7667 and #7729 --- CHANGELOG.md | 2 + clippy_lints/src/format.rs | 51 +----- clippy_lints/src/format_args.rs | 223 +++++++++++++++++++++++++ clippy_lints/src/lib.register_all.rs | 2 + clippy_lints/src/lib.register_lints.rs | 2 + clippy_lints/src/lib.register_perf.rs | 2 + clippy_lints/src/lib.rs | 2 + clippy_utils/src/higher.rs | 102 ++++++++++- clippy_utils/src/paths.rs | 20 +++ tests/ui/format_args.fixed | 105 ++++++++++++ tests/ui/format_args.rs | 105 ++++++++++++ tests/ui/format_args.stderr | 106 ++++++++++++ tests/ui/format_args_unfixable.rs | 60 +++++++ tests/ui/format_args_unfixable.stderr | 175 +++++++++++++++++++ 14 files changed, 908 insertions(+), 49 deletions(-) create mode 100644 clippy_lints/src/format_args.rs create mode 100644 tests/ui/format_args.fixed create mode 100644 tests/ui/format_args.rs create mode 100644 tests/ui/format_args.stderr create mode 100644 tests/ui/format_args_unfixable.rs create mode 100644 tests/ui/format_args_unfixable.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index b1e837d363a26..f6a883311c91b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2736,6 +2736,7 @@ Released 2018-09-13 [`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_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 [`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 @@ -3015,6 +3016,7 @@ Released 2018-09-13 [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some [`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display +[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 8df7f91ce59f5..472db6f5731db 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::FormatExpn; -use clippy_utils::last_path_segment; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -69,8 +68,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { ty::Str => true, _ => false, }; - if format_args.args.iter().all(is_display_arg); - if format_args.fmt_expr.map_or(true, check_unformatted); + if let Some(args) = format_args.args(); + if args.iter().all(|arg| arg.is_display() && !arg.has_string_formatting()); then { let is_new_string = match value.kind { ExprKind::Binary(..) => true, @@ -106,47 +105,3 @@ fn span_useless_format(cx: &LateContext<'_>, span: Span, mut sugg: String, mut a applicability, ); } - -fn is_display_arg(expr: &Expr<'_>) -> bool { - if_chain! { - if let ExprKind::Call(_, [_, fmt]) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = fmt.kind; - if let [.., t, _] = path.segments; - if t.ident.name == sym::Display; - then { true } else { false } - } -} - -/// Checks if the expression matches -/// ```rust,ignore -/// &[_ { -/// format: _ { -/// width: _::Implied, -/// precision: _::Implied, -/// ... -/// }, -/// ..., -/// }] -/// ``` -fn check_unformatted(expr: &Expr<'_>) -> bool { - if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; - if let ExprKind::Array([expr]) = expr.kind; - // struct `core::fmt::rt::v1::Argument` - if let ExprKind::Struct(_, fields, _) = expr.kind; - if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format); - // struct `core::fmt::rt::v1::FormatSpec` - if let ExprKind::Struct(_, fields, _) = format_field.expr.kind; - if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym::precision); - if let ExprKind::Path(ref precision_path) = precision_field.expr.kind; - if last_path_segment(precision_path).ident.name == sym::Implied; - if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym::width); - if let ExprKind::Path(ref width_qpath) = width_field.expr.kind; - if last_path_segment(width_qpath).ident.name == sym::Implied; - then { - return true; - } - } - - false -} diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs new file mode 100644 index 0000000000000..8b27442aa9465 --- /dev/null +++ b/clippy_lints/src/format_args.rs @@ -0,0 +1,223 @@ +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::higher::{FormatArgsArg, FormatArgsExpn, FormatExpn}; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::implements_trait; +use clippy_utils::{is_diag_trait_item, match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::Ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, BytePos, ExpnData, ExpnKind, Span, Symbol}; + +declare_clippy_lint! { + /// ### What it does + /// Detects `format!` within the arguments of another macro that does + /// formatting such as `format!` itself, `write!` or `println!`. Suggests + /// inlining the `format!` call. + /// + /// ### Why is this bad? + /// The recommended code is both shorter and avoids a temporary allocation. + /// + /// ### Example + /// ```rust + /// # use std::panic::Location; + /// println!("error: {}", format!("something failed at {}", Location::caller())); + /// ``` + /// Use instead: + /// ```rust + /// # use std::panic::Location; + /// println!("error: something failed at {}", Location::caller()); + /// ``` + pub FORMAT_IN_FORMAT_ARGS, + perf, + "`format!` used in a macro that does formatting" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for [`ToString::to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string) + /// applied to a type that implements [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html) + /// in a macro that does formatting. + /// + /// ### Why is this bad? + /// Since the type implements `Display`, the use of `to_string` is + /// unnecessary. + /// + /// ### Example + /// ```rust + /// # use std::panic::Location; + /// println!("error: something failed at {}", Location::caller().to_string()); + /// ``` + /// Use instead: + /// ```rust + /// # use std::panic::Location; + /// println!("error: something failed at {}", Location::caller()); + /// ``` + pub TO_STRING_IN_FORMAT_ARGS, + perf, + "`to_string` applied to a type that implements `Display` in format args" +} + +declare_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); + +const FORMAT_MACRO_PATHS: &[&[&str]] = &[ + &paths::FORMAT_ARGS_MACRO, + &paths::ASSERT_EQ_MACRO, + &paths::ASSERT_MACRO, + &paths::ASSERT_NE_MACRO, + &paths::EPRINT_MACRO, + &paths::EPRINTLN_MACRO, + &paths::PRINT_MACRO, + &paths::PRINTLN_MACRO, + &paths::WRITE_MACRO, + &paths::WRITELN_MACRO, +]; + +const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[sym::format_macro, sym::std_panic_macro]; + +impl<'tcx> LateLintPass<'tcx> for FormatArgs { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if_chain! { + if let Some(format_args) = FormatArgsExpn::parse(expr); + let expr_expn_data = expr.span.ctxt().outer_expn_data(); + let outermost_expn_data = outermost_expn_data(expr_expn_data); + if let Some(macro_def_id) = outermost_expn_data.macro_def_id; + if FORMAT_MACRO_PATHS + .iter() + .any(|path| match_def_path(cx, macro_def_id, path)) + || FORMAT_MACRO_DIAG_ITEMS + .iter() + .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, macro_def_id)); + if let ExpnKind::Macro(_, name) = outermost_expn_data.kind; + if let Some(args) = format_args.args(); + then { + for (i, arg) in args.iter().enumerate() { + if !arg.is_display() { + continue; + } + if arg.has_string_formatting() { + continue; + } + if is_aliased(&args, i) { + continue; + } + check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg); + check_to_string_in_format_args(cx, name, arg); + } + } + } + } +} + +fn outermost_expn_data(expn_data: ExpnData) -> ExpnData { + if expn_data.call_site.from_expansion() { + outermost_expn_data(expn_data.call_site.ctxt().outer_expn_data()) + } else { + expn_data + } +} + +fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &FormatArgsArg<'_>) { + if_chain! { + if FormatExpn::parse(arg.value).is_some(); + if !arg.value.span.ctxt().outer_expn_data().call_site.from_expansion(); + then { + span_lint_and_then( + cx, + FORMAT_IN_FORMAT_ARGS, + trim_semicolon(cx, call_site), + &format!("`format!` in `{}!` args", name), + |diag| { + diag.help(&format!( + "combine the `format!(..)` arguments with the outer `{}!(..)` call", + name + )); + diag.help("or consider changing `format!` to `format_args!`"); + }, + ); + } + } +} + +fn check_to_string_in_format_args<'tcx>(cx: &LateContext<'tcx>, name: Symbol, arg: &FormatArgsArg<'tcx>) { + let value = arg.value; + if_chain! { + if !value.span.from_expansion(); + if let ExprKind::MethodCall(_, _, [receiver], _) = value.kind; + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id); + if is_diag_trait_item(cx, method_def_id, sym::ToString); + let receiver_ty = cx.typeck_results().expr_ty(receiver); + if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display); + if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); + then { + let (n_needed_derefs, target) = count_needed_derefs( + receiver_ty, + cx.typeck_results().expr_adjustments(receiver).iter(), + ); + if implements_trait(cx, target, display_trait_id, &[]) { + if n_needed_derefs == 0 { + span_lint_and_sugg( + cx, + TO_STRING_IN_FORMAT_ARGS, + value.span.with_lo(receiver.span.hi()), + &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name), + "remove this", + String::new(), + Applicability::MachineApplicable, + ); + } else { + span_lint_and_sugg( + cx, + TO_STRING_IN_FORMAT_ARGS, + value.span, + &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name), + "use this", + format!("{:*>width$}{}", "", receiver_snippet, width = n_needed_derefs), + Applicability::MachineApplicable, + ); + } + } + } + } +} + +// Returns true if `args[i]` "refers to" or "is referred to by" another argument. +fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool { + let value = args[i].value; + args.iter() + .enumerate() + .any(|(j, arg)| i != j && std::ptr::eq(value, arg.value)) +} + +fn trim_semicolon(cx: &LateContext<'_>, span: Span) -> Span { + snippet_opt(cx, span).map_or(span, |snippet| { + let snippet = snippet.trim_end_matches(';'); + span.with_hi(span.lo() + BytePos(u32::try_from(snippet.len()).unwrap())) + }) +} + +fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>) +where + I: Iterator>, +{ + let mut n_total = 0; + let mut n_needed = 0; + loop { + if let Some(Adjustment { + kind: Adjust::Deref(overloaded_deref), + target, + }) = iter.next() + { + n_total += 1; + if overloaded_deref.is_some() { + n_needed = n_total; + } + ty = target; + } else { + return (n_needed, ty); + } + } +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 7c8733f902257..4c2dfbd1d8443 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -60,6 +60,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(float_literal::EXCESSIVE_PRECISION), LintId::of(format::USELESS_FORMAT), + LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), + LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), 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 7d81fafe4c93d..f334f723f6b87 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -139,6 +139,8 @@ store.register_lints(&[ floating_point_arithmetic::IMPRECISE_FLOPS, floating_point_arithmetic::SUBOPTIMAL_FLOPS, format::USELESS_FORMAT, + format_args::FORMAT_IN_FORMAT_ARGS, + format_args::TO_STRING_IN_FORMAT_ARGS, 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 5432345760bc3..a0d5cf9418e0b 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/clippy_lints/src/lib.register_perf.rs @@ -5,6 +5,8 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(entry::MAP_ENTRY), LintId::of(escape::BOXED_LOCAL), + LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), + LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), 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 6ab59b1b12fe9..9794d74e19871 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -218,6 +218,7 @@ mod float_equality_without_abs; mod float_literal; mod floating_point_arithmetic; mod format; +mod format_args; mod formatting; mod from_over_into; mod from_str_radix_10; @@ -775,6 +776,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic_for_send))); store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default())); store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch)); + store.register_late_pass(move || Box::new(format_args::FormatArgs)); } #[rustfmt::skip] diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 1ff282de95097..fb4d53e2845d1 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -3,7 +3,7 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::ty::is_type_diagnostic_item; -use crate::{is_expn_of, match_def_path, paths}; +use crate::{is_expn_of, last_path_segment, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{self, LitKind}; use rustc_hir as hir; @@ -572,6 +572,106 @@ impl FormatArgsExpn<'tcx> { } } } + + /// Returns a vector of `FormatArgsArg`. + pub fn args(&self) -> Option>> { + if let Some(expr) = self.fmt_expr { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; + if let ExprKind::Array(exprs) = expr.kind; + then { + exprs.iter().map(|fmt| { + if_chain! { + // struct `core::fmt::rt::v1::Argument` + if let ExprKind::Struct(_, fields, _) = fmt.kind; + if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position); + if let ExprKind::Lit(lit) = &position_field.expr.kind; + if let LitKind::Int(position, _) = lit.node; + then { + let i = usize::try_from(position).unwrap(); + Some(FormatArgsArg { value: self.value_args[i], arg: &self.args[i], fmt: Some(fmt) }) + } else { + None + } + } + }).collect() + } else { + None + } + } + } else { + Some( + self.value_args + .iter() + .zip(self.args.iter()) + .map(|(value, arg)| FormatArgsArg { value, arg, fmt: None }) + .collect(), + ) + } + } +} + +/// Type representing a `FormatArgsExpn`'s format arguments +pub struct FormatArgsArg<'tcx> { + /// An element of `value_args` according to `position` + pub value: &'tcx Expr<'tcx>, + /// An element of `args` according to `position` + pub arg: &'tcx Expr<'tcx>, + /// An element of `fmt_expn` + pub fmt: Option<&'tcx Expr<'tcx>>, +} + +impl<'tcx> FormatArgsArg<'tcx> { + /// Returns true if any formatting parameters are used that would have an effect on strings, + /// like `{:+2}` instead of just `{}`. + pub fn has_string_formatting(&self) -> bool { + self.fmt.map_or(false, |fmt| { + // `!` because these conditions check that `self` is unformatted. + !if_chain! { + // struct `core::fmt::rt::v1::Argument` + if let ExprKind::Struct(_, fields, _) = fmt.kind; + if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format); + // struct `core::fmt::rt::v1::FormatSpec` + if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind; + let mut precision_found = false; + let mut width_found = false; + if subfields.iter().all(|field| { + match field.ident.name { + sym::precision => { + precision_found = true; + if let ExprKind::Path(ref precision_path) = field.expr.kind { + last_path_segment(precision_path).ident.name == sym::Implied + } else { + false + } + } + sym::width => { + width_found = true; + if let ExprKind::Path(ref width_qpath) = field.expr.kind { + last_path_segment(width_qpath).ident.name == sym::Implied + } else { + false + } + } + _ => true, + } + }); + if precision_found && width_found; + then { true } else { false } + } + }) + } + + /// Returns true if the argument is formatted using `Display::fmt`. + pub fn is_display(&self) -> bool { + if_chain! { + if let ExprKind::Call(_, [_, format_field]) = self.arg.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = format_field.kind; + if let [.., t, _] = path.segments; + if t.ident.name == sym::Display; + then { true } else { false } + } + } } /// Checks if a `let` statement is from a `for` loop desugaring. diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index e43c575602145..7c61288f66104 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -17,6 +17,12 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ #[cfg(feature = "metadata-collector-lint")] pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const ASSERT_EQ_MACRO: [&str; 3] = ["core", "macros", "assert_eq"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const ASSERT_MACRO: [&str; 4] = ["core", "macros", "builtin", "assert"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const ASSERT_NE_MACRO: [&str; 3] = ["core", "macros", "assert_ne"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"]; @@ -42,11 +48,17 @@ pub const DROP: [&str; 3] = ["core", "mem", "drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; #[cfg(feature = "internal-lints")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const EPRINT_MACRO: [&str; 3] = ["std", "macros", "eprint"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const EPRINTLN_MACRO: [&str; 3] = ["std", "macros", "eprintln"]; pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; pub const F64_EPSILON: [&str; 4] = ["core", "f64", "", "EPSILON"]; pub const FILE: [&str; 3] = ["std", "fs", "File"]; pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const FORMAT_ARGS_MACRO: [&str; 4] = ["core", "macros", "builtin", "format_args"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"]; pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; @@ -109,6 +121,10 @@ pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "Permis pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const PRINT_MACRO: [&str; 3] = ["std", "macros", "print"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const PRINTLN_MACRO: [&str; 3] = ["std", "macros", "println"]; pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"]; pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; @@ -185,3 +201,7 @@ pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"]; pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const WRITE_MACRO: [&str; 3] = ["core", "macros", "write"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const WRITELN_MACRO: [&str; 3] = ["core", "macros", "writeln"]; diff --git a/tests/ui/format_args.fixed b/tests/ui/format_args.fixed new file mode 100644 index 0000000000000..8376566c4d62d --- /dev/null +++ b/tests/ui/format_args.fixed @@ -0,0 +1,105 @@ +// run-rustfix + +#![allow(unreachable_code)] +#![allow(unused_macros)] +#![allow(unused_variables)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::eq_op)] +#![warn(clippy::to_string_in_format_args)] + +use std::io::{stdout, Write}; +use std::ops::Deref; +use std::panic::Location; + +struct Somewhere; + +impl ToString for Somewhere { + fn to_string(&self) -> String { + String::from("somewhere") + } +} + +struct X(u32); + +impl Deref for X { + type Target = u32; + + fn deref(&self) -> &u32 { + &self.0 + } +} + +struct Y<'a>(&'a X); + +impl<'a> Deref for Y<'a> { + type Target = &'a X; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +struct Z(u32); + +impl Deref for Z { + type Target = u32; + + fn deref(&self) -> &u32 { + &self.0 + } +} + +impl std::fmt::Display for Z { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Z") + } +} + +macro_rules! my_macro { + () => { + // here be dragons, do not enter (or lint) + println!("error: something failed at {}", Location::caller().to_string()); + }; +} + +macro_rules! my_other_macro { + () => { + Location::caller().to_string() + }; +} + +fn main() { + let x = &X(1); + let x_ref = &x; + + let _ = format!("error: something failed at {}", Location::caller()); + let _ = write!( + stdout(), + "error: something failed at {}", + Location::caller() + ); + let _ = writeln!( + stdout(), + "error: something failed at {}", + Location::caller() + ); + print!("error: something failed at {}", Location::caller()); + println!("error: something failed at {}", Location::caller()); + eprint!("error: something failed at {}", Location::caller()); + eprintln!("error: something failed at {}", Location::caller()); + let _ = format_args!("error: something failed at {}", Location::caller()); + assert!(true, "error: something failed at {}", Location::caller()); + assert_eq!(0, 0, "error: something failed at {}", Location::caller()); + assert_ne!(0, 0, "error: something failed at {}", Location::caller()); + panic!("error: something failed at {}", Location::caller()); + println!("{}", *X(1)); + println!("{}", ***Y(&X(1))); + println!("{}", Z(1)); + println!("{}", **x); + println!("{}", ***x_ref); + + println!("error: something failed at {}", Somewhere.to_string()); + println!("{} and again {0}", x.to_string()); + my_macro!(); + println!("error: something failed at {}", my_other_macro!()); +} diff --git a/tests/ui/format_args.rs b/tests/ui/format_args.rs new file mode 100644 index 0000000000000..164cc07066dc3 --- /dev/null +++ b/tests/ui/format_args.rs @@ -0,0 +1,105 @@ +// run-rustfix + +#![allow(unreachable_code)] +#![allow(unused_macros)] +#![allow(unused_variables)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::eq_op)] +#![warn(clippy::to_string_in_format_args)] + +use std::io::{stdout, Write}; +use std::ops::Deref; +use std::panic::Location; + +struct Somewhere; + +impl ToString for Somewhere { + fn to_string(&self) -> String { + String::from("somewhere") + } +} + +struct X(u32); + +impl Deref for X { + type Target = u32; + + fn deref(&self) -> &u32 { + &self.0 + } +} + +struct Y<'a>(&'a X); + +impl<'a> Deref for Y<'a> { + type Target = &'a X; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +struct Z(u32); + +impl Deref for Z { + type Target = u32; + + fn deref(&self) -> &u32 { + &self.0 + } +} + +impl std::fmt::Display for Z { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Z") + } +} + +macro_rules! my_macro { + () => { + // here be dragons, do not enter (or lint) + println!("error: something failed at {}", Location::caller().to_string()); + }; +} + +macro_rules! my_other_macro { + () => { + Location::caller().to_string() + }; +} + +fn main() { + let x = &X(1); + let x_ref = &x; + + let _ = format!("error: something failed at {}", Location::caller().to_string()); + let _ = write!( + stdout(), + "error: something failed at {}", + Location::caller().to_string() + ); + let _ = writeln!( + stdout(), + "error: something failed at {}", + Location::caller().to_string() + ); + print!("error: something failed at {}", Location::caller().to_string()); + println!("error: something failed at {}", Location::caller().to_string()); + eprint!("error: something failed at {}", Location::caller().to_string()); + eprintln!("error: something failed at {}", Location::caller().to_string()); + let _ = format_args!("error: something failed at {}", Location::caller().to_string()); + assert!(true, "error: something failed at {}", Location::caller().to_string()); + assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string()); + assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string()); + panic!("error: something failed at {}", Location::caller().to_string()); + println!("{}", X(1).to_string()); + println!("{}", Y(&X(1)).to_string()); + println!("{}", Z(1).to_string()); + println!("{}", x.to_string()); + println!("{}", x_ref.to_string()); + + println!("error: something failed at {}", Somewhere.to_string()); + println!("{} and again {0}", x.to_string()); + my_macro!(); + println!("error: something failed at {}", my_other_macro!()); +} diff --git a/tests/ui/format_args.stderr b/tests/ui/format_args.stderr new file mode 100644 index 0000000000000..9cfc97edeafb8 --- /dev/null +++ b/tests/ui/format_args.stderr @@ -0,0 +1,106 @@ +error: `to_string` applied to a type that implements `Display` in `format!` args + --> $DIR/format_args.rs:75:72 + | +LL | let _ = format!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + | + = note: `-D clippy::to-string-in-format-args` implied by `-D warnings` + +error: `to_string` applied to a type that implements `Display` in `write!` args + --> $DIR/format_args.rs:79:27 + | +LL | Location::caller().to_string() + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `writeln!` args + --> $DIR/format_args.rs:84:27 + | +LL | Location::caller().to_string() + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `print!` args + --> $DIR/format_args.rs:86:63 + | +LL | print!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:87:65 + | +LL | println!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `eprint!` args + --> $DIR/format_args.rs:88:64 + | +LL | eprint!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `eprintln!` args + --> $DIR/format_args.rs:89:66 + | +LL | eprintln!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `format_args!` args + --> $DIR/format_args.rs:90:77 + | +LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `assert!` args + --> $DIR/format_args.rs:91:70 + | +LL | assert!(true, "error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `assert_eq!` args + --> $DIR/format_args.rs:92:73 + | +LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `assert_ne!` args + --> $DIR/format_args.rs:93:73 + | +LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `panic!` args + --> $DIR/format_args.rs:94:63 + | +LL | panic!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:95:20 + | +LL | println!("{}", X(1).to_string()); + | ^^^^^^^^^^^^^^^^ help: use this: `*X(1)` + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:96:20 + | +LL | println!("{}", Y(&X(1)).to_string()); + | ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))` + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:97:24 + | +LL | println!("{}", Z(1).to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:98:20 + | +LL | println!("{}", x.to_string()); + | ^^^^^^^^^^^^^ help: use this: `**x` + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:99:20 + | +LL | println!("{}", x_ref.to_string()); + | ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref` + +error: aborting due to 17 previous errors + diff --git a/tests/ui/format_args_unfixable.rs b/tests/ui/format_args_unfixable.rs new file mode 100644 index 0000000000000..a8c06c2bde664 --- /dev/null +++ b/tests/ui/format_args_unfixable.rs @@ -0,0 +1,60 @@ +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::eq_op)] +#![warn(clippy::format_in_format_args)] +#![warn(clippy::to_string_in_format_args)] + +use std::io::{stdout, Error, ErrorKind, Write}; +use std::ops::Deref; +use std::panic::Location; + +macro_rules! my_macro { + () => { + // here be dragons, do not enter (or lint) + println!("error: {}", format!("something failed at {}", Location::caller())); + }; +} + +macro_rules! my_other_macro { + () => { + format!("something failed at {}", Location::caller()) + }; +} + +fn main() { + let error = Error::new(ErrorKind::Other, "bad thing"); + let x = 'x'; + + println!("error: {}", format!("something failed at {}", Location::caller())); + println!("{}: {}", error, format!("something failed at {}", Location::caller())); + println!("{:?}: {}", error, format!("something failed at {}", Location::caller())); + println!("{{}}: {}", format!("something failed at {}", Location::caller())); + println!(r#"error: "{}""#, format!("something failed at {}", Location::caller())); + println!("error: {}", format!(r#"something failed at "{}""#, Location::caller())); + println!("error: {}", format!("something failed at {} {0}", Location::caller())); + let _ = format!("error: {}", format!("something failed at {}", Location::caller())); + let _ = write!( + stdout(), + "error: {}", + format!("something failed at {}", Location::caller()) + ); + let _ = writeln!( + stdout(), + "error: {}", + format!("something failed at {}", Location::caller()) + ); + print!("error: {}", format!("something failed at {}", Location::caller())); + eprint!("error: {}", format!("something failed at {}", Location::caller())); + eprintln!("error: {}", format!("something failed at {}", Location::caller())); + let _ = format_args!("error: {}", format!("something failed at {}", Location::caller())); + assert!(true, "error: {}", format!("something failed at {}", Location::caller())); + assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); + assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); + panic!("error: {}", format!("something failed at {}", Location::caller())); + + println!("error: {}", format_args!("something failed at {}", Location::caller())); + println!("error: {:>70}", format!("something failed at {}", Location::caller())); + println!("error: {} {0}", format!("something failed at {}", Location::caller())); + println!("{} and again {0}", format!("hi {}", x)); + my_macro!(); + println!("error: {}", my_other_macro!()); +} diff --git a/tests/ui/format_args_unfixable.stderr b/tests/ui/format_args_unfixable.stderr new file mode 100644 index 0000000000000..4476218ad58e9 --- /dev/null +++ b/tests/ui/format_args_unfixable.stderr @@ -0,0 +1,175 @@ +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:27:5 + | +LL | println!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::format-in-format-args` implied by `-D warnings` + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:28:5 + | +LL | println!("{}: {}", error, format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:29:5 + | +LL | println!("{:?}: {}", error, format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:30:5 + | +LL | println!("{{}}: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:31:5 + | +LL | println!(r#"error: "{}""#, format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:32:5 + | +LL | println!("error: {}", format!(r#"something failed at "{}""#, Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:33:5 + | +LL | println!("error: {}", format!("something failed at {} {0}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `format!` args + --> $DIR/format_args_unfixable.rs:34:13 + | +LL | let _ = format!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `format!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `write!` args + --> $DIR/format_args_unfixable.rs:35:13 + | +LL | let _ = write!( + | _____________^ +LL | | stdout(), +LL | | "error: {}", +LL | | format!("something failed at {}", Location::caller()) +LL | | ); + | |_____^ + | + = help: combine the `format!(..)` arguments with the outer `write!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `writeln!` args + --> $DIR/format_args_unfixable.rs:40:13 + | +LL | let _ = writeln!( + | _____________^ +LL | | stdout(), +LL | | "error: {}", +LL | | format!("something failed at {}", Location::caller()) +LL | | ); + | |_____^ + | + = help: combine the `format!(..)` arguments with the outer `writeln!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `print!` args + --> $DIR/format_args_unfixable.rs:45:5 + | +LL | print!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `print!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `eprint!` args + --> $DIR/format_args_unfixable.rs:46:5 + | +LL | eprint!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `eprint!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `eprintln!` args + --> $DIR/format_args_unfixable.rs:47:5 + | +LL | eprintln!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `eprintln!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `format_args!` args + --> $DIR/format_args_unfixable.rs:48:13 + | +LL | let _ = format_args!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `format_args!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `assert!` args + --> $DIR/format_args_unfixable.rs:49:5 + | +LL | assert!(true, "error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `assert!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `assert_eq!` args + --> $DIR/format_args_unfixable.rs:50:5 + | +LL | assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `assert_eq!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `assert_ne!` args + --> $DIR/format_args_unfixable.rs:51:5 + | +LL | assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `assert_ne!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `panic!` args + --> $DIR/format_args_unfixable.rs:52:5 + | +LL | panic!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `panic!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: aborting due to 18 previous errors + From 47014d81a1e1adbd5dcfbc4d6df5d116d1b36104 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Thu, 30 Sep 2021 07:40:46 -0400 Subject: [PATCH 044/101] Fix adjacent tests --- tests/ui/expect_fun_call.fixed | 1 + tests/ui/expect_fun_call.rs | 1 + tests/ui/expect_fun_call.stderr | 24 ++++++++++++------------ tests/ui/format.fixed | 2 +- tests/ui/format.rs | 2 +- tests/ui/to_string_in_display.rs | 2 +- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/tests/ui/expect_fun_call.fixed b/tests/ui/expect_fun_call.fixed index a756d1cf50659..cf923a6a5940c 100644 --- a/tests/ui/expect_fun_call.fixed +++ b/tests/ui/expect_fun_call.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::expect_fun_call)] +#![allow(clippy::to_string_in_format_args)] /// Checks implementation of the `EXPECT_FUN_CALL` lint diff --git a/tests/ui/expect_fun_call.rs b/tests/ui/expect_fun_call.rs index 60bbaa89d4282..e6f252259df70 100644 --- a/tests/ui/expect_fun_call.rs +++ b/tests/ui/expect_fun_call.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::expect_fun_call)] +#![allow(clippy::to_string_in_format_args)] /// Checks implementation of the `EXPECT_FUN_CALL` lint diff --git a/tests/ui/expect_fun_call.stderr b/tests/ui/expect_fun_call.stderr index 6dc796f5cee37..ac48a06671cd2 100644 --- a/tests/ui/expect_fun_call.stderr +++ b/tests/ui/expect_fun_call.stderr @@ -1,5 +1,5 @@ error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:28:26 + --> $DIR/expect_fun_call.rs:29:26 | LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` @@ -7,67 +7,67 @@ LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code = note: `-D clippy::expect-fun-call` implied by `-D warnings` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:31:26 + --> $DIR/expect_fun_call.rs:32:26 | LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:41:25 + --> $DIR/expect_fun_call.rs:42:25 | LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:44:25 + --> $DIR/expect_fun_call.rs:45:25 | LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:56:17 + --> $DIR/expect_fun_call.rs:57:17 | LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:77:21 + --> $DIR/expect_fun_call.rs:78:21 | LL | Some("foo").expect(&get_string()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:78:21 + --> $DIR/expect_fun_call.rs:79:21 | LL | Some("foo").expect(get_string().as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:79:21 + --> $DIR/expect_fun_call.rs:80:21 | LL | Some("foo").expect(get_string().as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:81:21 + --> $DIR/expect_fun_call.rs:82:21 | LL | Some("foo").expect(get_static_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_static_str()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:82:21 + --> $DIR/expect_fun_call.rs:83:21 | LL | Some("foo").expect(get_non_static_str(&0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:86:16 + --> $DIR/expect_fun_call.rs:87:16 | LL | Some(true).expect(&format!("key {}, {}", 1, 2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:92:17 + --> $DIR/expect_fun_call.rs:93:17 | LL | opt_ref.expect(&format!("{:?}", opt_ref)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))` diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index 5dd64140e8116..73fc750511c78 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::print_literal, clippy::redundant_clone)] +#![allow(clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args)] #![warn(clippy::useless_format)] struct Foo(pub String); diff --git a/tests/ui/format.rs b/tests/ui/format.rs index 4599fb5207ea8..2f4595650cbf3 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::print_literal, clippy::redundant_clone)] +#![allow(clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args)] #![warn(clippy::useless_format)] struct Foo(pub String); diff --git a/tests/ui/to_string_in_display.rs b/tests/ui/to_string_in_display.rs index eb8105c6b6da0..3ccdcd1117b5a 100644 --- a/tests/ui/to_string_in_display.rs +++ b/tests/ui/to_string_in_display.rs @@ -1,5 +1,5 @@ #![warn(clippy::to_string_in_display)] -#![allow(clippy::inherent_to_string_shadow_display)] +#![allow(clippy::inherent_to_string_shadow_display, clippy::to_string_in_format_args)] use std::fmt; From 75e9f8cbd6d8c704ec89d12e3ddc382402a13119 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Thu, 30 Sep 2021 07:38:03 -0400 Subject: [PATCH 045/101] Remove redundant `to_string`s --- clippy_dev/src/update_lints.rs | 4 ++-- clippy_lints/src/if_then_panic.rs | 4 ++-- clippy_lints/src/inherent_to_string.rs | 8 ++++---- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/transmute/transmute_int_to_char.rs | 2 +- clippy_lints/src/transmute/transmute_int_to_float.rs | 2 +- clippy_lints/src/transmute/transmute_num_to_bytes.rs | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 10d859988f6f2..23f58bc4915f9 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -619,8 +619,8 @@ mod tests { Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"), ]; let expected = vec![ - format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK.to_string()), - format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string()), + 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())); } diff --git a/clippy_lints/src/if_then_panic.rs b/clippy_lints/src/if_then_panic.rs index 10bca59e6d06a..e8cea5529e889 100644 --- a/clippy_lints/src/if_then_panic.rs +++ b/clippy_lints/src/if_then_panic.rs @@ -78,10 +78,10 @@ impl LateLintPass<'_> for IfThenPanic { if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e { sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string() } else { - format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par().to_string()) + format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par()) } } else { - format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par().to_string()) + format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par()) }; span_lint_and_sugg( diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 3c40ca50a0981..61dd0eb4af7ed 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -138,10 +138,10 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { item.span, &format!( "type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`", - self_type.to_string() + self_type ), None, - &format!("remove the inherent method from type `{}`", self_type.to_string()), + &format!("remove the inherent method from type `{}`", self_type), ); } else { span_lint_and_help( @@ -150,10 +150,10 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { item.span, &format!( "implementation of inherent method `to_string(&self) -> String` for type `{}`", - self_type.to_string() + self_type ), None, - &format!("implement trait `Display` for type `{}` instead", self_type.to_string()), + &format!("implement trait `Display` for type `{}` instead", self_type), ); } } diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 44d5ff0b63ad5..201aa06782405 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -678,7 +678,7 @@ fn suggestion_with_swapped_ident( Some(format!( "{}{}{}", snippet_with_applicability(cx, expr.span.with_hi(current_ident.span.lo()), "..", applicability), - new_ident.to_string(), + new_ident, snippet_with_applicability(cx, expr.span.with_lo(current_ident.span.hi()), "..", applicability), )) }) diff --git a/clippy_lints/src/transmute/transmute_int_to_char.rs b/clippy_lints/src/transmute/transmute_int_to_char.rs index 8f884e6a4a17b..e83d2e06b9a8d 100644 --- a/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using", - format!("std::char::from_u32({}).unwrap()", arg.to_string()), + format!("std::char::from_u32({}).unwrap()", arg), Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/transmute/transmute_int_to_float.rs b/clippy_lints/src/transmute/transmute_int_to_float.rs index 2b6a4cff81eb5..05eee380d6f40 100644 --- a/clippy_lints/src/transmute/transmute_int_to_float.rs +++ b/clippy_lints/src/transmute/transmute_int_to_float.rs @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using", - format!("{}::from_bits({})", to_ty, arg.to_string()), + format!("{}::from_bits({})", to_ty, arg), Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/clippy_lints/src/transmute/transmute_num_to_bytes.rs index dd5ded1c91a17..5ba58a7649401 100644 --- a/clippy_lints/src/transmute/transmute_num_to_bytes.rs +++ b/clippy_lints/src/transmute/transmute_num_to_bytes.rs @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using `to_ne_bytes()`", - format!("{}.to_ne_bytes()", arg.to_string()), + format!("{}.to_ne_bytes()", arg), Applicability::Unspecified, ); }, From 25ff7ce1283c45a330793ce74cfa5190c4ab68f8 Mon Sep 17 00:00:00 2001 From: Matthias Kaak Date: Sat, 16 Oct 2021 15:23:34 +0000 Subject: [PATCH 046/101] Fixed naive doc formatting for `#[must_use]` lints --- clippy_lints/src/functions/mod.rs | 12 +++--------- clippy_lints/src/let_underscore.rs | 7 +++---- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 04fc5887e8e8b..d7c5ec9ba355b 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -91,11 +91,9 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for a [`#[must_use]`] attribute on + /// Checks for a `#[must_use]` attribute on /// unit-returning functions and methods. /// - /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// /// ### Why is this bad? /// Unit values are useless. The attribute is likely /// a remnant of a refactoring that removed the return type. @@ -112,12 +110,10 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for a [`#[must_use]`] attribute without + /// Checks for a `#[must_use]` attribute without /// further information on functions and methods that return a type already /// marked as `#[must_use]`. /// - /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// /// ### Why is this bad? /// The attribute isn't needed. Not using the result /// will already be reported. Alternatively, one can add some text to the @@ -138,11 +134,9 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for public functions that have no - /// [`#[must_use]`] attribute, but return something not already marked + /// `#[must_use]` attribute, but return something not already marked /// must-use, have no mutable arg and mutate no statics. /// - /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// /// ### Why is this bad? /// Not bad at all, this lint just shows places where /// you could add the attribute. diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 89146b4dd2c9b..9efd7aba7e83b 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -10,12 +10,11 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// Checks for `let _ = ` - /// where expr is #[must_use] + /// Checks for `let _ = ` where expr is `#[must_use]` /// /// ### Why is this bad? - /// It's better to explicitly - /// handle the value of a #[must_use] expr + /// It's better to explicitly handle the value of a `#[must_use]` + /// expr /// /// ### Example /// ```rust From 599d9126a292d1f6944e473b1d47dde65cb7f993 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sun, 17 Oct 2021 12:04:01 +0200 Subject: [PATCH 047/101] Some "parenthesis" and "parentheses" fixes --- clippy_lints/src/manual_unwrap_or.rs | 2 +- clippy_utils/src/sugg.rs | 6 +++--- tests/ui/manual_unwrap_or.fixed | 4 ++-- tests/ui/manual_unwrap_or.rs | 4 ++-- tests/ui/useless_conversion.fixed | 2 +- tests/ui/useless_conversion.rs | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 2ae9cb4f9c132..42478e3416ece 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -98,7 +98,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { reindent_multiline(or_body_snippet.into(), true, Some(indent)); let suggestion = if scrutinee.span.from_expansion() { - // we don't want parenthesis around macro, e.g. `(some_macro!()).unwrap_or(0)` + // we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)` sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..") } else { diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 5b0efb1fd7132..01fb944cc36f6 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -16,10 +16,10 @@ use std::convert::TryInto; use std::fmt::Display; use std::ops::{Add, Neg, Not, Sub}; -/// A helper type to build suggestion correctly handling parenthesis. +/// A helper type to build suggestion correctly handling parentheses. #[derive(Clone, PartialEq)] pub enum Sugg<'a> { - /// An expression that never needs parenthesis such as `1337` or `[0; 42]`. + /// An expression that never needs parentheses such as `1337` or `[0; 42]`. NonParen(Cow<'a, str>), /// An expression that does not fit in other variants. MaybeParen(Cow<'a, str>), @@ -283,7 +283,7 @@ impl<'a> Sugg<'a> { } } - /// Adds parenthesis to any expression that might need them. Suitable to the + /// Adds parentheses to any expression that might need them. Suitable to the /// `self` argument of a method call /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`). pub fn maybe_par(self) -> Self { diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 3717f962745fb..05d6c56f2aca0 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -74,10 +74,10 @@ fn result_unwrap_or() { let a = Ok::(1); a.unwrap_or(42); - // int case, suggestion must surround Result expr with parenthesis + // int case, suggestion must surround Result expr with parentheses (Ok(1) as Result).unwrap_or(42); - // method call case, suggestion must not surround Result expr `s.method()` with parenthesis + // method call case, suggestion must not surround Result expr `s.method()` with parentheses struct S {} impl S { fn method(self) -> Option { diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 989adde1f5bbb..09f62c69b71de 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -95,13 +95,13 @@ fn result_unwrap_or() { Err(_) => 42, }; - // int case, suggestion must surround Result expr with parenthesis + // int case, suggestion must surround Result expr with parentheses match Ok(1) as Result { Ok(i) => i, Err(_) => 42, }; - // method call case, suggestion must not surround Result expr `s.method()` with parenthesis + // method call case, suggestion must not surround Result expr `s.method()` with parentheses struct S {} impl S { fn method(self) -> Option { diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 76aa82068d62e..70ff08f365518 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -66,7 +66,7 @@ fn main() { let _ = vec![1, 2, 3].into_iter(); let _: String = format!("Hello {}", "world"); - // keep parenthesis around `a + b` for suggestion (see #4750) + // keep parentheses around `a + b` for suggestion (see #4750) let a: i32 = 1; let b: i32 = 1; let _ = (a + b) * 3; diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index ccee7abb404e6..f2444a8f436bf 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -66,7 +66,7 @@ fn main() { let _ = vec![1, 2, 3].into_iter().into_iter(); let _: String = format!("Hello {}", "world").into(); - // keep parenthesis around `a + b` for suggestion (see #4750) + // keep parentheses around `a + b` for suggestion (see #4750) let a: i32 = 1; let b: i32 = 1; let _ = i32::from(a + b) * 3; From a550133b8fa9e3bae23c26eadae1a75f86b240a6 Mon Sep 17 00:00:00 2001 From: Paul Gey Date: Sun, 17 Oct 2021 15:56:59 +0200 Subject: [PATCH 048/101] Fix false positive of `implicit_saturating_sub` with `else` clause Fixes #7831 --- clippy_lints/src/implicit_saturating_sub.rs | 2 +- tests/ui/implicit_saturating_sub.fixed | 8 ++++++++ tests/ui/implicit_saturating_sub.rs | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 79d4d7ddcbced..a4f60ded3a6e0 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { return; } if_chain! { - if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); + if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr); // Check if the conditional expression is a binary operation if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind; diff --git a/tests/ui/implicit_saturating_sub.fixed b/tests/ui/implicit_saturating_sub.fixed index 859765d08a70b..e6f57e9267eac 100644 --- a/tests/ui/implicit_saturating_sub.fixed +++ b/tests/ui/implicit_saturating_sub.fixed @@ -157,4 +157,12 @@ fn main() { if i_64 != 0 { i_64 -= 1; } + + // issue #7831 + // No Lint + if u_32 > 0 { + u_32 -= 1; + } else { + println!("side effect"); + } } diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs index 2f32a7b157821..8bb28d149c628 100644 --- a/tests/ui/implicit_saturating_sub.rs +++ b/tests/ui/implicit_saturating_sub.rs @@ -203,4 +203,12 @@ fn main() { if i_64 != 0 { i_64 -= 1; } + + // issue #7831 + // No Lint + if u_32 > 0 { + u_32 -= 1; + } else { + println!("side effect"); + } } From bdc16242594e2ad9dea5ad8591417765b6bcc41c Mon Sep 17 00:00:00 2001 From: Dmitry Borodin <11879032+Dmitry-Borodin@users.noreply.github.com> Date: Sun, 17 Oct 2021 18:59:36 +0200 Subject: [PATCH 049/101] Add reference to another doc with explanation Add reference to another doc that explains which repository should be passed in this command since this is not covered in the command help itself. --- doc/basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/basics.md b/doc/basics.md index ff2e0417435bf..6da90ebf5f556 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -93,7 +93,7 @@ cargo dev update_lints cargo dev new_lint # automatically formatting all code before each commit cargo dev setup git-hook -# (experimental) Setup Clippy to work with IntelliJ-Rust +# (experimental) Setup Clippy to work with IntelliJ-Rust. Details here: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust cargo dev setup intellij ``` From 5f2ecc37f895f2eeea84651c9246ffea97e5b0fe Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 17 Oct 2021 23:20:30 +0300 Subject: [PATCH 050/101] rustc_span: `Ident::invalid` -> `Ident::empty` The equivalent for `Symbol`s was renamed some time ago (`kw::Invalid` -> `kw::Empty`), and it makes sense to do the same thing for `Ident`s. --- tests/ui-internal/unnecessary_symbol_str.fixed | 4 ++-- tests/ui-internal/unnecessary_symbol_str.rs | 4 ++-- tests/ui-internal/unnecessary_symbol_str.stderr | 9 ++++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/ui-internal/unnecessary_symbol_str.fixed b/tests/ui-internal/unnecessary_symbol_str.fixed index 2ec0efe4c10a5..95b8c6dfe89ee 100644 --- a/tests/ui-internal/unnecessary_symbol_str.fixed +++ b/tests/ui-internal/unnecessary_symbol_str.fixed @@ -11,6 +11,6 @@ fn main() { Symbol::intern("foo") == rustc_span::sym::clippy; Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower; Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper; - Ident::invalid().name == rustc_span::sym::clippy; - rustc_span::sym::clippy == Ident::invalid().name; + Ident::empty().name == rustc_span::sym::clippy; + rustc_span::sym::clippy == Ident::empty().name; } diff --git a/tests/ui-internal/unnecessary_symbol_str.rs b/tests/ui-internal/unnecessary_symbol_str.rs index 87e1b3a2ee76a..ad6937cf60a65 100644 --- a/tests/ui-internal/unnecessary_symbol_str.rs +++ b/tests/ui-internal/unnecessary_symbol_str.rs @@ -11,6 +11,6 @@ fn main() { Symbol::intern("foo").as_str() == "clippy"; Symbol::intern("foo").to_string() == "self"; Symbol::intern("foo").to_ident_string() != "Self"; - &*Ident::invalid().as_str() == "clippy"; - "clippy" == Ident::invalid().to_string(); + &*Ident::empty().as_str() == "clippy"; + "clippy" == Ident::empty().to_string(); } diff --git a/tests/ui-internal/unnecessary_symbol_str.stderr b/tests/ui-internal/unnecessary_symbol_str.stderr index b1284b7c8ffd0..8e04d447fbcaa 100644 --- a/tests/ui-internal/unnecessary_symbol_str.stderr +++ b/tests/ui-internal/unnecessary_symbol_str.stderr @@ -26,14 +26,13 @@ LL | Symbol::intern("foo").to_ident_string() != "Self"; error: unnecessary `Symbol` to string conversion --> $DIR/unnecessary_symbol_str.rs:14:5 | -LL | &*Ident::invalid().as_str() == "clippy"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::invalid().name == rustc_span::sym::clippy` +LL | &*Ident::empty().as_str() == "clippy"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` error: unnecessary `Symbol` to string conversion --> $DIR/unnecessary_symbol_str.rs:15:5 | -LL | "clippy" == Ident::invalid().to_string(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::invalid().name` +LL | "clippy" == Ident::empty().to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` error: aborting due to 5 previous errors - From 63d715209eaf241114f87db247b435df5272e8e4 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Mon, 18 Oct 2021 08:46:11 +0200 Subject: [PATCH 051/101] Remove unneeded allow --- clippy_lints/src/identity_op.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 73bdd67ff5d25..414f465c49414 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -66,7 +66,6 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_ && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)) } -#[allow(clippy::cast_possible_wrap)] fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e) { let check = match *cx.typeck_results().expr_ty(e).kind() { From 797507c58390662098f0d20a9ee89d200f93f11c Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Thu, 14 Oct 2021 20:08:38 -0700 Subject: [PATCH 052/101] Add boilerplate and basic tests --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.rs | 1 + ...railing_zero_sized_array_without_repr_c.rs | 58 +++++++++++++++++++ ...railing_zero_sized_array_without_repr_c.rs | 23 ++++++++ 6 files changed, 85 insertions(+) create mode 100644 clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs create mode 100644 tests/ui/trailing_zero_sized_array_without_repr_c.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index f6a883311c91b..a124bebe92443 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3021,6 +3021,7 @@ Released 2018-09-13 [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines [`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg +[`trailing_zero_sized_array_without_repr_c`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_zero_sized_array_without_repr_c [`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds [`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str [`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index f334f723f6b87..34b87002fa36f 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -443,6 +443,7 @@ store.register_lints(&[ temporary_assignment::TEMPORARY_ASSIGNMENT, to_digit_is_some::TO_DIGIT_IS_SOME, to_string_in_display::TO_STRING_IN_DISPLAY, + trailing_zero_sized_array_without_repr_c::TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, trait_bounds::TYPE_REPETITION_IN_BOUNDS, transmute::CROSSPOINTER_TRANSMUTE, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 96e0b421094d6..325706746db58 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -25,6 +25,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(regex::TRIVIAL_REGEX), LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), + LintId::of(trailing_zero_sized_array_without_repr_c::TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), ]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9794d74e19871..17bffc0830961 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -355,6 +355,7 @@ mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; mod to_string_in_display; +mod trailing_zero_sized_array_without_repr_c; mod trait_bounds; mod transmute; mod transmuting_null; diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs new file mode 100644 index 0000000000000..9aea22af274fd --- /dev/null +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -0,0 +1,58 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Displays a warning when a struct with a trailing zero-sized array is declared without the `repr(C)` attribute. + /// + /// ### Why is this bad? + /// Zero-sized arrays aren't very useful in Rust itself, so such a struct is likely being created to pass to C code (or in conjuction with manual allocation to make it easy to compute the offset of the array). Either way, `#[repr(C)]` is needed. + /// + /// ### Example + /// ```rust + /// struct RarelyUseful { + /// some_field: usize, + /// last: [SomeType; 0], + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// #[repr(C)] + /// struct MakesSense { + /// some_field: usize, + /// last: [SomeType; 0], + /// } + /// ``` + pub TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, + nursery, + "struct with a trailing zero-sized array but without `repr(C)`" +} +declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C]); + +impl LateLintPass<'_> for TrailingZeroSizedArrayWithoutReprC { + fn check_struct_def(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::VariantData<'tcx>) {} + + fn check_struct_def_post(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::VariantData<'tcx>) {} + // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/enum.TyKind.html#variant.Array in latepass + // or https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.TyKind.html#variant.Array in early pass + + fn check_field_def(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::FieldDef<'tcx>) {} + + fn check_attribute(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_ast::Attribute) {} + + fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, _: &'tcx [rustc_ast::Attribute]) {} + + fn exit_lint_attrs(&mut self, _: &LateContext<'tcx>, _: &'tcx [rustc_ast::Attribute]) {} +} +// +// TODO: Register the lint pass in `clippy_lints/src/lib.rs`, +// e.g. store.register_late_pass(|| +// Box::new(trailing_zero_sized_array_without_repr_c::TrailingZeroSizedArrayWithoutReprC)); + + +fn temp_alert() { + span_lint_and_sugg(cx, lint, sp, msg, help, sugg, applicability) +} \ No newline at end of file diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.rs b/tests/ui/trailing_zero_sized_array_without_repr_c.rs new file mode 100644 index 0000000000000..2a0f432bc2d0e --- /dev/null +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.rs @@ -0,0 +1,23 @@ +#![warn(clippy::trailing_zero_sized_array_without_repr_c)] + +struct RarelyUseful { + field: i32, + last: [SomeType; 0], +} + +#[repr(C)] +struct GoodReason { + field: i32, + last: [SomeType; 0], +} + +struct OnlyFieldIsZeroSizeArray { + first_and_last: [SomeType; 0], +} + +struct GenericArrayType { + field: i32, + last: [T; 0], +} + +fn main() {} From c69387a0d530dccb56ef11d4cd148f9c38904893 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Fri, 15 Oct 2021 00:13:42 -0700 Subject: [PATCH 053/101] Well it builds --- ...railing_zero_sized_array_without_repr_c.rs | 47 +++++++++++++++---- ...railing_zero_sized_array_without_repr_c.rs | 6 +-- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index 9aea22af274fd..507d9b404127e 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_hir::*; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -33,26 +34,54 @@ declare_clippy_lint! { declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C]); impl LateLintPass<'_> for TrailingZeroSizedArrayWithoutReprC { - fn check_struct_def(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::VariantData<'tcx>) {} + fn check_struct_def(&mut self, cx: &LateContext<'tcx>, data: &'tcx rustc_hir::VariantData<'tcx>) { + dbg!("in check_struct_def"); + if_chain! { + if let Some(def) = data.fields().last(); + if let rustc_hir::TyKind::Array(ty, acost) = def.ty.kind; + then { + // is the AnonConst `0` + } + } - fn check_struct_def_post(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::VariantData<'tcx>) {} + // span_lint_and_sugg( + // cx, + // todo!(), + // todo!(), + // todo!(), + // todo!(), + // todo!(), + // rustc_errors::Applicability::MaybeIncorrect, + // ) + } // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/enum.TyKind.html#variant.Array in latepass // or https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.TyKind.html#variant.Array in early pass - fn check_field_def(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::FieldDef<'tcx>) {} + // fn check_struct_def_post(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::VariantData<'tcx>) + // {} - fn check_attribute(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_ast::Attribute) {} + // fn check_field_def(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::FieldDef<'tcx>) {} - fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, _: &'tcx [rustc_ast::Attribute]) {} + // fn check_attribute(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_ast::Attribute) {} - fn exit_lint_attrs(&mut self, _: &LateContext<'tcx>, _: &'tcx [rustc_ast::Attribute]) {} + // fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, _: &'tcx [rustc_ast::Attribute]) {} + + // fn exit_lint_attrs(&mut self, _: &LateContext<'tcx>, _: &'tcx [rustc_ast::Attribute]) {} } // // TODO: Register the lint pass in `clippy_lints/src/lib.rs`, // e.g. store.register_late_pass(|| // Box::new(trailing_zero_sized_array_without_repr_c::TrailingZeroSizedArrayWithoutReprC)); +// fn temp_alert() {} -fn temp_alert() { - span_lint_and_sugg(cx, lint, sp, msg, help, sugg, applicability) -} \ No newline at end of file +impl EarlyLintPass for TrailingZeroSizedArrayWithoutReprC { + fn check_struct_def(&mut self, cx: &EarlyContext<'_>, data: &rustc_ast::VariantData) { + if_chain! { + if let rustc_ast::ast::VariantData::Struct(field_defs, some_bool_huh) = data; + if let Some(last_field) = field_defs.last(); + if let rustc_ast::ast::TyKind::Array(_, aconst) = &last_field.ty.kind; + then {dbg!(aconst); return ();} + } + } +} diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.rs b/tests/ui/trailing_zero_sized_array_without_repr_c.rs index 2a0f432bc2d0e..771622178e73f 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.rs @@ -2,17 +2,17 @@ struct RarelyUseful { field: i32, - last: [SomeType; 0], + last: [usize; 0], } #[repr(C)] struct GoodReason { field: i32, - last: [SomeType; 0], + last: [usize; 0], } struct OnlyFieldIsZeroSizeArray { - first_and_last: [SomeType; 0], + first_and_last: [usize; 0], } struct GenericArrayType { From 92d3b775bd05c363b69b91ed66c20904c7fbfbe7 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Fri, 15 Oct 2021 01:31:26 -0700 Subject: [PATCH 054/101] =?UTF-8?q?ayy=20it=20compiles!=20ship=20it,=20rig?= =?UTF-8?q?ht=3F=20=F0=9F=98=8E=20/s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit why was `rustc_lint_defs` not already externed in `lib.rs`? and how was r-a able to find it but cargo wasn't? 🤔 --- clippy_lints/src/lib.rs | 2 + ...railing_zero_sized_array_without_repr_c.rs | 76 ++++++++----------- 2 files changed, 33 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 17bffc0830961..6624ee9310144 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -30,6 +30,7 @@ extern crate rustc_index; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; +extern crate rustc_lint_defs; extern crate rustc_middle; extern crate rustc_mir_dataflow; extern crate rustc_parse; @@ -487,6 +488,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(utils::internal_lints::OuterExpnDataPass)); } + store.register_early_pass(|| Box::new(trailing_zero_sized_array_without_repr_c::TrailingZeroSizedArrayWithoutReprC)); 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/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index 507d9b404127e..a96b879229587 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_hir::*; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint_defs::Applicability; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -33,55 +32,42 @@ declare_clippy_lint! { } declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C]); -impl LateLintPass<'_> for TrailingZeroSizedArrayWithoutReprC { - fn check_struct_def(&mut self, cx: &LateContext<'tcx>, data: &'tcx rustc_hir::VariantData<'tcx>) { - dbg!("in check_struct_def"); - if_chain! { - if let Some(def) = data.fields().last(); - if let rustc_hir::TyKind::Array(ty, acost) = def.ty.kind; - then { - // is the AnonConst `0` - } - } - - // span_lint_and_sugg( - // cx, - // todo!(), - // todo!(), - // todo!(), - // todo!(), - // todo!(), - // rustc_errors::Applicability::MaybeIncorrect, - // ) - } - // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/enum.TyKind.html#variant.Array in latepass - // or https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.TyKind.html#variant.Array in early pass - - // fn check_struct_def_post(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::VariantData<'tcx>) - // {} - - // fn check_field_def(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::FieldDef<'tcx>) {} - - // fn check_attribute(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_ast::Attribute) {} - - // fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, _: &'tcx [rustc_ast::Attribute]) {} - - // fn exit_lint_attrs(&mut self, _: &LateContext<'tcx>, _: &'tcx [rustc_ast::Attribute]) {} -} // // TODO: Register the lint pass in `clippy_lints/src/lib.rs`, -// e.g. store.register_late_pass(|| +// e.g. store.register_early_pass(|| // Box::new(trailing_zero_sized_array_without_repr_c::TrailingZeroSizedArrayWithoutReprC)); -// fn temp_alert() {} - impl EarlyLintPass for TrailingZeroSizedArrayWithoutReprC { fn check_struct_def(&mut self, cx: &EarlyContext<'_>, data: &rustc_ast::VariantData) { - if_chain! { - if let rustc_ast::ast::VariantData::Struct(field_defs, some_bool_huh) = data; - if let Some(last_field) = field_defs.last(); - if let rustc_ast::ast::TyKind::Array(_, aconst) = &last_field.ty.kind; - then {dbg!(aconst); return ();} + if is_struct_with_trailing_zero_sized_array(cx, data) && !has_repr_c(cx, data) { + span_lint_and_sugg( + cx, + todo!(), + todo!(), + todo!(), + "try", + "`#[repr(C)]`".to_string(), + Applicability::MachineApplicable, + ) + } + } +} + +fn is_struct_with_trailing_zero_sized_array(cx: &EarlyContext<'_>, data: &rustc_ast::VariantData) -> bool { + if_chain! { + if let rustc_ast::ast::VariantData::Struct(field_defs, some_bool_huh) = data; + if let Some(last_field) = field_defs.last(); + if let rustc_ast::ast::TyKind::Array(_, aconst) = &last_field.ty.kind; + // TODO: if array is zero-sized; + then { + dbg!(aconst); + true + } else { + false } } } + +fn has_repr_c(cx: &EarlyContext<'_>, data: &rustc_ast::VariantData) -> bool { + todo!() +} From 7ee8e7a9b846686148b8797ecc5ae724a6c5a1a9 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Fri, 15 Oct 2021 16:16:27 -0700 Subject: [PATCH 055/101] Implement detecting trailing zero-sized array --- clippy_lints/src/lib.rs | 4 +- ...railing_zero_sized_array_without_repr_c.rs | 62 ++++++++++++------- ...railing_zero_sized_array_without_repr_c.rs | 26 ++++++++ 3 files changed, 69 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6624ee9310144..d494892c3b4c6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -30,7 +30,6 @@ extern crate rustc_index; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; -extern crate rustc_lint_defs; extern crate rustc_middle; extern crate rustc_mir_dataflow; extern crate rustc_parse; @@ -488,7 +487,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(utils::internal_lints::OuterExpnDataPass)); } - store.register_early_pass(|| Box::new(trailing_zero_sized_array_without_repr_c::TrailingZeroSizedArrayWithoutReprC)); 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)); @@ -780,6 +778,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default())); 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_zero_sized_array_without_repr_c::TrailingZeroSizedArrayWithoutReprC)); + } #[rustfmt::skip] diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index a96b879229587..6ca382d167959 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -1,6 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_lint_defs::Applicability; +// use clippy_utils::is_integer_const; +use clippy_utils::consts::{miri_to_const, Constant}; +use rustc_errors::Applicability; +use rustc_hir::{Item, ItemKind, TyKind, VariantData}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -36,38 +39,55 @@ declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_AR // TODO: Register the lint pass in `clippy_lints/src/lib.rs`, // e.g. store.register_early_pass(|| // Box::new(trailing_zero_sized_array_without_repr_c::TrailingZeroSizedArrayWithoutReprC)); +// DONE! -impl EarlyLintPass for TrailingZeroSizedArrayWithoutReprC { - fn check_struct_def(&mut self, cx: &EarlyContext<'_>, data: &rustc_ast::VariantData) { - if is_struct_with_trailing_zero_sized_array(cx, data) && !has_repr_c(cx, data) { - span_lint_and_sugg( - cx, - todo!(), - todo!(), - todo!(), - "try", - "`#[repr(C)]`".to_string(), - Applicability::MachineApplicable, - ) +impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if is_struct_with_trailing_zero_sized_array(cx, item) + /* && !has_repr_c(cx, item) */ + { + // span_lint_and_sugg( + // cx, + // todo!(), + // todo!(), + // todo!(), + // "try", + // "`#[repr(C)]`".to_string(), + // Applicability::MachineApplicable, + // ); + // println!("consider yourself linted 😎"); } } } -fn is_struct_with_trailing_zero_sized_array(cx: &EarlyContext<'_>, data: &rustc_ast::VariantData) -> bool { +fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { + dbg!(item.ident); if_chain! { - if let rustc_ast::ast::VariantData::Struct(field_defs, some_bool_huh) = data; + if let ItemKind::Struct(data, _generics) = &item.kind; + if let VariantData::Struct(field_defs, _) = data; if let Some(last_field) = field_defs.last(); - if let rustc_ast::ast::TyKind::Array(_, aconst) = &last_field.ty.kind; - // TODO: if array is zero-sized; + if let TyKind::Array(_, aconst) = last_field.ty.kind; + let aconst_def_id = cx.tcx.hir().body_owner_def_id(aconst.body).to_def_id(); + let ty = cx.tcx.type_of(aconst_def_id); + let constant = cx + .tcx + .const_eval_poly(aconst_def_id) // NOTE: maybe const_eval_resolve? seems especially cursed to be using a const expr which resolves to 0 to create a zero-sized array, tho + .ok() + .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); + if let Some(Constant::Int(val)) = constant.and_then(miri_to_const); + if val == 0; then { - dbg!(aconst); + eprintln!("true"); true } else { + // dbg!(aconst); + eprintln!("false"); false } } } -fn has_repr_c(cx: &EarlyContext<'_>, data: &rustc_ast::VariantData) -> bool { - todo!() +fn has_repr_c(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { + // todo!() + true } diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.rs b/tests/ui/trailing_zero_sized_array_without_repr_c.rs index 771622178e73f..5f844f16ba103 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.rs @@ -20,4 +20,30 @@ struct GenericArrayType { last: [T; 0], } +struct SizedArray { + field: i32, + last: [usize; 1], +} + +const ZERO: usize = 0; +struct ZeroSizedFromExternalConst { + field: i32, + last: [usize; ZERO], +} + +const ONE: usize = 1; +struct NonZeroSizedFromExternalConst { + field: i32, + last: [usize; ONE], +} + +#[allow(clippy::eq_op)] // lmao im impressed +const fn compute_zero() -> usize { + (4 + 6) - (2 * 5) +} +struct UsingFunction { + field: i32, + last: [usize; compute_zero()], +} + fn main() {} From 523b0131618d8e4437a788eb1e3ddca743b3789f Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Fri, 15 Oct 2021 23:44:39 -0700 Subject: [PATCH 056/101] Implement getting an array of attributes! --- ...railing_zero_sized_array_without_repr_c.rs | 73 ++++++++++++------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index 6ca382d167959..063dfb874cf1b 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind, TyKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -43,9 +44,8 @@ declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_AR impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if is_struct_with_trailing_zero_sized_array(cx, item) - /* && !has_repr_c(cx, item) */ - { + dbg!(item.ident); + if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_c(cx, item) { // span_lint_and_sugg( // cx, // todo!(), @@ -61,33 +61,52 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { } fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { - dbg!(item.ident); - if_chain! { - if let ItemKind::Struct(data, _generics) = &item.kind; - if let VariantData::Struct(field_defs, _) = data; - if let Some(last_field) = field_defs.last(); - if let TyKind::Array(_, aconst) = last_field.ty.kind; - let aconst_def_id = cx.tcx.hir().body_owner_def_id(aconst.body).to_def_id(); - let ty = cx.tcx.type_of(aconst_def_id); - let constant = cx - .tcx - .const_eval_poly(aconst_def_id) // NOTE: maybe const_eval_resolve? seems especially cursed to be using a const expr which resolves to 0 to create a zero-sized array, tho - .ok() - .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); - if let Some(Constant::Int(val)) = constant.and_then(miri_to_const); - if val == 0; - then { - eprintln!("true"); - true - } else { - // dbg!(aconst); - eprintln!("false"); - false + if let ItemKind::Struct(data, _generics) = &item.kind { + if let VariantData::Struct(field_defs, _) = data { + if let Some(last_field) = field_defs.last() { + if let TyKind::Array(_, aconst) = last_field.ty.kind { + let aconst_def_id = cx.tcx.hir().body_owner_def_id(aconst.body).to_def_id(); + let ty = cx.tcx.type_of(aconst_def_id); + let constant = cx + .tcx + // NOTE: maybe const_eval_resolve? seems especially cursed to be using a const expr which + // resolves to 0 to create a zero-sized array, tho + .const_eval_poly(aconst_def_id) + .ok() + .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); + if let Some(Constant::Int(val)) = constant.and_then(miri_to_const) { + if val == 0 { + eprintln!("trailing: true"); + return true; + } + } + } + } } } + // dbg!(aconst); + eprintln!("trailing: false"); + false } fn has_repr_c(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { - // todo!() - true + // let hir_id2 = if let Some(body) = cx.enclosing_body { + // body.hir_id + // } else { + // todo!(); + // }; + + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id); + let attrs = cx.tcx.hir().attrs(hir_id); + // NOTE: Can there ever be more than one `repr` attribute? + // other `repr` syms: repr, repr128, repr_align, repr_align_enum, repr_no_niche, repr_packed, + // repr_simd, repr_transparent + + if let Some(repr_attr) = attrs.iter().find(|attr| attr.has_name(sym::repr)) { + eprintln!("repr: true"); + true + } else { + eprintln!("repr: false"); + false + } } From e53a4da4a142d3e90f8eb658a19466eca54901ea Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Sat, 16 Oct 2021 00:51:09 -0700 Subject: [PATCH 057/101] it works i think (incl some `dbg`s) --- ...railing_zero_sized_array_without_repr_c.rs | 53 +++++++------ ...railing_zero_sized_array_without_repr_c.rs | 79 +++++++++++++------ 2 files changed, 82 insertions(+), 50 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index 063dfb874cf1b..62f8ecdc2163a 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; // use clippy_utils::is_integer_const; use clippy_utils::consts::{miri_to_const, Constant}; use rustc_errors::Applicability; -use rustc_hir::{Item, ItemKind, TyKind, VariantData}; +use rustc_hir::{HirId, Item, ItemKind, TyKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -45,17 +45,29 @@ declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_AR impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { dbg!(item.ident); - if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_c(cx, item) { - // span_lint_and_sugg( - // cx, - // todo!(), - // todo!(), - // todo!(), - // "try", - // "`#[repr(C)]`".to_string(), - // Applicability::MachineApplicable, - // ); - // println!("consider yourself linted 😎"); + + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id); + let hir_id2 = item.hir_id(); + dbg!(hir_id); + dbg!(hir_id2); + dbg!(hir_id == hir_id2); + + let span1 = cx.tcx.hir().span(hir_id); + let span2 = item.span; + dbg!(span1); + dbg!(span2); + dbg!(span1 == span2); + + if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_c(cx, hir_id) { + span_lint_and_sugg( + cx, + TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, + span2, + "trailing zero-sized array in a struct which isn't marked `#[repr(C)]`", + "try", + "#[repr(C)]".to_string(), + Applicability::MaybeIncorrect, + ); } } } @@ -76,7 +88,7 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); if let Some(Constant::Int(val)) = constant.and_then(miri_to_const) { if val == 0 { - eprintln!("trailing: true"); + // eprintln!("trailing: true"); return true; } } @@ -85,28 +97,21 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx } } // dbg!(aconst); - eprintln!("trailing: false"); + // eprintln!("trailing: false"); false } -fn has_repr_c(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { - // let hir_id2 = if let Some(body) = cx.enclosing_body { - // body.hir_id - // } else { - // todo!(); - // }; - - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id); +fn has_repr_c(cx: &LateContext<'tcx>, hir_id: HirId) -> bool { let attrs = cx.tcx.hir().attrs(hir_id); // NOTE: Can there ever be more than one `repr` attribute? // other `repr` syms: repr, repr128, repr_align, repr_align_enum, repr_no_niche, repr_packed, // repr_simd, repr_transparent if let Some(repr_attr) = attrs.iter().find(|attr| attr.has_name(sym::repr)) { - eprintln!("repr: true"); + // eprintln!("repr: true"); true } else { - eprintln!("repr: false"); + // eprintln!("repr: false"); false } } diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.rs b/tests/ui/trailing_zero_sized_array_without_repr_c.rs index 5f844f16ba103..1e3683b2c2596 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.rs @@ -15,35 +15,62 @@ struct OnlyFieldIsZeroSizeArray { first_and_last: [usize; 0], } -struct GenericArrayType { - field: i32, - last: [T; 0], -} +// struct GenericArrayType { +// field: i32, +// last: [T; 0], +// } -struct SizedArray { - field: i32, - last: [usize; 1], -} +// struct SizedArray { +// field: i32, +// last: [usize; 1], +// } -const ZERO: usize = 0; -struct ZeroSizedFromExternalConst { - field: i32, - last: [usize; ZERO], -} +// const ZERO: usize = 0; +// struct ZeroSizedFromExternalConst { +// field: i32, +// last: [usize; ZERO], +// } -const ONE: usize = 1; -struct NonZeroSizedFromExternalConst { - field: i32, - last: [usize; ONE], -} +// const ONE: usize = 1; +// struct NonZeroSizedFromExternalConst { +// field: i32, +// last: [usize; ONE], +// } -#[allow(clippy::eq_op)] // lmao im impressed -const fn compute_zero() -> usize { - (4 + 6) - (2 * 5) -} -struct UsingFunction { - field: i32, - last: [usize; compute_zero()], -} +// #[allow(clippy::eq_op)] // lmao im impressed +// const fn compute_zero() -> usize { +// (4 + 6) - (2 * 5) +// } +// struct UsingFunction { +// field: i32, +// last: [usize; compute_zero()], +// } + +// // TODO: same +// #[repr(packed)] +// struct ReprPacked { +// small: u8, +// medium: i32, +// weird: [u64; 0], +// } + +// // TODO: actually, uh,, +// #[repr(align(64))] +// struct ReprAlign { +// field: i32, +// last: [usize; 0], +// } +// #[repr(C, align(64))] +// struct ReprCAlign { +// field: i32, +// last: [usize; 0], +// } + +// #[repr(C)] +// enum DontLintAnonymousStructsFromDesuraging { +// A(u32), +// B(f32, [u64; 0]), +// C { x: u32, y: [u64; 0] }, +// } fn main() {} From 4b4db597722ff561515812e2e0cab255a678a41c Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Sat, 16 Oct 2021 02:01:17 -0700 Subject: [PATCH 058/101] output looks fantastic --- ...railing_zero_sized_array_without_repr_c.rs | 34 ++--- ...railing_zero_sized_array_without_repr_c.rs | 131 +++++++++++------- 2 files changed, 96 insertions(+), 69 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index 62f8ecdc2163a..7eeb2914c4007 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -1,8 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -// use clippy_utils::is_integer_const; use clippy_utils::consts::{miri_to_const, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; use rustc_errors::Applicability; -use rustc_hir::{HirId, Item, ItemKind, TyKind, VariantData}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{Item, ItemKind, TyKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -46,26 +47,14 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { dbg!(item.ident); - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id); - let hir_id2 = item.hir_id(); - dbg!(hir_id); - dbg!(hir_id2); - dbg!(hir_id == hir_id2); - - let span1 = cx.tcx.hir().span(hir_id); - let span2 = item.span; - dbg!(span1); - dbg!(span2); - dbg!(span1 == span2); - - if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_c(cx, hir_id) { + if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_c(cx, item.def_id) { span_lint_and_sugg( cx, TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, - span2, - "trailing zero-sized array in a struct which isn't marked `#[repr(C)]`", + item.span, + "trailing zero-sized array in a struct which is not marked `#[repr(C)]`", "try", - "#[repr(C)]".to_string(), + format!("#[repr(C)]\n{}", snippet(cx, item.span, "..")), Applicability::MaybeIncorrect, ); } @@ -101,13 +90,14 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx false } -fn has_repr_c(cx: &LateContext<'tcx>, hir_id: HirId) -> bool { +fn has_repr_c(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id); let attrs = cx.tcx.hir().attrs(hir_id); + // NOTE: Can there ever be more than one `repr` attribute? // other `repr` syms: repr, repr128, repr_align, repr_align_enum, repr_no_niche, repr_packed, // repr_simd, repr_transparent - - if let Some(repr_attr) = attrs.iter().find(|attr| attr.has_name(sym::repr)) { + if let Some(_repr_attr) = attrs.iter().find(|attr| attr.has_name(sym::repr)) { // eprintln!("repr: true"); true } else { diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.rs b/tests/ui/trailing_zero_sized_array_without_repr_c.rs index 1e3683b2c2596..311193fb4a1b6 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.rs @@ -1,5 +1,7 @@ #![warn(clippy::trailing_zero_sized_array_without_repr_c)] +// #![feature(const_generics_defaults)] + struct RarelyUseful { field: i32, last: [usize; 0], @@ -15,62 +17,97 @@ struct OnlyFieldIsZeroSizeArray { first_and_last: [usize; 0], } -// struct GenericArrayType { -// field: i32, -// last: [T; 0], -// } +struct GenericArrayType { + field: i32, + last: [T; 0], +} -// struct SizedArray { -// field: i32, -// last: [usize; 1], -// } +struct SizedArray { + field: i32, + last: [usize; 1], +} -// const ZERO: usize = 0; -// struct ZeroSizedFromExternalConst { -// field: i32, -// last: [usize; ZERO], -// } +const ZERO: usize = 0; +struct ZeroSizedFromExternalConst { + field: i32, + last: [usize; ZERO], +} + +const ONE: usize = 1; +struct NonZeroSizedFromExternalConst { + field: i32, + last: [usize; ONE], +} -// const ONE: usize = 1; -// struct NonZeroSizedFromExternalConst { +#[allow(clippy::eq_op)] // lmao im impressed +const fn compute_zero() -> usize { + (4 + 6) - (2 * 5) +} +struct UsingFunction { + field: i32, + last: [usize; compute_zero()], +} + +// #[repr(C)] +// struct ConstParamOk { // field: i32, -// last: [usize; ONE], +// last: [usize; N] // } -// #[allow(clippy::eq_op)] // lmao im impressed -// const fn compute_zero() -> usize { -// (4 + 6) - (2 * 5) -// } -// struct UsingFunction { +// struct ConstParamLint { // field: i32, -// last: [usize; compute_zero()], +// last: [usize; N] // } -// // TODO: same -// #[repr(packed)] -// struct ReprPacked { -// small: u8, -// medium: i32, -// weird: [u64; 0], -// } -// // TODO: actually, uh,, -// #[repr(align(64))] -// struct ReprAlign { -// field: i32, -// last: [usize; 0], -// } -// #[repr(C, align(64))] -// struct ReprCAlign { -// field: i32, -// last: [usize; 0], -// } +// TODO: actually, uh,, +#[repr(packed)] +struct ReprPacked { + small: u8, + medium: i32, + weird: [u64; 0], +} -// #[repr(C)] -// enum DontLintAnonymousStructsFromDesuraging { -// A(u32), -// B(f32, [u64; 0]), -// C { x: u32, y: [u64; 0] }, -// } +// same +#[repr(align(64))] +struct ReprAlign { + field: i32, + last: [usize; 0], +} -fn main() {} +// same +#[repr(C, align(64))] +struct ReprCAlign { + field: i32, + last: [usize; 0], +} + +#[repr(C)] +enum DontLintAnonymousStructsFromDesuraging { + A(u32), + B(f32, [u64; 0]), + C { x: u32, y: [u64; 0] }, +} + +struct LotsOfFields { + f1: u32, + f2: u32, + f3: u32, + f4: u32, + f5: u32, + f6: u32, + f7: u32, + f8: u32, + f9: u32, + f10: u32, + f11: u32, + f12: u32, + f13: u32, + f14: u32, + f15: u32, + f16: u32, + last: [usize; 0], +} + +fn main() { +} From b9948c4be6741ae916b838df29e9777467d4c84c Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Sat, 16 Oct 2021 02:26:08 -0700 Subject: [PATCH 059/101] Ran `dev bless`! --- ...railing_zero_sized_array_without_repr_c.rs | 58 ++++----- ...railing_zero_sized_array_without_repr_c.rs | 17 +-- ...ing_zero_sized_array_without_repr_c.stderr | 113 ++++++++++++++++++ 3 files changed, 147 insertions(+), 41 deletions(-) create mode 100644 tests/ui/trailing_zero_sized_array_without_repr_c.stderr diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index 7eeb2914c4007..7f5798355129d 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// Use instead: /// ```rust /// #[repr(C)] - /// struct MakesSense { + /// struct MoreOftenUseful { /// some_field: usize, /// last: [SomeType; 0], /// } @@ -45,15 +45,13 @@ declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_AR impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - dbg!(item.ident); - if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_c(cx, item.def_id) { span_lint_and_sugg( cx, TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, item.span, "trailing zero-sized array in a struct which is not marked `#[repr(C)]`", - "try", + "try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute):", format!("#[repr(C)]\n{}", snippet(cx, item.span, "..")), Applicability::MaybeIncorrect, ); @@ -62,46 +60,40 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { } fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { - if let ItemKind::Struct(data, _generics) = &item.kind { - if let VariantData::Struct(field_defs, _) = data { - if let Some(last_field) = field_defs.last() { - if let TyKind::Array(_, aconst) = last_field.ty.kind { - let aconst_def_id = cx.tcx.hir().body_owner_def_id(aconst.body).to_def_id(); - let ty = cx.tcx.type_of(aconst_def_id); - let constant = cx - .tcx - // NOTE: maybe const_eval_resolve? seems especially cursed to be using a const expr which - // resolves to 0 to create a zero-sized array, tho - .const_eval_poly(aconst_def_id) - .ok() - .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); - if let Some(Constant::Int(val)) = constant.and_then(miri_to_const) { - if val == 0 { - // eprintln!("trailing: true"); - return true; - } - } - } - } + if_chain! { + if let ItemKind::Struct(data, _generics) = &item.kind; + if let VariantData::Struct(field_defs, _) = data; + if let Some(last_field) = field_defs.last(); + if let TyKind::Array(_, aconst) = last_field.ty.kind; + let aconst_def_id = cx.tcx.hir().body_owner_def_id(aconst.body).to_def_id(); + let ty = cx.tcx.type_of(aconst_def_id); + let constant = cx + .tcx + // NOTE: maybe const_eval_resolve? + .const_eval_poly(aconst_def_id) + .ok() + .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); + if let Some(Constant::Int(val)) = constant.and_then(miri_to_const); + if val == 0; + then { + true + } else { + false } } - // dbg!(aconst); - // eprintln!("trailing: false"); - false } fn has_repr_c(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool { - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id); - let attrs = cx.tcx.hir().attrs(hir_id); + let hir_map = cx.tcx.hir(); + let hir_id = hir_map.local_def_id_to_hir_id(def_id); + let attrs = hir_map.attrs(hir_id); // NOTE: Can there ever be more than one `repr` attribute? // other `repr` syms: repr, repr128, repr_align, repr_align_enum, repr_no_niche, repr_packed, // repr_simd, repr_transparent - if let Some(_repr_attr) = attrs.iter().find(|attr| attr.has_name(sym::repr)) { - // eprintln!("repr: true"); + if let Some(_attr) = attrs.iter().find(|attr| attr.has_name(sym::repr)) { true } else { - // eprintln!("repr: false"); false } } diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.rs b/tests/ui/trailing_zero_sized_array_without_repr_c.rs index 311193fb4a1b6..62fe94d7abf05 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.rs @@ -1,6 +1,5 @@ #![warn(clippy::trailing_zero_sized_array_without_repr_c)] - -// #![feature(const_generics_defaults)] +// #![feature(const_generics_defaults)] // see below struct RarelyUseful { field: i32, @@ -48,6 +47,9 @@ struct UsingFunction { last: [usize; compute_zero()], } +// NOTE: including these (along with the required feature) triggers an ICE. Should make sure the +// const generics people are aware of that if they weren't already. + // #[repr(C)] // struct ConstParamOk { // field: i32, @@ -59,8 +61,7 @@ struct UsingFunction { // last: [usize; N] // } - -// TODO: actually, uh,, +// TODO: actually, uh,, no idea what behavior here would be #[repr(packed)] struct ReprPacked { small: u8, @@ -68,20 +69,21 @@ struct ReprPacked { weird: [u64; 0], } -// same +// TODO: clarify expected behavior #[repr(align(64))] struct ReprAlign { field: i32, last: [usize; 0], } -// same +// TODO: clarify expected behavior #[repr(C, align(64))] struct ReprCAlign { field: i32, last: [usize; 0], } +// NOTE: because of https://doc.rust-lang.org/stable/reference/type-layout.html#primitive-representation-of-enums-with-fields and I'm not sure when in the compilation pipeline that would happen #[repr(C)] enum DontLintAnonymousStructsFromDesuraging { A(u32), @@ -109,5 +111,4 @@ struct LotsOfFields { last: [usize; 0], } -fn main() { -} +fn main() {} diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.stderr b/tests/ui/trailing_zero_sized_array_without_repr_c.stderr new file mode 100644 index 0000000000000..84606ed618588 --- /dev/null +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.stderr @@ -0,0 +1,113 @@ +error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:4:1 + | +LL | / struct RarelyUseful { +LL | | field: i32, +LL | | last: [usize; 0], +LL | | } + | |_^ + | + = note: `-D clippy::trailing-zero-sized-array-without-repr-c` implied by `-D warnings` +help: try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute): + | +LL + #[repr(C)] +LL + struct RarelyUseful { +LL + field: i32, +LL + last: [usize; 0], +LL + } + | + +error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:15:1 + | +LL | / struct OnlyFieldIsZeroSizeArray { +LL | | first_and_last: [usize; 0], +LL | | } + | |_^ + | +help: try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute): + | +LL + #[repr(C)] +LL + struct OnlyFieldIsZeroSizeArray { +LL + first_and_last: [usize; 0], +LL + } + | + +error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:19:1 + | +LL | / struct GenericArrayType { +LL | | field: i32, +LL | | last: [T; 0], +LL | | } + | |_^ + | +help: try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute): + | +LL + #[repr(C)] +LL + struct GenericArrayType { +LL + field: i32, +LL + last: [T; 0], +LL + } + | + +error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:30:1 + | +LL | / struct ZeroSizedFromExternalConst { +LL | | field: i32, +LL | | last: [usize; ZERO], +LL | | } + | |_^ + | +help: try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute): + | +LL + #[repr(C)] +LL + struct ZeroSizedFromExternalConst { +LL + field: i32, +LL + last: [usize; ZERO], +LL + } + | + +error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:45:1 + | +LL | / struct UsingFunction { +LL | | field: i32, +LL | | last: [usize; compute_zero()], +LL | | } + | |_^ + | +help: try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute): + | +LL + #[repr(C)] +LL + struct UsingFunction { +LL + field: i32, +LL + last: [usize; compute_zero()], +LL + } + | + +error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:94:1 + | +LL | / struct LotsOfFields { +LL | | f1: u32, +LL | | f2: u32, +LL | | f3: u32, +... | +LL | | last: [usize; 0], +LL | | } + | |_^ + | +help: try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute): + | +LL + #[repr(C)] +LL + struct LotsOfFields { +LL + f1: u32, +LL + f2: u32, +LL + f3: u32, +LL + f4: u32, + ... + +error: aborting due to 6 previous errors + From 003972f4281ab83fb56ce9a898efd93b6ca3740e Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Sat, 16 Oct 2021 15:26:10 -0700 Subject: [PATCH 060/101] add multiple `get_attrs` and `includes_repr` and they all work! --- clippy_lints/src/lib.rs | 1 + ...railing_zero_sized_array_without_repr_c.rs | 66 ++++++--- ...railing_zero_sized_array_without_repr_c.rs | 125 ++++++++++-------- 3 files changed, 123 insertions(+), 69 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d494892c3b4c6..72636146d7c3f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -21,6 +21,7 @@ // (Currently there is no way to opt into sysroot crates without `extern crate`.) extern crate rustc_ast; extern crate rustc_ast_pretty; +extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index 7f5798355129d..4c3c5191d2868 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -1,10 +1,14 @@ use clippy_utils::consts::{miri_to_const, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use rustc_ast::Attribute; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{Item, ItemKind, TyKind, VariantData}; +use rustc_hir::{Item, ItemKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::dep_graph::DepContext; +use rustc_middle::ty as ty_mod; +use rustc_middle::ty::ReprFlags; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -33,19 +37,16 @@ declare_clippy_lint! { /// ``` pub TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, nursery, - "struct with a trailing zero-sized array but without `repr(C)`" + "struct with a trailing zero-sized array but without `repr(C)` or another `repr` attribute" } declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C]); -// -// TODO: Register the lint pass in `clippy_lints/src/lib.rs`, -// e.g. store.register_early_pass(|| -// Box::new(trailing_zero_sized_array_without_repr_c::TrailingZeroSizedArrayWithoutReprC)); -// DONE! +// TESTNAME=trailing_zero_sized_array_without_repr_c cargo uitest impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_c(cx, item.def_id) { + dbg!(item.ident); + if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.def_id) { span_lint_and_sugg( cx, TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, @@ -64,7 +65,7 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx if let ItemKind::Struct(data, _generics) = &item.kind; if let VariantData::Struct(field_defs, _) = data; if let Some(last_field) = field_defs.last(); - if let TyKind::Array(_, aconst) = last_field.ty.kind; + if let rustc_hir::TyKind::Array(_, aconst) = last_field.ty.kind; let aconst_def_id = cx.tcx.hir().body_owner_def_id(aconst.body).to_def_id(); let ty = cx.tcx.type_of(aconst_def_id); let constant = cx @@ -83,17 +84,50 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx } } -fn has_repr_c(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool { +fn has_repr_attr(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool { + let attrs_get_attrs = get_attrs_get_attrs(cx, def_id); + let attrs_hir_map = get_attrs_hir_map(cx, def_id); + let b11 = dbg!(includes_repr_attr_using_sym(attrs_get_attrs)); + let b12 = dbg!(includes_repr_attr_using_sym(attrs_hir_map)); + let b21 = dbg!(includes_repr_attr_using_helper(cx, attrs_get_attrs)); + let b22 = dbg!(includes_repr_attr_using_helper(cx, attrs_hir_map)); + let b3 = dbg!(has_repr_attr_using_adt(cx, def_id)); + let all_same = b11 && b12 && b21 && b22 && b3; + dbg!(all_same); + + b11 +} + +fn get_attrs_get_attrs(cx: &LateContext<'tcx>, def_id: LocalDefId) -> &'tcx [Attribute] { + cx.tcx.get_attrs(def_id.to_def_id()) +} + +fn get_attrs_hir_map(cx: &LateContext<'tcx>, def_id: LocalDefId) -> &'tcx [Attribute] { let hir_map = cx.tcx.hir(); let hir_id = hir_map.local_def_id_to_hir_id(def_id); - let attrs = hir_map.attrs(hir_id); + hir_map.attrs(hir_id) +} - // NOTE: Can there ever be more than one `repr` attribute? - // other `repr` syms: repr, repr128, repr_align, repr_align_enum, repr_no_niche, repr_packed, - // repr_simd, repr_transparent - if let Some(_attr) = attrs.iter().find(|attr| attr.has_name(sym::repr)) { - true +// Don't like this because it's so dependent on the current list of `repr` flags and it would have to be manually updated if that ever expanded. idk if there's any mechanism in `bitflag!` or elsewhere for requiring that sort of exhaustiveness +fn has_repr_attr_using_adt(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool { + let ty = cx.tcx.type_of(def_id.to_def_id()); + if let ty_mod::Adt(adt, _) = ty.kind() { + if adt.is_struct() { + let repr = adt.repr; + let repr_attr = ReprFlags::IS_C | ReprFlags::IS_TRANSPARENT | ReprFlags::IS_SIMD | ReprFlags::IS_LINEAR; + repr.int.is_some() || repr.align.is_some() || repr.pack.is_some() || repr.flags.intersects(repr_attr) + } else { + false + } } else { false } } + +fn includes_repr_attr_using_sym(attrs: &'tcx [Attribute]) -> bool { + attrs.iter().any(|attr| attr.has_name(sym::repr)) +} + +fn includes_repr_attr_using_helper(cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) -> bool { + attrs.iter().any(|attr| !rustc_attr::find_repr_attrs(cx.tcx.sess(), attr).is_empty()) +} diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.rs b/tests/ui/trailing_zero_sized_array_without_repr_c.rs index 62fe94d7abf05..8e8c84fe9c5ba 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.rs @@ -1,13 +1,9 @@ #![warn(clippy::trailing_zero_sized_array_without_repr_c)] // #![feature(const_generics_defaults)] // see below -struct RarelyUseful { - field: i32, - last: [usize; 0], -} +// Do lint: -#[repr(C)] -struct GoodReason { +struct RarelyUseful { field: i32, last: [usize; 0], } @@ -21,24 +17,25 @@ struct GenericArrayType { last: [T; 0], } -struct SizedArray { +#[derive(Debug)] +struct PlayNiceWithOtherAttributesDerive { field: i32, - last: [usize; 1], + last: [usize; 0] } -const ZERO: usize = 0; -struct ZeroSizedFromExternalConst { +#[must_use] +struct PlayNiceWithOtherAttributesMustUse { field: i32, - last: [usize; ZERO], + last: [usize; 0] } -const ONE: usize = 1; -struct NonZeroSizedFromExternalConst { +const ZERO: usize = 0; +struct ZeroSizedFromExternalConst { field: i32, - last: [usize; ONE], + last: [usize; ZERO], } -#[allow(clippy::eq_op)] // lmao im impressed +#[allow(clippy::eq_op)] const fn compute_zero() -> usize { (4 + 6) - (2 * 5) } @@ -47,36 +44,62 @@ struct UsingFunction { last: [usize; compute_zero()], } -// NOTE: including these (along with the required feature) triggers an ICE. Should make sure the -// const generics people are aware of that if they weren't already. +struct LotsOfFields { + f1: u32, + f2: u32, + f3: u32, + f4: u32, + f5: u32, + f6: u32, + f7: u32, + f8: u32, + f9: u32, + f10: u32, + f11: u32, + f12: u32, + f13: u32, + f14: u32, + f15: u32, + f16: u32, + last: [usize; 0], +} -// #[repr(C)] -// struct ConstParamOk { -// field: i32, -// last: [usize; N] -// } +// Don't lint -// struct ConstParamLint { -// field: i32, -// last: [usize; N] -// } +#[repr(C)] +struct GoodReason { + field: i32, + last: [usize; 0], +} + +struct SizedArray { + field: i32, + last: [usize; 1], +} + +const ONE: usize = 1; +struct NonZeroSizedFromExternalConst { + field: i32, + last: [usize; ONE], +} -// TODO: actually, uh,, no idea what behavior here would be #[repr(packed)] struct ReprPacked { - small: u8, - medium: i32, - weird: [u64; 0], + field: i32, + last: [usize; 0], +} + +#[repr(C, packed)] +struct ReprCPacked { + field: i32, + last: [usize; 0], } -// TODO: clarify expected behavior #[repr(align(64))] struct ReprAlign { field: i32, last: [usize; 0], } - -// TODO: clarify expected behavior #[repr(C, align(64))] struct ReprCAlign { field: i32, @@ -91,24 +114,20 @@ enum DontLintAnonymousStructsFromDesuraging { C { x: u32, y: [u64; 0] }, } -struct LotsOfFields { - f1: u32, - f2: u32, - f3: u32, - f4: u32, - f5: u32, - f6: u32, - f7: u32, - f8: u32, - f9: u32, - f10: u32, - f11: u32, - f12: u32, - f13: u32, - f14: u32, - f15: u32, - f16: u32, - last: [usize; 0], -} +// NOTE: including these (along with the required feature) triggers an ICE. Should make sure the +// const generics people are aware of that if they weren't already. + +// #[repr(C)] +// struct ConstParamOk { +// field: i32, +// last: [usize; N] +// } -fn main() {} +// struct ConstParamLint { +// field: i32, +// last: [usize; N] +// } + +fn main() { + let _ = PlayNiceWithOtherAttributesMustUse {field: 0, last: []}; +} From 5fdf93415bfd59a7cd61ebec20f0a4e4fec78164 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Sat, 16 Oct 2021 16:13:14 -0700 Subject: [PATCH 061/101] intermediate step --- ...railing_zero_sized_array_without_repr_c.rs | 45 ++++++++++--------- ...railing_zero_sized_array_without_repr_c.rs | 14 +++--- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index 4c3c5191d2868..a9c6e24918e99 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -1,8 +1,6 @@ use clippy_utils::consts::{miri_to_const, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::Attribute; -use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; use rustc_hir::{Item, ItemKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; @@ -47,15 +45,15 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { dbg!(item.ident); if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.def_id) { - span_lint_and_sugg( - cx, - TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, - item.span, - "trailing zero-sized array in a struct which is not marked `#[repr(C)]`", - "try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute):", - format!("#[repr(C)]\n{}", snippet(cx, item.span, "..")), - Applicability::MaybeIncorrect, - ); + // span_lint_and_help( + // cx, + // TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, + // item.span, + // "trailing zero-sized array in a struct which is not marked `#[repr(C)]`", + // None, + // "consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute)", + // ); + eprintln!("— consider yourself linted — 🦀") } } } @@ -87,15 +85,16 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx fn has_repr_attr(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool { let attrs_get_attrs = get_attrs_get_attrs(cx, def_id); let attrs_hir_map = get_attrs_hir_map(cx, def_id); - let b11 = dbg!(includes_repr_attr_using_sym(attrs_get_attrs)); - let b12 = dbg!(includes_repr_attr_using_sym(attrs_hir_map)); - let b21 = dbg!(includes_repr_attr_using_helper(cx, attrs_get_attrs)); - let b22 = dbg!(includes_repr_attr_using_helper(cx, attrs_hir_map)); - let b3 = dbg!(has_repr_attr_using_adt(cx, def_id)); - let all_same = b11 && b12 && b21 && b22 && b3; + + let b11 = includes_repr_attr_using_sym(attrs_get_attrs); + let b12 = includes_repr_attr_using_sym(attrs_hir_map); + let b21 = includes_repr_attr_using_helper(cx, attrs_get_attrs); + let b22 = includes_repr_attr_using_helper(cx, attrs_hir_map); + let b3 = has_repr_attr_using_adt(cx, def_id); + let all_same = (b11 && b12 && b21 && b22 && b3) || (!b11 && !b12 && !b21 && !b22 && !b3); dbg!(all_same); - b11 + b21 } fn get_attrs_get_attrs(cx: &LateContext<'tcx>, def_id: LocalDefId) -> &'tcx [Attribute] { @@ -108,7 +107,9 @@ fn get_attrs_hir_map(cx: &LateContext<'tcx>, def_id: LocalDefId) -> &'tcx [Attri hir_map.attrs(hir_id) } -// Don't like this because it's so dependent on the current list of `repr` flags and it would have to be manually updated if that ever expanded. idk if there's any mechanism in `bitflag!` or elsewhere for requiring that sort of exhaustiveness +// Don't like this because it's so dependent on the current list of `repr` flags and it would have +// to be manually updated if that ever expanded. idk if there's any mechanism in `bitflag!` or +// elsewhere for requiring that sort of exhaustiveness fn has_repr_attr_using_adt(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool { let ty = cx.tcx.type_of(def_id.to_def_id()); if let ty_mod::Adt(adt, _) = ty.kind() { @@ -129,5 +130,7 @@ fn includes_repr_attr_using_sym(attrs: &'tcx [Attribute]) -> bool { } fn includes_repr_attr_using_helper(cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) -> bool { - attrs.iter().any(|attr| !rustc_attr::find_repr_attrs(cx.tcx.sess(), attr).is_empty()) + attrs + .iter() + .any(|attr| !rustc_attr::find_repr_attrs(cx.tcx.sess(), attr).is_empty()) } diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.rs b/tests/ui/trailing_zero_sized_array_without_repr_c.rs index 8e8c84fe9c5ba..07cba5774a5c4 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.rs @@ -1,5 +1,5 @@ #![warn(clippy::trailing_zero_sized_array_without_repr_c)] -// #![feature(const_generics_defaults)] // see below +#![feature(const_generics_defaults)] // see below // Do lint: @@ -20,13 +20,13 @@ struct GenericArrayType { #[derive(Debug)] struct PlayNiceWithOtherAttributesDerive { field: i32, - last: [usize; 0] + last: [usize; 0], } #[must_use] struct PlayNiceWithOtherAttributesMustUse { field: i32, - last: [usize; 0] + last: [usize; 0], } const ZERO: usize = 0; @@ -72,7 +72,7 @@ struct GoodReason { last: [usize; 0], } -struct SizedArray { +struct NonZeroSizedArray { field: i32, last: [usize; 1], } @@ -114,8 +114,8 @@ enum DontLintAnonymousStructsFromDesuraging { C { x: u32, y: [u64; 0] }, } -// NOTE: including these (along with the required feature) triggers an ICE. Should make sure the -// const generics people are aware of that if they weren't already. +// NOTE: including these (along with the required feature) triggers an ICE. Not sure why. Should +// make sure the const generics people are aware of that if they weren't already. // #[repr(C)] // struct ConstParamOk { @@ -129,5 +129,5 @@ enum DontLintAnonymousStructsFromDesuraging { // } fn main() { - let _ = PlayNiceWithOtherAttributesMustUse {field: 0, last: []}; + let _ = PlayNiceWithOtherAttributesMustUse { field: 0, last: [] }; } From 9b3f55ee61e781ef3360ddfaa436746bb7e40df5 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Sat, 16 Oct 2021 17:49:13 -0700 Subject: [PATCH 062/101] tried to simplify but it doesn't work :/ --- ...railing_zero_sized_array_without_repr_c.rs | 109 ++++++------------ ...railing_zero_sized_array_without_repr_c.rs | 34 ++++-- 2 files changed, 61 insertions(+), 82 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index a9c6e24918e99..913812126a9bd 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -1,14 +1,11 @@ use clippy_utils::consts::{miri_to_const, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::Attribute; -use rustc_hir::def_id::LocalDefId; use rustc_hir::{Item, ItemKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::dep_graph::DepContext; -use rustc_middle::ty as ty_mod; -use rustc_middle::ty::ReprFlags; +use rustc_middle::ty::Const; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -43,93 +40,55 @@ declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_AR impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - dbg!(item.ident); - if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.def_id) { - // span_lint_and_help( - // cx, - // TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, - // item.span, - // "trailing zero-sized array in a struct which is not marked `#[repr(C)]`", - // None, - // "consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute)", - // ); - eprintln!("— consider yourself linted — 🦀") + if is_struct_with_trailing_zero_sized_array(cx, item) { + let attrs = cx.tcx.get_attrs(item.def_id.to_def_id()); + let first_attr = attrs.first(); // Actually, I've no idea if this is guaranteed to be the first one in the source code. + + let lint_span = if let Some(first_attr) = first_attr { + first_attr.span.until(item.span) + } else { + item.span + }; + + if !has_repr_attr(cx, attrs) { + span_lint_and_help( + cx, + TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, + lint_span, + "trailing zero-sized array in a struct which is not marked `#[repr(C)]`", + None, + "consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute)", + ); + } } } } fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { if_chain! { - if let ItemKind::Struct(data, _generics) = &item.kind; + // Check if last field is an array + if let ItemKind::Struct(data, _) = &item.kind; if let VariantData::Struct(field_defs, _) = data; if let Some(last_field) = field_defs.last(); - if let rustc_hir::TyKind::Array(_, aconst) = last_field.ty.kind; - let aconst_def_id = cx.tcx.hir().body_owner_def_id(aconst.body).to_def_id(); - let ty = cx.tcx.type_of(aconst_def_id); - let constant = cx - .tcx - // NOTE: maybe const_eval_resolve? - .const_eval_poly(aconst_def_id) - .ok() - .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); - if let Some(Constant::Int(val)) = constant.and_then(miri_to_const); - if val == 0; - then { - true - } else { - false - } - } -} - -fn has_repr_attr(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool { - let attrs_get_attrs = get_attrs_get_attrs(cx, def_id); - let attrs_hir_map = get_attrs_hir_map(cx, def_id); + if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind; - let b11 = includes_repr_attr_using_sym(attrs_get_attrs); - let b12 = includes_repr_attr_using_sym(attrs_hir_map); - let b21 = includes_repr_attr_using_helper(cx, attrs_get_attrs); - let b22 = includes_repr_attr_using_helper(cx, attrs_hir_map); - let b3 = has_repr_attr_using_adt(cx, def_id); - let all_same = (b11 && b12 && b21 && b22 && b3) || (!b11 && !b12 && !b21 && !b22 && !b3); - dbg!(all_same); + // Check if that that array zero-sized. + let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); + let length = Const::from_anon_const(cx.tcx, length_ldid); + if let Some(Constant::Int(length)) = miri_to_const(length); + if length == 0; - b21 -} - -fn get_attrs_get_attrs(cx: &LateContext<'tcx>, def_id: LocalDefId) -> &'tcx [Attribute] { - cx.tcx.get_attrs(def_id.to_def_id()) -} - -fn get_attrs_hir_map(cx: &LateContext<'tcx>, def_id: LocalDefId) -> &'tcx [Attribute] { - let hir_map = cx.tcx.hir(); - let hir_id = hir_map.local_def_id_to_hir_id(def_id); - hir_map.attrs(hir_id) -} - -// Don't like this because it's so dependent on the current list of `repr` flags and it would have -// to be manually updated if that ever expanded. idk if there's any mechanism in `bitflag!` or -// elsewhere for requiring that sort of exhaustiveness -fn has_repr_attr_using_adt(cx: &LateContext<'tcx>, def_id: LocalDefId) -> bool { - let ty = cx.tcx.type_of(def_id.to_def_id()); - if let ty_mod::Adt(adt, _) = ty.kind() { - if adt.is_struct() { - let repr = adt.repr; - let repr_attr = ReprFlags::IS_C | ReprFlags::IS_TRANSPARENT | ReprFlags::IS_SIMD | ReprFlags::IS_LINEAR; - repr.int.is_some() || repr.align.is_some() || repr.pack.is_some() || repr.flags.intersects(repr_attr) + then { + true } else { false } - } else { - false } } -fn includes_repr_attr_using_sym(attrs: &'tcx [Attribute]) -> bool { - attrs.iter().any(|attr| attr.has_name(sym::repr)) -} - -fn includes_repr_attr_using_helper(cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) -> bool { +fn has_repr_attr(cx: &LateContext<'tcx>, attrs: &[Attribute]) -> bool { + // NOTE: there's at least four other ways to do this but I liked this one the best. (All five agreed + // on all testcases.) Happy to use another; they're in the commit history. attrs .iter() .any(|attr| !rustc_attr::find_repr_attrs(cx.tcx.sess(), attr).is_empty()) diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.rs b/tests/ui/trailing_zero_sized_array_without_repr_c.rs index 07cba5774a5c4..6ab96c2ebf637 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.rs @@ -8,7 +8,7 @@ struct RarelyUseful { last: [usize; 0], } -struct OnlyFieldIsZeroSizeArray { +struct OnlyField { first_and_last: [usize; 0], } @@ -18,19 +18,19 @@ struct GenericArrayType { } #[derive(Debug)] -struct PlayNiceWithOtherAttributesDerive { +struct OnlyAnotherAttributeDerive { field: i32, last: [usize; 0], } #[must_use] -struct PlayNiceWithOtherAttributesMustUse { +struct OnlyAnotherAttributeMustUse { field: i32, last: [usize; 0], } const ZERO: usize = 0; -struct ZeroSizedFromExternalConst { +struct ZeroSizedWithConst { field: i32, last: [usize; ZERO], } @@ -39,7 +39,7 @@ struct ZeroSizedFromExternalConst { const fn compute_zero() -> usize { (4 + 6) - (2 * 5) } -struct UsingFunction { +struct ZeroSizedWithConstFunction { field: i32, last: [usize; compute_zero()], } @@ -72,17 +72,36 @@ struct GoodReason { last: [usize; 0], } +#[repr(C)] +struct OnlyFieldWithReprC { + first_and_last: [usize; 0], +} + struct NonZeroSizedArray { field: i32, last: [usize; 1], } const ONE: usize = 1; -struct NonZeroSizedFromExternalConst { +struct NonZeroSizedWithConst { field: i32, last: [usize; ONE], } +#[derive(Debug)] +#[repr(C)] +struct OtherAttributesDerive { + field: i32, + last: [usize; 0], +} + +#[must_use] +#[repr(C)] +struct OtherAttributesMustUse { + field: i32, + last: [usize; 0], +} + #[repr(packed)] struct ReprPacked { field: i32, @@ -129,5 +148,6 @@ enum DontLintAnonymousStructsFromDesuraging { // } fn main() { - let _ = PlayNiceWithOtherAttributesMustUse { field: 0, last: [] }; + let _ = OnlyAnotherAttributeMustUse { field: 0, last: [] }; + let _ = OtherAttributesMustUse { field: 0, last: [] }; } From a3420f70043c19165a4e44639ef4e6f3c156a174 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Sat, 16 Oct 2021 22:03:08 -0700 Subject: [PATCH 063/101] Tidy comments + tests; revert 'size-is-zero' detection --- ...railing_zero_sized_array_without_repr_c.rs | 35 ++++--- ...railing_zero_sized_array_without_repr_c.rs | 36 +++----- ...ing_zero_sized_array_without_repr_c.stderr | 91 ++++++++----------- 3 files changed, 72 insertions(+), 90 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index 913812126a9bd..bc055307d5efe 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -36,16 +36,15 @@ declare_clippy_lint! { } declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C]); -// TESTNAME=trailing_zero_sized_array_without_repr_c cargo uitest - impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if is_struct_with_trailing_zero_sized_array(cx, item) { + // NOTE: This is to include attributes on the definition when we print the lint. If the convention + // is to not do that with struct definitions (I'm not sure), then this isn't necessary. let attrs = cx.tcx.get_attrs(item.def_id.to_def_id()); - let first_attr = attrs.first(); // Actually, I've no idea if this is guaranteed to be the first one in the source code. - + let first_attr = attrs.iter().min_by_key(|attr| attr.span.lo()); let lint_span = if let Some(first_attr) = first_attr { - first_attr.span.until(item.span) + first_attr.span.to(item.span) } else { item.span }; @@ -66,18 +65,29 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { if_chain! { - // Check if last field is an array + // First check if last field is an array if let ItemKind::Struct(data, _) = &item.kind; if let VariantData::Struct(field_defs, _) = data; if let Some(last_field) = field_defs.last(); if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind; - // Check if that that array zero-sized. - let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); - let length = Const::from_anon_const(cx.tcx, length_ldid); - if let Some(Constant::Int(length)) = miri_to_const(length); - if length == 0; + // Then check if that that array zero-sized + // This is pretty much copied from `enum_clike.rs` and I don't fully understand it, so let me know + // if there's a better way. I tried `Const::from_anon_const` but it didn't fold in the values + // on the `ZeroSizedWithConst` and `ZeroSizedWithConstFunction` tests. + + // This line in particular seems convoluted. + let length_did = cx.tcx.hir().body_owner_def_id(length.body).to_def_id(); + let length_ty = cx.tcx.type_of(length_did); + let length = cx + .tcx + .const_eval_poly(length_did) + .ok() + .map(|val| Const::from_value(cx.tcx, val, length_ty)) + .and_then(miri_to_const); + if let Some(Constant::Int(length)) = length; + if length == 0; then { true } else { @@ -88,7 +98,8 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx fn has_repr_attr(cx: &LateContext<'tcx>, attrs: &[Attribute]) -> bool { // NOTE: there's at least four other ways to do this but I liked this one the best. (All five agreed - // on all testcases.) Happy to use another; they're in the commit history. + // on all testcases.) Happy to use another; they're in the commit history if you want to look (or I + // can go find them). attrs .iter() .any(|attr| !rustc_attr::find_repr_attrs(cx.tcx.sess(), attr).is_empty()) diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.rs b/tests/ui/trailing_zero_sized_array_without_repr_c.rs index 6ab96c2ebf637..77b2c29b275b9 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.rs @@ -1,5 +1,4 @@ #![warn(clippy::trailing_zero_sized_array_without_repr_c)] -#![feature(const_generics_defaults)] // see below // Do lint: @@ -17,14 +16,16 @@ struct GenericArrayType { last: [T; 0], } -#[derive(Debug)] -struct OnlyAnotherAttributeDerive { +#[must_use] +struct OnlyAnotherAttributeMustUse { field: i32, last: [usize; 0], } -#[must_use] -struct OnlyAnotherAttributeMustUse { +// NOTE: Unfortunately the attribute isn't included in the lint output. I'm not sure how to make it +// show up. +#[derive(Debug)] +struct OnlyAnotherAttributeDerive { field: i32, last: [usize; 0], } @@ -82,6 +83,12 @@ struct NonZeroSizedArray { last: [usize; 1], } +struct NotLastField { + f1: u32, + zero_sized: [usize; 0], + last: i32, +} + const ONE: usize = 1; struct NonZeroSizedWithConst { field: i32, @@ -133,21 +140,4 @@ enum DontLintAnonymousStructsFromDesuraging { C { x: u32, y: [u64; 0] }, } -// NOTE: including these (along with the required feature) triggers an ICE. Not sure why. Should -// make sure the const generics people are aware of that if they weren't already. - -// #[repr(C)] -// struct ConstParamOk { -// field: i32, -// last: [usize; N] -// } - -// struct ConstParamLint { -// field: i32, -// last: [usize; N] -// } - -fn main() { - let _ = OnlyAnotherAttributeMustUse { field: 0, last: [] }; - let _ = OtherAttributesMustUse { field: 0, last: [] }; -} +fn main() {} diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.stderr b/tests/ui/trailing_zero_sized_array_without_repr_c.stderr index 84606ed618588..ee8182cdc3807 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.stderr +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.stderr @@ -1,5 +1,5 @@ error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:4:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:5:1 | LL | / struct RarelyUseful { LL | | field: i32, @@ -8,33 +8,20 @@ LL | | } | |_^ | = note: `-D clippy::trailing-zero-sized-array-without-repr-c` implied by `-D warnings` -help: try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute): - | -LL + #[repr(C)] -LL + struct RarelyUseful { -LL + field: i32, -LL + last: [usize; 0], -LL + } - | + = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:15:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:10:1 | -LL | / struct OnlyFieldIsZeroSizeArray { +LL | / struct OnlyField { LL | | first_and_last: [usize; 0], LL | | } | |_^ | -help: try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute): - | -LL + #[repr(C)] -LL + struct OnlyFieldIsZeroSizeArray { -LL + first_and_last: [usize; 0], -LL + } - | + = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:19:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:14:1 | LL | / struct GenericArrayType { LL | | field: i32, @@ -42,53 +29,55 @@ LL | | last: [T; 0], LL | | } | |_^ | -help: try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute): + = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) + +error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:19:1 | -LL + #[repr(C)] -LL + struct GenericArrayType { -LL + field: i32, -LL + last: [T; 0], -LL + } +LL | / #[must_use] +LL | | struct OnlyAnotherAttributeMustUse { +LL | | field: i32, +LL | | last: [usize; 0], +LL | | } + | |_^ | + = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:30:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:28:1 | -LL | / struct ZeroSizedFromExternalConst { +LL | / struct OnlyAnotherAttributeDerive { LL | | field: i32, -LL | | last: [usize; ZERO], +LL | | last: [usize; 0], LL | | } | |_^ | -help: try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute): + = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) + +error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:34:1 | -LL + #[repr(C)] -LL + struct ZeroSizedFromExternalConst { -LL + field: i32, -LL + last: [usize; ZERO], -LL + } +LL | / struct ZeroSizedWithConst { +LL | | field: i32, +LL | | last: [usize; ZERO], +LL | | } + | |_^ | + = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:45:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:43:1 | -LL | / struct UsingFunction { +LL | / struct ZeroSizedWithConstFunction { LL | | field: i32, LL | | last: [usize; compute_zero()], LL | | } | |_^ | -help: try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute): - | -LL + #[repr(C)] -LL + struct UsingFunction { -LL + field: i32, -LL + last: [usize; compute_zero()], -LL + } - | + = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:94:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:48:1 | LL | / struct LotsOfFields { LL | | f1: u32, @@ -99,15 +88,7 @@ LL | | last: [usize; 0], LL | | } | |_^ | -help: try annotating the struct definition with `#[repr(C)]` (or another `repr` attribute): - | -LL + #[repr(C)] -LL + struct LotsOfFields { -LL + f1: u32, -LL + f2: u32, -LL + f3: u32, -LL + f4: u32, - ... + = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors From c5d3167a23e7f1f6515a28ff15a6698b6712ae54 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Sun, 17 Oct 2021 17:28:45 -0700 Subject: [PATCH 064/101] update testsuite and expand `if_chain` --- ...railing_zero_sized_array_without_repr_c.rs | 55 +++++++++++-------- ...railing_zero_sized_array_without_repr_c.rs | 32 +++++++++++ 2 files changed, 63 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index bc055307d5efe..de2513244da61 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -64,35 +64,42 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { } fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { - if_chain! { - // First check if last field is an array - if let ItemKind::Struct(data, _) = &item.kind; - if let VariantData::Struct(field_defs, _) = data; - if let Some(last_field) = field_defs.last(); - if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind; + // First check if last field is an array + if let ItemKind::Struct(data, _) = &item.kind { + if let VariantData::Struct(field_defs, _) = data { + if let Some(last_field) = field_defs.last() { + if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind { + // Then check if that that array zero-sized - // Then check if that that array zero-sized + // This is pretty much copied from `enum_clike.rs` and I don't fully understand it, so let me know + // if there's a better way. I tried `Const::from_anon_const` but it didn't fold in the values + // on the `ZeroSizedWithConst` and `ZeroSizedWithConstFunction` tests. - // This is pretty much copied from `enum_clike.rs` and I don't fully understand it, so let me know - // if there's a better way. I tried `Const::from_anon_const` but it didn't fold in the values - // on the `ZeroSizedWithConst` and `ZeroSizedWithConstFunction` tests. - - // This line in particular seems convoluted. - let length_did = cx.tcx.hir().body_owner_def_id(length.body).to_def_id(); - let length_ty = cx.tcx.type_of(length_did); - let length = cx - .tcx - .const_eval_poly(length_did) - .ok() - .map(|val| Const::from_value(cx.tcx, val, length_ty)) - .and_then(miri_to_const); - if let Some(Constant::Int(length)) = length; - if length == 0; - then { - true + // This line in particular seems convoluted. + let length_did = cx.tcx.hir().body_owner_def_id(length.body).to_def_id(); + let length_ty = cx.tcx.type_of(length_did); + let length = cx + .tcx + .const_eval_poly(length_did) + .ok() + .map(|val| Const::from_value(cx.tcx, val, length_ty)) + .and_then(miri_to_const); + if let Some(Constant::Int(length)) = length { + length == 0 + } else { + false + } + } else { + false + } + } else { + false + } } else { false } + } else { + false } } diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.rs b/tests/ui/trailing_zero_sized_array_without_repr_c.rs index 77b2c29b275b9..c6ee36e9685c9 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.rs @@ -1,4 +1,5 @@ #![warn(clippy::trailing_zero_sized_array_without_repr_c)] +#![feature(const_generics_defaults)] // Do lint: @@ -45,6 +46,10 @@ struct ZeroSizedWithConstFunction { last: [usize; compute_zero()], } +struct ZeroSizedArrayWrapper([usize; 0]); + +struct TupleStruct(i32, [usize; 0]); + struct LotsOfFields { f1: u32, f2: u32, @@ -140,4 +145,31 @@ enum DontLintAnonymousStructsFromDesuraging { C { x: u32, y: [u64; 0] }, } +#[repr(C)] +struct TupleStructReprC(i32, [usize; 0]); + +type NamedTuple = (i32, [usize; 0]); + +#[rustfmt::skip] // [rustfmt#4995](https://github.com/rust-lang/rustfmt/issues/4995) +struct ConstParamZeroDefault { + field: i32, + last: [usize; N], +} + +struct ConstParamNoDefault { + field: i32, + last: [usize; N], +} + +#[rustfmt::skip] +struct ConstParamNonZeroDefault { + field: i32, + last: [usize; N], +} + +type A = ConstParamZeroDefault; +type B = ConstParamZeroDefault<0>; +type C = ConstParamNoDefault<0>; +type D = ConstParamNonZeroDefault<0>; + fn main() {} From 2a5a4f07cf34895cf6d3c6f8843169e3d6d9c46d Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 00:51:30 -0700 Subject: [PATCH 065/101] =?UTF-8?q?Refactor=20ZS=20array=20detection=20aga?= =?UTF-8?q?in=20and=20this=20one=20seems=20great=20=F0=9F=91=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...railing_zero_sized_array_without_repr_c.rs | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index de2513244da61..d68ad90170443 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{miri_to_const, Constant}; +use clippy_utils::consts::{constant, miri_to_const, ConstEvalLateContext, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::Attribute; use rustc_hir::{Item, ItemKind, VariantData}; @@ -38,9 +38,11 @@ declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_AR impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + dbg!(item.ident); if is_struct_with_trailing_zero_sized_array(cx, item) { // NOTE: This is to include attributes on the definition when we print the lint. If the convention - // is to not do that with struct definitions (I'm not sure), then this isn't necessary. + // is to not do that with struct definitions (I'm not sure), then this isn't necessary. (note: if + // you don't get rid of this, change `has_repr_attr` to `includes_repr_attr`). let attrs = cx.tcx.get_attrs(item.def_id.to_def_id()); let first_attr = attrs.iter().min_by_key(|attr| attr.span.lo()); let lint_span = if let Some(first_attr) = first_attr { @@ -70,21 +72,11 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx if let Some(last_field) = field_defs.last() { if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind { // Then check if that that array zero-sized - - // This is pretty much copied from `enum_clike.rs` and I don't fully understand it, so let me know - // if there's a better way. I tried `Const::from_anon_const` but it didn't fold in the values - // on the `ZeroSizedWithConst` and `ZeroSizedWithConstFunction` tests. - - // This line in particular seems convoluted. - let length_did = cx.tcx.hir().body_owner_def_id(length.body).to_def_id(); - let length_ty = cx.tcx.type_of(length_did); - let length = cx - .tcx - .const_eval_poly(length_did) - .ok() - .map(|val| Const::from_value(cx.tcx, val, length_ty)) - .and_then(miri_to_const); - if let Some(Constant::Int(length)) = length { + let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); + let length = Const::from_anon_const(cx.tcx, length_ldid); + let length = length.try_eval_usize(cx.tcx, cx.param_env); + // if let Some((Constant::Int(length), _)) = length { + if let Some(length) = length { length == 0 } else { false From 9f402b370c43f8cbad15d477d29aa96ff9746de5 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:03:48 -0700 Subject: [PATCH 066/101] Check for tuple structs --- ...railing_zero_sized_array_without_repr_c.rs | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index d68ad90170443..9373551db1599 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -38,7 +38,6 @@ declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_AR impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - dbg!(item.ident); if is_struct_with_trailing_zero_sized_array(cx, item) { // NOTE: This is to include attributes on the definition when we print the lint. If the convention // is to not do that with struct definitions (I'm not sure), then this isn't necessary. (note: if @@ -66,24 +65,18 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { } fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { + // TODO: when finalized, replace with an `if_chain`. I have it like this because my rust-analyzer doesn't work when it's an `if_chain` // First check if last field is an array if let ItemKind::Struct(data, _) = &item.kind { - if let VariantData::Struct(field_defs, _) = data { - if let Some(last_field) = field_defs.last() { - if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind { - // Then check if that that array zero-sized - let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); - let length = Const::from_anon_const(cx.tcx, length_ldid); - let length = length.try_eval_usize(cx.tcx, cx.param_env); - // if let Some((Constant::Int(length), _)) = length { - if let Some(length) = length { - length == 0 - } else { - false - } - } else { - false - } + let field_defs = data.fields(); + if let Some(last_field) = field_defs.last() { + if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind { + // Then check if that that array zero-sized + let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); + let length = Const::from_anon_const(cx.tcx, length_ldid); + let length = length.try_eval_usize(cx.tcx, cx.param_env); + // if let Some((Constant::Int(length), _)) = length { + if let Some(length) = length { length == 0 } else { false } } else { false } From cd6862283ee603432f509510c53f35bda12e3a5a Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:04:50 -0700 Subject: [PATCH 067/101] Tidy imports --- .../src/trailing_zero_sized_array_without_repr_c.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index 9373551db1599..a759fff9cfcb6 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -1,7 +1,7 @@ -use clippy_utils::consts::{constant, miri_to_const, ConstEvalLateContext, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::Attribute; -use rustc_hir::{Item, ItemKind, VariantData}; +use rustc_hir::VariantData; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::Const; @@ -65,8 +65,8 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { } fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { - // TODO: when finalized, replace with an `if_chain`. I have it like this because my rust-analyzer doesn't work when it's an `if_chain` - // First check if last field is an array + // TODO: when finalized, replace with an `if_chain`. I have it like this because my rust-analyzer + // doesn't work when it's an `if_chain` First check if last field is an array if let ItemKind::Struct(data, _) = &item.kind { let field_defs = data.fields(); if let Some(last_field) = field_defs.last() { From 6377fb2fe71a8bde91a20fbadc93242e19fd0dee Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:13:48 -0700 Subject: [PATCH 068/101] Tidy import + update expected stderr --- ...railing_zero_sized_array_without_repr_c.rs | 3 +- ...ing_zero_sized_array_without_repr_c.stderr | 34 ++++++++++++++----- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index a759fff9cfcb6..aea2fb208dfab 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::Attribute; -use rustc_hir::VariantData; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::dep_graph::DepContext; @@ -90,7 +89,7 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx fn has_repr_attr(cx: &LateContext<'tcx>, attrs: &[Attribute]) -> bool { // NOTE: there's at least four other ways to do this but I liked this one the best. (All five agreed - // on all testcases.) Happy to use another; they're in the commit history if you want to look (or I + // on all testcases (when i wrote this comment. I added a few since then).) Happy to use another; they're in the commit history if you want to look (or I // can go find them). attrs .iter() diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.stderr b/tests/ui/trailing_zero_sized_array_without_repr_c.stderr index ee8182cdc3807..5ed91e937d595 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.stderr +++ b/tests/ui/trailing_zero_sized_array_without_repr_c.stderr @@ -1,5 +1,5 @@ error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:5:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:6:1 | LL | / struct RarelyUseful { LL | | field: i32, @@ -11,7 +11,7 @@ LL | | } = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:10:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:11:1 | LL | / struct OnlyField { LL | | first_and_last: [usize; 0], @@ -21,7 +21,7 @@ LL | | } = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:14:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:15:1 | LL | / struct GenericArrayType { LL | | field: i32, @@ -32,7 +32,7 @@ LL | | } = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:19:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:20:1 | LL | / #[must_use] LL | | struct OnlyAnotherAttributeMustUse { @@ -44,7 +44,7 @@ LL | | } = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:28:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:29:1 | LL | / struct OnlyAnotherAttributeDerive { LL | | field: i32, @@ -55,7 +55,7 @@ LL | | } = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:34:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:35:1 | LL | / struct ZeroSizedWithConst { LL | | field: i32, @@ -66,7 +66,7 @@ LL | | } = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:43:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:44:1 | LL | / struct ZeroSizedWithConstFunction { LL | | field: i32, @@ -77,7 +77,23 @@ LL | | } = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:48:1 + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:49:1 + | +LL | struct ZeroSizedArrayWrapper([usize; 0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) + +error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:51:1 + | +LL | struct TupleStruct(i32, [usize; 0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) + +error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` + --> $DIR/trailing_zero_sized_array_without_repr_c.rs:53:1 | LL | / struct LotsOfFields { LL | | f1: u32, @@ -90,5 +106,5 @@ LL | | } | = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) -error: aborting due to 8 previous errors +error: aborting due to 10 previous errors From 149b3728732169cbd6684dfb5e17242924a506a7 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:16:10 -0700 Subject: [PATCH 069/101] run rustfmt --- clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs index aea2fb208dfab..6cba18146b14a 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs @@ -89,8 +89,8 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx fn has_repr_attr(cx: &LateContext<'tcx>, attrs: &[Attribute]) -> bool { // NOTE: there's at least four other ways to do this but I liked this one the best. (All five agreed - // on all testcases (when i wrote this comment. I added a few since then).) Happy to use another; they're in the commit history if you want to look (or I - // can go find them). + // on all testcases (when i wrote this comment. I added a few since then).) Happy to use another; + // they're in the commit history if you want to look (or I can go find them). attrs .iter() .any(|attr| !rustc_attr::find_repr_attrs(cx.tcx.sess(), attr).is_empty()) From 7f84e3d791846fb462eb39d05bbec029b68d97af Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:45:08 -0700 Subject: [PATCH 070/101] Rename lint --- CHANGELOG.md | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_nursery.rs | 2 +- clippy_lints/src/lib.rs | 4 ++-- ...trailing_zero_sized_array_without_repr.rs} | 22 +++++++++---------- ...trailing_zero_sized_array_without_repr.rs} | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) rename clippy_lints/src/{trailing_zero_sized_array_without_repr_c.rs => trailing_zero_sized_array_without_repr.rs} (81%) rename tests/ui/{trailing_zero_sized_array_without_repr_c.rs => trailing_zero_sized_array_without_repr.rs} (98%) diff --git a/CHANGELOG.md b/CHANGELOG.md index a124bebe92443..0cf19a6f0f169 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3021,7 +3021,7 @@ Released 2018-09-13 [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines [`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg -[`trailing_zero_sized_array_without_repr_c`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_zero_sized_array_without_repr_c +[`trailing_zero_sized_array_without_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_zero_sized_array_without_repr [`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds [`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str [`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 34b87002fa36f..af0629beb36d8 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -443,7 +443,7 @@ store.register_lints(&[ temporary_assignment::TEMPORARY_ASSIGNMENT, to_digit_is_some::TO_DIGIT_IS_SOME, to_string_in_display::TO_STRING_IN_DISPLAY, - trailing_zero_sized_array_without_repr_c::TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, + trailing_zero_sized_array_without_repr::TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, trait_bounds::TYPE_REPETITION_IN_BOUNDS, transmute::CROSSPOINTER_TRANSMUTE, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 325706746db58..eab0e5b90f11c 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -25,7 +25,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(regex::TRIVIAL_REGEX), LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), - LintId::of(trailing_zero_sized_array_without_repr_c::TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C), + LintId::of(trailing_zero_sized_array_without_repr::TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), ]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 72636146d7c3f..1473a2f3095d0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -356,7 +356,7 @@ mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; mod to_string_in_display; -mod trailing_zero_sized_array_without_repr_c; +mod trailing_zero_sized_array_without_repr; mod trait_bounds; mod transmute; mod transmuting_null; @@ -779,7 +779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default())); 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_zero_sized_array_without_repr_c::TrailingZeroSizedArrayWithoutReprC)); + store.register_late_pass(|| Box::new(trailing_zero_sized_array_without_repr::TrailingZeroSizedArrayWithoutRepr)); } diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs similarity index 81% rename from clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs rename to clippy_lints/src/trailing_zero_sized_array_without_repr.rs index 6cba18146b14a..0267f7bdf0e9b 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr_c.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs @@ -8,10 +8,10 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// Displays a warning when a struct with a trailing zero-sized array is declared without the `repr(C)` attribute. + /// Displays a warning when a struct with a trailing zero-sized array is declared without a `repr` attribute. /// /// ### Why is this bad? - /// Zero-sized arrays aren't very useful in Rust itself, so such a struct is likely being created to pass to C code (or in conjuction with manual allocation to make it easy to compute the offset of the array). Either way, `#[repr(C)]` is needed. + /// Zero-sized arrays aren't very useful in Rust itself, so such a struct is likely being created to pass to C code or in some other situation where control over memory layout matters (for example, in conjuction with manual allocation to make it easy to compute the offset of the array). Either way, `#[repr(C)]` (or another `repr` attribute) is needed. /// /// ### Example /// ```rust @@ -29,13 +29,13 @@ declare_clippy_lint! { /// last: [SomeType; 0], /// } /// ``` - pub TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, + pub TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, nursery, "struct with a trailing zero-sized array but without `repr(C)` or another `repr` attribute" } -declare_lint_pass!(TrailingZeroSizedArrayWithoutReprC => [TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C]); +declare_lint_pass!(TrailingZeroSizedArrayWithoutRepr => [TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR]); -impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { +impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutRepr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if is_struct_with_trailing_zero_sized_array(cx, item) { // NOTE: This is to include attributes on the definition when we print the lint. If the convention @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { if !has_repr_attr(cx, attrs) { span_lint_and_help( cx, - TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR_C, + TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, lint_span, "trailing zero-sized array in a struct which is not marked `#[repr(C)]`", None, @@ -65,17 +65,17 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutReprC { fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { // TODO: when finalized, replace with an `if_chain`. I have it like this because my rust-analyzer - // doesn't work when it's an `if_chain` First check if last field is an array + // doesn't work when it's an `if_chain`. + + // First check if last field is an array if let ItemKind::Struct(data, _) = &item.kind { - let field_defs = data.fields(); - if let Some(last_field) = field_defs.last() { + if let Some(last_field) = data.fields().last() { if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind { // Then check if that that array zero-sized let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); let length = Const::from_anon_const(cx.tcx, length_ldid); let length = length.try_eval_usize(cx.tcx, cx.param_env); - // if let Some((Constant::Int(length), _)) = length { - if let Some(length) = length { length == 0 } else { false } + length == Some(0) } else { false } diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.rs b/tests/ui/trailing_zero_sized_array_without_repr.rs similarity index 98% rename from tests/ui/trailing_zero_sized_array_without_repr_c.rs rename to tests/ui/trailing_zero_sized_array_without_repr.rs index c6ee36e9685c9..6ac124c8b34d5 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr.rs @@ -1,4 +1,4 @@ -#![warn(clippy::trailing_zero_sized_array_without_repr_c)] +#![warn(clippy::trailing_zero_sized_array_without_repr)] #![feature(const_generics_defaults)] // Do lint: From a6aa9864a339783e2e8400053408a197b607301d Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:52:37 -0700 Subject: [PATCH 071/101] Rename stderr --- ...iling_zero_sized_array_without_repr.stderr | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 tests/ui/trailing_zero_sized_array_without_repr.stderr diff --git a/tests/ui/trailing_zero_sized_array_without_repr.stderr b/tests/ui/trailing_zero_sized_array_without_repr.stderr new file mode 100644 index 0000000000000..54b36ec6cecb9 --- /dev/null +++ b/tests/ui/trailing_zero_sized_array_without_repr.stderr @@ -0,0 +1,110 @@ +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_zero_sized_array_without_repr.rs:6:1 + | +LL | / struct RarelyUseful { +LL | | field: i32, +LL | | last: [usize; 0], +LL | | } + | |_^ + | + = note: `-D clippy::trailing-zero-sized-array-without-repr` implied by `-D warnings` + = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_zero_sized_array_without_repr.rs:11:1 + | +LL | / struct OnlyField { +LL | | first_and_last: [usize; 0], +LL | | } + | |_^ + | + = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_zero_sized_array_without_repr.rs:15:1 + | +LL | / struct GenericArrayType { +LL | | field: i32, +LL | | last: [T; 0], +LL | | } + | |_^ + | + = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_zero_sized_array_without_repr.rs:20:1 + | +LL | / #[must_use] +LL | | struct OnlyAnotherAttributeMustUse { +LL | | field: i32, +LL | | last: [usize; 0], +LL | | } + | |_^ + | + = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_zero_sized_array_without_repr.rs:29:1 + | +LL | / struct OnlyAnotherAttributeDerive { +LL | | field: i32, +LL | | last: [usize; 0], +LL | | } + | |_^ + | + = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_zero_sized_array_without_repr.rs:35:1 + | +LL | / struct ZeroSizedWithConst { +LL | | field: i32, +LL | | last: [usize; ZERO], +LL | | } + | |_^ + | + = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_zero_sized_array_without_repr.rs:44:1 + | +LL | / struct ZeroSizedWithConstFunction { +LL | | field: i32, +LL | | last: [usize; compute_zero()], +LL | | } + | |_^ + | + = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_zero_sized_array_without_repr.rs:49:1 + | +LL | struct ZeroSizedArrayWrapper([usize; 0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_zero_sized_array_without_repr.rs:51:1 + | +LL | struct TupleStruct(i32, [usize; 0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_zero_sized_array_without_repr.rs:53:1 + | +LL | / struct LotsOfFields { +LL | | f1: u32, +LL | | f2: u32, +LL | | f3: u32, +... | +LL | | last: [usize; 0], +LL | | } + | |_^ + | + = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + +error: aborting due to 10 previous errors + From a54dbf6445f923b6b315a4c9b08f3e45eaac0081 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:52:57 -0700 Subject: [PATCH 072/101] Improve doc and span messages --- clippy_lints/src/trailing_zero_sized_array_without_repr.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs index 0267f7bdf0e9b..98f5073681d46 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs @@ -31,7 +31,7 @@ declare_clippy_lint! { /// ``` pub TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, nursery, - "struct with a trailing zero-sized array but without `repr(C)` or another `repr` attribute" + "struct with a trailing zero-sized array but without `#[repr(C)]` or another `repr` attribute" } declare_lint_pass!(TrailingZeroSizedArrayWithoutRepr => [TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR]); @@ -54,9 +54,9 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutRepr { cx, TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, lint_span, - "trailing zero-sized array in a struct which is not marked `#[repr(C)]`", + "trailing zero-sized array in a struct which is not marked with a `repr` attribute", None, - "consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute)", + "consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute", ); } } From 5b78907be7b67cfb598301e20a9ebd500a494c41 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:56:49 -0700 Subject: [PATCH 073/101] Still renaming lmao --- ...ing_zero_sized_array_without_repr_c.stderr | 110 ------------------ 1 file changed, 110 deletions(-) delete mode 100644 tests/ui/trailing_zero_sized_array_without_repr_c.stderr diff --git a/tests/ui/trailing_zero_sized_array_without_repr_c.stderr b/tests/ui/trailing_zero_sized_array_without_repr_c.stderr deleted file mode 100644 index 5ed91e937d595..0000000000000 --- a/tests/ui/trailing_zero_sized_array_without_repr_c.stderr +++ /dev/null @@ -1,110 +0,0 @@ -error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:6:1 - | -LL | / struct RarelyUseful { -LL | | field: i32, -LL | | last: [usize; 0], -LL | | } - | |_^ - | - = note: `-D clippy::trailing-zero-sized-array-without-repr-c` implied by `-D warnings` - = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) - -error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:11:1 - | -LL | / struct OnlyField { -LL | | first_and_last: [usize; 0], -LL | | } - | |_^ - | - = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) - -error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:15:1 - | -LL | / struct GenericArrayType { -LL | | field: i32, -LL | | last: [T; 0], -LL | | } - | |_^ - | - = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) - -error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:20:1 - | -LL | / #[must_use] -LL | | struct OnlyAnotherAttributeMustUse { -LL | | field: i32, -LL | | last: [usize; 0], -LL | | } - | |_^ - | - = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) - -error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:29:1 - | -LL | / struct OnlyAnotherAttributeDerive { -LL | | field: i32, -LL | | last: [usize; 0], -LL | | } - | |_^ - | - = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) - -error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:35:1 - | -LL | / struct ZeroSizedWithConst { -LL | | field: i32, -LL | | last: [usize; ZERO], -LL | | } - | |_^ - | - = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) - -error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:44:1 - | -LL | / struct ZeroSizedWithConstFunction { -LL | | field: i32, -LL | | last: [usize; compute_zero()], -LL | | } - | |_^ - | - = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) - -error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:49:1 - | -LL | struct ZeroSizedArrayWrapper([usize; 0]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) - -error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:51:1 - | -LL | struct TupleStruct(i32, [usize; 0]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) - -error: trailing zero-sized array in a struct which is not marked `#[repr(C)]` - --> $DIR/trailing_zero_sized_array_without_repr_c.rs:53:1 - | -LL | / struct LotsOfFields { -LL | | f1: u32, -LL | | f2: u32, -LL | | f3: u32, -... | -LL | | last: [usize; 0], -LL | | } - | |_^ - | - = help: consider annotating the struct definition with `#[repr(C)]` (or another `repr` attribute) - -error: aborting due to 10 previous errors - From d25b4eeefbcb6e2755b0843b0a66de0f5a744460 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 04:22:43 -0700 Subject: [PATCH 074/101] One more test --- tests/ui/trailing_zero_sized_array_without_repr.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/ui/trailing_zero_sized_array_without_repr.rs b/tests/ui/trailing_zero_sized_array_without_repr.rs index 6ac124c8b34d5..5b12a882aa79e 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr.rs @@ -167,6 +167,11 @@ struct ConstParamNonZeroDefault { last: [usize; N], } +struct TwoGenericParams { + field: i32, + last: [T; N], +} + type A = ConstParamZeroDefault; type B = ConstParamZeroDefault<0>; type C = ConstParamNoDefault<0>; From ab9fa25e82b3af4691df275f53c73bc439e84d78 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 04:59:03 -0700 Subject: [PATCH 075/101] Better testcase names --- tests/ui/trailing_zero_sized_array_without_repr.rs | 8 ++++---- tests/ui/trailing_zero_sized_array_without_repr.stderr | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ui/trailing_zero_sized_array_without_repr.rs b/tests/ui/trailing_zero_sized_array_without_repr.rs index 5b12a882aa79e..52966c64db736 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr.rs @@ -18,7 +18,7 @@ struct GenericArrayType { } #[must_use] -struct OnlyAnotherAttributeMustUse { +struct OnlyAnotherAttribute { field: i32, last: [usize; 0], } @@ -26,7 +26,7 @@ struct OnlyAnotherAttributeMustUse { // NOTE: Unfortunately the attribute isn't included in the lint output. I'm not sure how to make it // show up. #[derive(Debug)] -struct OnlyAnotherAttributeDerive { +struct OnlyADeriveAttribute { field: i32, last: [usize; 0], } @@ -102,14 +102,14 @@ struct NonZeroSizedWithConst { #[derive(Debug)] #[repr(C)] -struct OtherAttributesDerive { +struct AlsoADeriveAttribute { field: i32, last: [usize; 0], } #[must_use] #[repr(C)] -struct OtherAttributesMustUse { +struct AlsoAnotherAttribute { field: i32, last: [usize; 0], } diff --git a/tests/ui/trailing_zero_sized_array_without_repr.stderr b/tests/ui/trailing_zero_sized_array_without_repr.stderr index 54b36ec6cecb9..e5714386c8b1d 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr.stderr +++ b/tests/ui/trailing_zero_sized_array_without_repr.stderr @@ -35,7 +35,7 @@ error: trailing zero-sized array in a struct which is not marked with a `repr` a --> $DIR/trailing_zero_sized_array_without_repr.rs:20:1 | LL | / #[must_use] -LL | | struct OnlyAnotherAttributeMustUse { +LL | | struct OnlyAnotherAttribute { LL | | field: i32, LL | | last: [usize; 0], LL | | } @@ -46,7 +46,7 @@ LL | | } error: trailing zero-sized array in a struct which is not marked with a `repr` attribute --> $DIR/trailing_zero_sized_array_without_repr.rs:29:1 | -LL | / struct OnlyAnotherAttributeDerive { +LL | / struct OnlyADeriveAttribute { LL | | field: i32, LL | | last: [usize; 0], LL | | } From 3a41f226c5c89806f23ef67502ea29bedf7d9ce8 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 07:02:00 -0700 Subject: [PATCH 076/101] Exploring emitting other sorts of `span`s --- .../trailing_zero_sized_array_without_repr.rs | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs index 98f5073681d46..48feb365ed852 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{diagnostics::{span_lint_and_help, span_lint_and_then, span_lint_and_sugg}, source::{indent_of, snippet}}; use rustc_ast::Attribute; +use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::dep_graph::DepContext; @@ -50,14 +51,34 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutRepr { }; if !has_repr_attr(cx, attrs) { - span_lint_and_help( - cx, - TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, - lint_span, - "trailing zero-sized array in a struct which is not marked with a `repr` attribute", - None, - "consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute", - ); + let suggestion_span = item.span.shrink_to_lo(); + let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); + + span_lint_and_sugg(cx, TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, item.span, "trailing zero-sized array in a struct which is not marked with a `repr` attribute", "consider adding `#[repr(C)]` or another `repr` attribute", format!("#[repr(C)]\n{}", snippet(cx, item.span.shrink_to_lo().to(item.ident.span), "..")), Applicability::MaybeIncorrect); + + // span_lint_and_then( + // cx, + // TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, + // item.span, + // "trailing zero-sized array in a struct which is not marked with a `repr` attribute", + // |diag| { + // let sugg = format!("#[repr(C)]\n{}", indent); + // let sugg2 = format!("#[repr(C)]\n{}", item.ident.span); + // diag.span_suggestion(item.span, + // "consider adding `#[repr(C)]` or another `repr` attribute", + // sugg2, + // Applicability::MaybeIncorrect); + // } + // ); + + // span_lint_and_help( + // cx, + // TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, + // lint_span, + // "trailing zero-sized array in a struct which is not marked with a `repr` attribute", + // None, + // "consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute", + // ); } } } @@ -91,7 +112,8 @@ fn has_repr_attr(cx: &LateContext<'tcx>, attrs: &[Attribute]) -> bool { // NOTE: there's at least four other ways to do this but I liked this one the best. (All five agreed // on all testcases (when i wrote this comment. I added a few since then).) Happy to use another; // they're in the commit history if you want to look (or I can go find them). + let sess = cx.tcx.sess(); // are captured values in closures evaluated once or every time? attrs .iter() - .any(|attr| !rustc_attr::find_repr_attrs(cx.tcx.sess(), attr).is_empty()) + .any(|attr| !rustc_attr::find_repr_attrs(sess, attr).is_empty()) } From d8bacf078a0c0d1dd3f15e6554338805f2d7256b Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 15:33:11 -0700 Subject: [PATCH 077/101] All five `has_repr_attr` agree + are correct --- .../trailing_zero_sized_array_without_repr.rs | 115 ++++++++++-------- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs index 48feb365ed852..10088ea55a902 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs @@ -1,11 +1,15 @@ -use clippy_utils::{diagnostics::{span_lint_and_help, span_lint_and_then, span_lint_and_sugg}, source::{indent_of, snippet}}; +use clippy_utils::{ + diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}, + source::{indent_of, snippet}, +}; use rustc_ast::Attribute; use rustc_errors::Applicability; -use rustc_hir::{Item, ItemKind}; +use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::dep_graph::DepContext; -use rustc_middle::ty::Const; +use rustc_middle::ty::{self as ty_mod, Const, ReprFlags}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -38,48 +42,19 @@ declare_lint_pass!(TrailingZeroSizedArrayWithoutRepr => [TRAILING_ZERO_SIZED_ARR impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutRepr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if is_struct_with_trailing_zero_sized_array(cx, item) { - // NOTE: This is to include attributes on the definition when we print the lint. If the convention - // is to not do that with struct definitions (I'm not sure), then this isn't necessary. (note: if - // you don't get rid of this, change `has_repr_attr` to `includes_repr_attr`). - let attrs = cx.tcx.get_attrs(item.def_id.to_def_id()); - let first_attr = attrs.iter().min_by_key(|attr| attr.span.lo()); - let lint_span = if let Some(first_attr) = first_attr { - first_attr.span.to(item.span) - } else { - item.span - }; - - if !has_repr_attr(cx, attrs) { - let suggestion_span = item.span.shrink_to_lo(); - let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); - - span_lint_and_sugg(cx, TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, item.span, "trailing zero-sized array in a struct which is not marked with a `repr` attribute", "consider adding `#[repr(C)]` or another `repr` attribute", format!("#[repr(C)]\n{}", snippet(cx, item.span.shrink_to_lo().to(item.ident.span), "..")), Applicability::MaybeIncorrect); - - // span_lint_and_then( - // cx, - // TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, - // item.span, - // "trailing zero-sized array in a struct which is not marked with a `repr` attribute", - // |diag| { - // let sugg = format!("#[repr(C)]\n{}", indent); - // let sugg2 = format!("#[repr(C)]\n{}", item.ident.span); - // diag.span_suggestion(item.span, - // "consider adding `#[repr(C)]` or another `repr` attribute", - // sugg2, - // Applicability::MaybeIncorrect); - // } - // ); - - // span_lint_and_help( - // cx, - // TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, - // lint_span, - // "trailing zero-sized array in a struct which is not marked with a `repr` attribute", - // None, - // "consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute", - // ); - } + dbg!(item.ident); + if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item) { + eprintln!("consider yourself linted 😎"); + // span_lint_and_help( + // cx, + // TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, + // item.span, + // "trailing zero-sized array in a struct which is not marked with a `repr` + // attribute", + // None, + // "consider annotating the struct definition with `#[repr(C)]` or another + // `repr` attribute", + // ); } } } @@ -108,12 +83,52 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx } } -fn has_repr_attr(cx: &LateContext<'tcx>, attrs: &[Attribute]) -> bool { +fn has_repr_attr(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { // NOTE: there's at least four other ways to do this but I liked this one the best. (All five agreed // on all testcases (when i wrote this comment. I added a few since then).) Happy to use another; // they're in the commit history if you want to look (or I can go find them). - let sess = cx.tcx.sess(); // are captured values in closures evaluated once or every time? - attrs - .iter() - .any(|attr| !rustc_attr::find_repr_attrs(sess, attr).is_empty()) + + let attrs1 = cx.tcx.hir().attrs(item.hir_id()); + let attrs2 = cx.tcx.get_attrs(item.def_id.to_def_id()); + + let res11 = { + let sess = cx.tcx.sess(); // are captured values in closures evaluated once or every time? + attrs1 + .iter() + .any(|attr| !rustc_attr::find_repr_attrs(sess, attr).is_empty()) + }; + let res12 = { attrs1.iter().any(|attr| attr.has_name(sym::repr)) }; + + let res21 = { + let sess = cx.tcx.sess(); // are captured values in closures evaluated once or every time? + attrs2 + .iter() + .any(|attr| !rustc_attr::find_repr_attrs(sess, attr).is_empty()) + }; + let res22 = { attrs2.iter().any(|attr| attr.has_name(sym::repr)) }; + + let res_adt = { + let ty = cx.tcx.type_of(item.def_id.to_def_id()); + if let ty_mod::Adt(adt, _) = ty.kind() { + if adt.is_struct() { + let repr = adt.repr; + let repr_attr = ReprFlags::IS_C | ReprFlags::IS_TRANSPARENT | ReprFlags::IS_SIMD | ReprFlags::IS_LINEAR; + repr.int.is_some() || repr.align.is_some() || repr.pack.is_some() || repr.flags.intersects(repr_attr) + } else { + false + } + } else { + false + } + }; + + let all_same = (res11 && res12 && res21 && res22 && res_adt) || (!res11 && !res12 && !res21 && !res22 && !res_adt); + + + dbg!(( + (res11, res12, res21, res22, res_adt), + all_same, + )); + + res12 } From 18c863dd0ef490d8f069dba6263d7d5d6f8b3e13 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 16:53:05 -0700 Subject: [PATCH 078/101] Improve help message --- .../trailing_zero_sized_array_without_repr.rs | 87 ++++++------------- 1 file changed, 26 insertions(+), 61 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs index 10088ea55a902..0c9066fda8226 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs @@ -2,12 +2,10 @@ use clippy_utils::{ diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}, source::{indent_of, snippet}, }; -use rustc_ast::Attribute; use rustc_errors::Applicability; use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::dep_graph::DepContext; -use rustc_middle::ty::{self as ty_mod, Const, ReprFlags}; +use rustc_middle::ty::{Const, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -43,18 +41,28 @@ declare_lint_pass!(TrailingZeroSizedArrayWithoutRepr => [TRAILING_ZERO_SIZED_ARR impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutRepr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { dbg!(item.ident); - if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item) { - eprintln!("consider yourself linted 😎"); - // span_lint_and_help( - // cx, - // TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, - // item.span, - // "trailing zero-sized array in a struct which is not marked with a `repr` - // attribute", - // None, - // "consider annotating the struct definition with `#[repr(C)]` or another - // `repr` attribute", - // ); + if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) { + let help_msg = format!( + "consider annotating {} with `#[repr(C)]` or another `repr` attribute", + cx.tcx + .type_of(item.def_id) + .ty_adt_def() + .map(|adt_def| cx.tcx.def_path_str(adt_def.did)) + .unwrap_or_else( + // I don't think this will ever be the case, since we made it through + // `is_struct_with_trailing_zero_sized_array`, but I don't feel comfortable putting an `unwrap` + || "the struct definition".to_string() + ) + ); + + span_lint_and_help( + cx, + TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, + item.span, + "trailing zero-sized array in a struct which is not marked with a `repr` attribute", + None, + &help_msg, + ); } } } @@ -83,52 +91,9 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx } } -fn has_repr_attr(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { +fn has_repr_attr(cx: &LateContext<'tcx>, hir_id: HirId) -> bool { // NOTE: there's at least four other ways to do this but I liked this one the best. (All five agreed - // on all testcases (when i wrote this comment. I added a few since then).) Happy to use another; + // on all testcases.) Happy to use another; // they're in the commit history if you want to look (or I can go find them). - - let attrs1 = cx.tcx.hir().attrs(item.hir_id()); - let attrs2 = cx.tcx.get_attrs(item.def_id.to_def_id()); - - let res11 = { - let sess = cx.tcx.sess(); // are captured values in closures evaluated once or every time? - attrs1 - .iter() - .any(|attr| !rustc_attr::find_repr_attrs(sess, attr).is_empty()) - }; - let res12 = { attrs1.iter().any(|attr| attr.has_name(sym::repr)) }; - - let res21 = { - let sess = cx.tcx.sess(); // are captured values in closures evaluated once or every time? - attrs2 - .iter() - .any(|attr| !rustc_attr::find_repr_attrs(sess, attr).is_empty()) - }; - let res22 = { attrs2.iter().any(|attr| attr.has_name(sym::repr)) }; - - let res_adt = { - let ty = cx.tcx.type_of(item.def_id.to_def_id()); - if let ty_mod::Adt(adt, _) = ty.kind() { - if adt.is_struct() { - let repr = adt.repr; - let repr_attr = ReprFlags::IS_C | ReprFlags::IS_TRANSPARENT | ReprFlags::IS_SIMD | ReprFlags::IS_LINEAR; - repr.int.is_some() || repr.align.is_some() || repr.pack.is_some() || repr.flags.intersects(repr_attr) - } else { - false - } - } else { - false - } - }; - - let all_same = (res11 && res12 && res21 && res22 && res_adt) || (!res11 && !res12 && !res21 && !res22 && !res_adt); - - - dbg!(( - (res11, res12, res21, res22, res_adt), - all_same, - )); - - res12 + cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::repr)) } From 48cf9c284ac858545cdd22ef2efec9096e6c6b06 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 16:53:17 -0700 Subject: [PATCH 079/101] Don't need `rustc_attr` anymore --- clippy_lints/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1473a2f3095d0..de2ebcab66700 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -21,7 +21,6 @@ // (Currently there is no way to opt into sysroot crates without `extern crate`.) extern crate rustc_ast; extern crate rustc_ast_pretty; -extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; From d85f903c91d909534003ee2ff0e16316b20687dc Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 17:07:51 -0700 Subject: [PATCH 080/101] !: this is the commit that demonstrates the ICE --- .../trailing_zero_sized_array_without_repr.rs | 55 ++++++++----------- .../trailing_zero_sized_array_without_repr.rs | 8 ++- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs index 0c9066fda8226..ac4bbded127f6 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs @@ -1,11 +1,6 @@ -use clippy_utils::{ - diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}, - source::{indent_of, snippet}, -}; -use rustc_errors::Applicability; +use clippy_utils::{consts::miri_to_const, consts::Constant, diagnostics::span_lint_and_help}; use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{Const, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -42,27 +37,15 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutRepr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { dbg!(item.ident); if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) { - let help_msg = format!( - "consider annotating {} with `#[repr(C)]` or another `repr` attribute", - cx.tcx - .type_of(item.def_id) - .ty_adt_def() - .map(|adt_def| cx.tcx.def_path_str(adt_def.did)) - .unwrap_or_else( - // I don't think this will ever be the case, since we made it through - // `is_struct_with_trailing_zero_sized_array`, but I don't feel comfortable putting an `unwrap` - || "the struct definition".to_string() - ) - ); - - span_lint_and_help( - cx, - TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, - item.span, - "trailing zero-sized array in a struct which is not marked with a `repr` attribute", - None, - &help_msg, - ); + // span_lint_and_help( + // cx, + // TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, + // item.span, + // "trailing zero-sized array in a struct which is not marked with a `repr` attribute", + // None, + // "", + // ); + eprintln!("consider yourself linted 😎"); } } } @@ -75,11 +58,19 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx if let ItemKind::Struct(data, _) = &item.kind { if let Some(last_field) = data.fields().last() { if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind { - // Then check if that that array zero-sized - let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); - let length = Const::from_anon_const(cx.tcx, length_ldid); - let length = length.try_eval_usize(cx.tcx, cx.param_env); - length == Some(0) + let length_did = cx.tcx.hir().body_owner_def_id(length.body).to_def_id(); + let ty = cx.tcx.type_of(length_did); + let length = cx + .tcx + // ICE happens in `const_eval_poly` according to my backtrace + .const_eval_poly(length_did) + .ok() + .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); + if let Some(Constant::Int(length)) = length.and_then(miri_to_const){ + length == 0 + } else { + false + } } else { false } diff --git a/tests/ui/trailing_zero_sized_array_without_repr.rs b/tests/ui/trailing_zero_sized_array_without_repr.rs index 52966c64db736..c00a772d2feda 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr.rs @@ -1,6 +1,8 @@ #![warn(clippy::trailing_zero_sized_array_without_repr)] #![feature(const_generics_defaults)] +// ICE note: All of these are fine + // Do lint: struct RarelyUseful { @@ -23,8 +25,6 @@ struct OnlyAnotherAttribute { last: [usize; 0], } -// NOTE: Unfortunately the attribute isn't included in the lint output. I'm not sure how to make it -// show up. #[derive(Debug)] struct OnlyADeriveAttribute { field: i32, @@ -150,12 +150,16 @@ struct TupleStructReprC(i32, [usize; 0]); type NamedTuple = (i32, [usize; 0]); +// ICE note: and then this one crashes + #[rustfmt::skip] // [rustfmt#4995](https://github.com/rust-lang/rustfmt/issues/4995) struct ConstParamZeroDefault { field: i32, last: [usize; N], } +// ICE notes: presumably these as well but I'm not sure + struct ConstParamNoDefault { field: i32, last: [usize; N], From 6303d2d075131641ef325344124c71052d2bd3eb Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 17:18:07 -0700 Subject: [PATCH 081/101] Revert "!: this is the commit that demonstrates the ICE" This reverts commit d85f903c91d909534003ee2ff0e16316b20687dc. --- .../trailing_zero_sized_array_without_repr.rs | 55 +++++++++++-------- .../trailing_zero_sized_array_without_repr.rs | 8 +-- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs index ac4bbded127f6..0c9066fda8226 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs @@ -1,6 +1,11 @@ -use clippy_utils::{consts::miri_to_const, consts::Constant, diagnostics::span_lint_and_help}; +use clippy_utils::{ + diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}, + source::{indent_of, snippet}, +}; +use rustc_errors::Applicability; use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Const, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -37,15 +42,27 @@ impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutRepr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { dbg!(item.ident); if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) { - // span_lint_and_help( - // cx, - // TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, - // item.span, - // "trailing zero-sized array in a struct which is not marked with a `repr` attribute", - // None, - // "", - // ); - eprintln!("consider yourself linted 😎"); + let help_msg = format!( + "consider annotating {} with `#[repr(C)]` or another `repr` attribute", + cx.tcx + .type_of(item.def_id) + .ty_adt_def() + .map(|adt_def| cx.tcx.def_path_str(adt_def.did)) + .unwrap_or_else( + // I don't think this will ever be the case, since we made it through + // `is_struct_with_trailing_zero_sized_array`, but I don't feel comfortable putting an `unwrap` + || "the struct definition".to_string() + ) + ); + + span_lint_and_help( + cx, + TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, + item.span, + "trailing zero-sized array in a struct which is not marked with a `repr` attribute", + None, + &help_msg, + ); } } } @@ -58,19 +75,11 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx if let ItemKind::Struct(data, _) = &item.kind { if let Some(last_field) = data.fields().last() { if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind { - let length_did = cx.tcx.hir().body_owner_def_id(length.body).to_def_id(); - let ty = cx.tcx.type_of(length_did); - let length = cx - .tcx - // ICE happens in `const_eval_poly` according to my backtrace - .const_eval_poly(length_did) - .ok() - .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)); - if let Some(Constant::Int(length)) = length.and_then(miri_to_const){ - length == 0 - } else { - false - } + // Then check if that that array zero-sized + let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); + let length = Const::from_anon_const(cx.tcx, length_ldid); + let length = length.try_eval_usize(cx.tcx, cx.param_env); + length == Some(0) } else { false } diff --git a/tests/ui/trailing_zero_sized_array_without_repr.rs b/tests/ui/trailing_zero_sized_array_without_repr.rs index c00a772d2feda..52966c64db736 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr.rs @@ -1,8 +1,6 @@ #![warn(clippy::trailing_zero_sized_array_without_repr)] #![feature(const_generics_defaults)] -// ICE note: All of these are fine - // Do lint: struct RarelyUseful { @@ -25,6 +23,8 @@ struct OnlyAnotherAttribute { last: [usize; 0], } +// NOTE: Unfortunately the attribute isn't included in the lint output. I'm not sure how to make it +// show up. #[derive(Debug)] struct OnlyADeriveAttribute { field: i32, @@ -150,16 +150,12 @@ struct TupleStructReprC(i32, [usize; 0]); type NamedTuple = (i32, [usize; 0]); -// ICE note: and then this one crashes - #[rustfmt::skip] // [rustfmt#4995](https://github.com/rust-lang/rustfmt/issues/4995) struct ConstParamZeroDefault { field: i32, last: [usize; N], } -// ICE notes: presumably these as well but I'm not sure - struct ConstParamNoDefault { field: i32, last: [usize; N], From c654cc56da81e9bebc37dd6c476b40c07d297cbe Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 17:41:27 -0700 Subject: [PATCH 082/101] One more test + final tidying --- .../trailing_zero_sized_array_without_repr.rs | 55 ++++++------------- .../trailing_zero_sized_array_without_repr.rs | 8 +++ ...iling_zero_sized_array_without_repr.stderr | 44 +++++++++------ 3 files changed, 53 insertions(+), 54 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs index 0c9066fda8226..88ec471184cf7 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs @@ -1,11 +1,7 @@ -use clippy_utils::{ - diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}, - source::{indent_of, snippet}, -}; -use rustc_errors::Applicability; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{Const, TyS}; +use rustc_middle::ty::Const; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -40,54 +36,39 @@ declare_lint_pass!(TrailingZeroSizedArrayWithoutRepr => [TRAILING_ZERO_SIZED_ARR impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutRepr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - dbg!(item.ident); if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) { - let help_msg = format!( - "consider annotating {} with `#[repr(C)]` or another `repr` attribute", - cx.tcx - .type_of(item.def_id) - .ty_adt_def() - .map(|adt_def| cx.tcx.def_path_str(adt_def.did)) - .unwrap_or_else( - // I don't think this will ever be the case, since we made it through - // `is_struct_with_trailing_zero_sized_array`, but I don't feel comfortable putting an `unwrap` - || "the struct definition".to_string() - ) - ); - span_lint_and_help( cx, TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, item.span, "trailing zero-sized array in a struct which is not marked with a `repr` attribute", None, - &help_msg, + &format!( + "consider annotating `{}` with `#[repr(C)]` or another `repr` attribute", + cx.tcx.def_path_str(item.def_id.to_def_id()) + ), ); } } } fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { - // TODO: when finalized, replace with an `if_chain`. I have it like this because my rust-analyzer - // doesn't work when it's an `if_chain`. + if_chain! { + // First check if last field is an array + if let ItemKind::Struct(data, _) = &item.kind; + if let Some(last_field) = data.fields().last(); + if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind; - // First check if last field is an array - if let ItemKind::Struct(data, _) = &item.kind { - if let Some(last_field) = data.fields().last() { - if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind { - // Then check if that that array zero-sized - let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); - let length = Const::from_anon_const(cx.tcx, length_ldid); - let length = length.try_eval_usize(cx.tcx, cx.param_env); - length == Some(0) - } else { - false - } + // Then check if that that array zero-sized + let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); + let length = Const::from_anon_const(cx.tcx, length_ldid); + let length = length.try_eval_usize(cx.tcx, cx.param_env); + if let Some(length) = length; + then { + length == 0 } else { false } - } else { - false } } diff --git a/tests/ui/trailing_zero_sized_array_without_repr.rs b/tests/ui/trailing_zero_sized_array_without_repr.rs index 52966c64db736..dee7f81154000 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr.rs @@ -46,6 +46,14 @@ struct ZeroSizedWithConstFunction { last: [usize; compute_zero()], } +const fn compute_zero_from_arg(x: usize) -> usize { + x - 1 +} +struct ZeroSizedWithConstFunction2 { + field: i32, + last: [usize; compute_zero_from_arg(1)], +} + struct ZeroSizedArrayWrapper([usize; 0]); struct TupleStruct(i32, [usize; 0]); diff --git a/tests/ui/trailing_zero_sized_array_without_repr.stderr b/tests/ui/trailing_zero_sized_array_without_repr.stderr index e5714386c8b1d..93c607bbf38fd 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr.stderr +++ b/tests/ui/trailing_zero_sized_array_without_repr.stderr @@ -8,7 +8,7 @@ LL | | } | |_^ | = note: `-D clippy::trailing-zero-sized-array-without-repr` implied by `-D warnings` - = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + = help: consider annotating `RarelyUseful` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute --> $DIR/trailing_zero_sized_array_without_repr.rs:11:1 @@ -18,7 +18,7 @@ LL | | first_and_last: [usize; 0], LL | | } | |_^ | - = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + = help: consider annotating `OnlyField` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute --> $DIR/trailing_zero_sized_array_without_repr.rs:15:1 @@ -29,19 +29,18 @@ LL | | last: [T; 0], LL | | } | |_^ | - = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + = help: consider annotating `GenericArrayType` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:20:1 + --> $DIR/trailing_zero_sized_array_without_repr.rs:21:1 | -LL | / #[must_use] -LL | | struct OnlyAnotherAttribute { +LL | / struct OnlyAnotherAttribute { LL | | field: i32, LL | | last: [usize; 0], LL | | } | |_^ | - = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + = help: consider annotating `OnlyAnotherAttribute` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute --> $DIR/trailing_zero_sized_array_without_repr.rs:29:1 @@ -52,7 +51,7 @@ LL | | last: [usize; 0], LL | | } | |_^ | - = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + = help: consider annotating `OnlyADeriveAttribute` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute --> $DIR/trailing_zero_sized_array_without_repr.rs:35:1 @@ -63,7 +62,7 @@ LL | | last: [usize; ZERO], LL | | } | |_^ | - = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + = help: consider annotating `ZeroSizedWithConst` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute --> $DIR/trailing_zero_sized_array_without_repr.rs:44:1 @@ -74,26 +73,37 @@ LL | | last: [usize; compute_zero()], LL | | } | |_^ | - = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + = help: consider annotating `ZeroSizedWithConstFunction` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:49:1 + --> $DIR/trailing_zero_sized_array_without_repr.rs:52:1 + | +LL | / struct ZeroSizedWithConstFunction2 { +LL | | field: i32, +LL | | last: [usize; compute_zero_from_arg(1)], +LL | | } + | |_^ + | + = help: consider annotating `ZeroSizedWithConstFunction2` with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_zero_sized_array_without_repr.rs:57:1 | LL | struct ZeroSizedArrayWrapper([usize; 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + = help: consider annotating `ZeroSizedArrayWrapper` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:51:1 + --> $DIR/trailing_zero_sized_array_without_repr.rs:59:1 | LL | struct TupleStruct(i32, [usize; 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + = help: consider annotating `TupleStruct` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:53:1 + --> $DIR/trailing_zero_sized_array_without_repr.rs:61:1 | LL | / struct LotsOfFields { LL | | f1: u32, @@ -104,7 +114,7 @@ LL | | last: [usize; 0], LL | | } | |_^ | - = help: consider annotating the struct definition with `#[repr(C)]` or another `repr` attribute + = help: consider annotating `LotsOfFields` with `#[repr(C)]` or another `repr` attribute -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors From bc32be0fecd9a99c981323699bbd5a8e485220f0 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 18:03:00 -0700 Subject: [PATCH 083/101] Remove comment --- clippy_lints/src/trailing_zero_sized_array_without_repr.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs index 88ec471184cf7..cc1671af82d5f 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs @@ -73,8 +73,5 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx } fn has_repr_attr(cx: &LateContext<'tcx>, hir_id: HirId) -> bool { - // NOTE: there's at least four other ways to do this but I liked this one the best. (All five agreed - // on all testcases.) Happy to use another; - // they're in the commit history if you want to look (or I can go find them). cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::repr)) } From 02b1f266d6a98cd9dc430554b80971705c5de09b Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 18:20:35 -0700 Subject: [PATCH 084/101] Remove explicit lifetime --- clippy_lints/src/trailing_zero_sized_array_without_repr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs index cc1671af82d5f..5fa13605ae35b 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs @@ -72,6 +72,6 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx } } -fn has_repr_attr(cx: &LateContext<'tcx>, hir_id: HirId) -> bool { +fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::repr)) } From 4c8e8169720d54db905142a18502ab2a2dad63a0 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 18:32:00 -0700 Subject: [PATCH 085/101] Use real type in doc examples --- .../src/trailing_zero_sized_array_without_repr.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs index 5fa13605ae35b..bfe0049dfaeb2 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs @@ -15,17 +15,17 @@ declare_clippy_lint! { /// ### Example /// ```rust /// struct RarelyUseful { - /// some_field: usize, - /// last: [SomeType; 0], + /// some_field: u32, + /// last: [u32; 0], /// } /// ``` /// - /// Use instead: + /// Use instead: /// ```rust /// #[repr(C)] /// struct MoreOftenUseful { /// some_field: usize, - /// last: [SomeType; 0], + /// last: [u32; 0], /// } /// ``` pub TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, From 5283d24b38631941790082b41ae3c99c698fc346 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Mon, 18 Oct 2021 18:42:01 -0700 Subject: [PATCH 086/101] =?UTF-8?q?formatting=20=F0=9F=99=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clippy_lints/src/trailing_zero_sized_array_without_repr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs index bfe0049dfaeb2..2e579aecab028 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs +++ b/clippy_lints/src/trailing_zero_sized_array_without_repr.rs @@ -20,7 +20,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// Use instead: + /// Use instead: /// ```rust /// #[repr(C)] /// struct MoreOftenUseful { From 687f3925da6481d938be0d787808328f8a93fdee Mon Sep 17 00:00:00 2001 From: dswij Date: Tue, 19 Oct 2021 18:26:50 +0800 Subject: [PATCH 087/101] Cover `Result` for `question_mark` --- clippy_lints/src/question_mark.rs | 76 ++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index aa6d254e7a544..c1ee20c370a40 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -4,10 +4,10 @@ use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, path_to_local_id}; +use clippy_utils::{eq_expr_value, path_to_local, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultOk}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -48,16 +48,25 @@ impl QuestionMark { /// } /// ``` /// + /// ```ignore + /// if result.is_err() { + /// return result; + /// } + /// ``` + /// /// If it matches, it will suggest to use the question mark operator instead - fn check_is_none_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) { + fn check_is_none_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr); if let ExprKind::MethodCall(segment, _, args, _) = &cond.kind; - if segment.ident.name == sym!(is_none); - if Self::expression_returns_none(cx, then); if let Some(subject) = args.get(0); - if Self::is_option(cx, subject); - + if (Self::is_option(cx, subject) + && Self::expression_returns_none(cx, then) + && segment.ident.name == sym!(is_none)) + || + (Self::is_result(cx, subject) + && Self::expression_returns_unmodified_err(cx, then, subject) + && segment.ident.name == sym!(is_err)); then { let mut applicability = Applicability::MachineApplicable; let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability); @@ -95,31 +104,29 @@ impl QuestionMark { } } - fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) { + fn check_if_let_some_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr); - if Self::is_option(cx, let_expr); - if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind; - if is_lang_ctor(cx, path1, OptionSome); + if (Self::is_option(cx, let_expr) + && Self::expression_returns_none(cx, if_else) + && is_lang_ctor(cx, path1, OptionSome)) + || + (Self::is_result(cx, let_expr) + && Self::expression_returns_unmodified_err(cx, if_else, let_expr) + && is_lang_ctor(cx, path1, ResultOk)); + if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind; let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); - if let ExprKind::Block(block, None) = if_then.kind; if block.stmts.is_empty(); if let Some(trailing_expr) = &block.expr; if path_to_local_id(trailing_expr, bind_id); - - if Self::expression_returns_none(cx, if_else); then { let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); - let replacement = format!( - "{}{}?", - receiver_str, - if by_ref { ".as_ref()" } else { "" }, - ); + let replacement = format!("{}{}?", receiver_str, if by_ref { ".as_ref()" } else { "" },); span_lint_and_sugg( cx, @@ -146,6 +153,12 @@ impl QuestionMark { is_type_diagnostic_item(cx, expr_ty, sym::Option) } + fn is_result(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expression); + + is_type_diagnostic_item(cx, expr_ty, sym::Result) + } + fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { match expression.kind { ExprKind::Block(block, _) => { @@ -161,6 +174,27 @@ impl QuestionMark { } } + fn expression_returns_unmodified_err( + cx: &LateContext<'_>, + expression: &Expr<'_>, + origin_hir_id: &Expr<'_>, + ) -> bool { + match expression.kind { + ExprKind::Block(block, _) => { + if let Some(return_expression) = Self::return_expression(block) { + return Self::expression_returns_unmodified_err(cx, return_expression, origin_hir_id); + } + + false + }, + ExprKind::Ret(Some(expr)) | ExprKind::Call(expr, _) => { + Self::expression_returns_unmodified_err(cx, expr, origin_hir_id) + }, + ExprKind::Path(_) => path_to_local(expression) == path_to_local(origin_hir_id), + _ => false, + } + } + fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { // Check if last expression is a return statement. Then, return the expression if_chain! { @@ -189,7 +223,7 @@ impl QuestionMark { impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - Self::check_is_none_and_early_return_none(cx, expr); - Self::check_if_let_some_and_early_return_none(cx, expr); + Self::check_is_none_or_err_and_early_return(cx, expr); + Self::check_if_let_some_or_err_and_early_return(cx, expr); } } From 3fc99b6a3376a568adc2030e93b82ad3f99eb189 Mon Sep 17 00:00:00 2001 From: dswij Date: Tue, 19 Oct 2021 18:27:37 +0800 Subject: [PATCH 088/101] Update test for `question_mark` to cover `Result` --- tests/ui/question_mark.fixed | 17 +++++++++++++++++ tests/ui/question_mark.rs | 19 +++++++++++++++++++ tests/ui/question_mark.stderr | 16 +++++++++++++++- 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 0b5746cb52270..ccb2e5a302e91 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -104,6 +104,21 @@ fn func() -> Option { Some(0) } +fn result_func(x: Result) -> Result { + let _ = x?; + + x?; + + // No warning + let y = if let Ok(x) = x { + x + } else { + return Err("some error"); + }; + + Ok(y) +} + fn main() { some_func(Some(42)); some_func(None); @@ -123,4 +138,6 @@ fn main() { returns_something_similar_to_option(so); func(); + + let _ = result_func(Ok(42)); } diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 0f0825c933467..ca3722371f524 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -134,6 +134,23 @@ fn func() -> Option { Some(0) } +fn result_func(x: Result) -> Result { + let _ = if let Ok(x) = x { x } else { return x }; + + if x.is_err() { + return x; + } + + // No warning + let y = if let Ok(x) = x { + x + } else { + return Err("some error"); + }; + + Ok(y) +} + fn main() { some_func(Some(42)); some_func(None); @@ -153,4 +170,6 @@ fn main() { returns_something_similar_to_option(so); func(); + + let _ = result_func(Ok(42)); } diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 6f330cfa385dd..161588cb73cba 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -100,5 +100,19 @@ LL | | return None; LL | | } | |_____^ help: replace it with: `f()?;` -error: aborting due to 11 previous errors +error: this if-let-else may be rewritten with the `?` operator + --> $DIR/question_mark.rs:138:13 + | +LL | let _ = if let Ok(x) = x { x } else { return x }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:140:5 + | +LL | / if x.is_err() { +LL | | return x; +LL | | } + | |_____^ help: replace it with: `x?;` + +error: aborting due to 13 previous errors From ebf4f03f7d4c1fa53ab7da166d6bf0becabb584c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 19 Oct 2021 13:58:58 +0100 Subject: [PATCH 089/101] Remove begin_panic_fmt from clippy --- clippy_utils/src/higher.rs | 1 - clippy_utils/src/lib.rs | 1 - clippy_utils/src/paths.rs | 1 - 3 files changed, 3 deletions(-) diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index ba4d50bf74469..74cf323720cbb 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -619,7 +619,6 @@ impl PanicExpn<'tcx> { if let Some(init) = block.expr; if let ExprKind::Call(_, [format_args]) = init.kind; let expn_data = expr.span.ctxt().outer_expn_data(); - if let ExprKind::AddrOf(_, _, format_args) = format_args.kind; if let Some(format_args) = FormatArgsExpn::parse(format_args); then { Some(PanicExpn { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index c47aa9170e547..8e94d16a33a0e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1646,7 +1646,6 @@ pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool { did, &[ &paths::BEGIN_PANIC, - &paths::BEGIN_PANIC_FMT, &paths::PANIC_ANY, &paths::PANICKING_PANIC, &paths::PANICKING_PANIC_FMT, diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index e43c575602145..81aff585ded1b 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -20,7 +20,6 @@ pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"]; -pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"]; /// Preferably use the diagnostic item `sym::Borrow` where possible pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; From 7246731ad87b78b02a7074154e41a7ef1ea84296 Mon Sep 17 00:00:00 2001 From: Dmitry Borodin Date: Tue, 19 Oct 2021 20:33:39 +0200 Subject: [PATCH 090/101] made description of intellij a link by moving outside of code block. --- doc/basics.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/basics.md b/doc/basics.md index 6da90ebf5f556..57a90a924ec3c 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -93,9 +93,10 @@ cargo dev update_lints cargo dev new_lint # automatically formatting all code before each commit cargo dev setup git-hook -# (experimental) Setup Clippy to work with IntelliJ-Rust. Details here: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust +# (experimental) Setup Clippy to work with IntelliJ-Rust cargo dev setup intellij ``` +More about intellij command usage and reasons [here](../CONTRIBUTING.md#intellij-rust) ## lintcheck `cargo lintcheck` will build and run clippy on a fixed set of crates and generate a log of the results. From e88c956e1e18a99af55af00c8917f8e975d534c5 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Tue, 12 Oct 2021 19:18:22 +0200 Subject: [PATCH 091/101] avoid `eq_op` in test code --- clippy_lints/src/eq_op.rs | 11 ++++-- clippy_utils/src/lib.rs | 78 ++++++++++++++++++++++++++++++++++----- tests/compile-test.rs | 14 +++++++ tests/ui_test/eq_op.rs | 15 ++++++++ 4 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 tests/ui_test/eq_op.rs diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 51d5094e8c998..655560afd4250 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of}; +use clippy_utils::{ + ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of, is_in_test_function, +}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; @@ -81,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if macro_args.len() == 2; let (lhs, rhs) = (macro_args[0], macro_args[1]); if eq_expr_value(cx, lhs, rhs); - + if !is_in_test_function(cx.tcx, e.hir_id); then { span_lint( cx, @@ -108,7 +110,10 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) { return; } - if is_useless_with_eq_exprs(op.node.into()) && eq_expr_value(cx, left, right) { + if is_useless_with_eq_exprs(op.node.into()) + && eq_expr_value(cx, left, right) + && !is_in_test_function(cx.tcx, e.hir_id) + { span_lint( cx, EQ_OP, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 09eee78f0d1ff..c540e1ebc9cc7 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -69,11 +69,13 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, - ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, Param, Pat, - PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, + def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs, + HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, + Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, + UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -2064,16 +2066,72 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { false } -/// Checks whether item either has `test` attribute applied, or -/// is a module with `test` in its name. -pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool { - if let Some(def_id) = tcx.hir().opt_local_def_id(item.hir_id()) { - if tcx.has_attr(def_id.to_def_id(), sym::test) { - return true; +struct VisitConstTestStruct<'tcx> { + tcx: TyCtxt<'tcx>, + names: Vec, + found: bool, +} +impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'hir> { + fn visit_item(&mut self, item: &Item<'_>) { + if let ItemKind::Const(ty, _body) = item.kind { + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + // We could also check for the type name `test::TestDescAndFn` + // and the `#[rustc_test_marker]` attribute? + if let Res::Def(DefKind::Struct, _) = path.res { + let has_test_marker = self + .tcx + .hir() + .attrs(item.hir_id()) + .iter() + .any(|a| a.has_name(sym::rustc_test_marker)); + if has_test_marker && self.names.contains(&item.ident.name) { + self.found = true; + } + } + } } } + fn visit_trait_item(&mut self, _: &TraitItem<'_>) {} + fn visit_impl_item(&mut self, _: &ImplItem<'_>) {} + fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {} +} - matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests") +/// Checks if the function containing the given `HirId` is a `#[test]` function +/// +/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`. +pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { + let names: Vec<_> = tcx + .hir() + .parent_iter(id) + // Since you can nest functions we need to collect all until we leave + // function scope + .filter_map(|(_id, node)| { + if let Node::Item(item) = node { + if let ItemKind::Fn(_, _, _) = item.kind { + return Some(item.ident.name); + } + } + None + }) + .collect(); + let parent_mod = tcx.parent_module(id); + let mut vis = VisitConstTestStruct { + tcx, + names, + found: false, + }; + tcx.hir().visit_item_likes_in_module(parent_mod, &mut vis); + vis.found +} + +/// Checks whether item either has `test` attribute appelied, or +/// is a module with `test` in its name. +/// +/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`. +pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool { + is_in_test_function(tcx, item.hir_id()) + || matches!(item.kind, ItemKind::Mod(..)) + && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests") } macro_rules! op_utils { diff --git a/tests/compile-test.rs b/tests/compile-test.rs index e8b1640c8693e..c15835ef29956 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -149,6 +149,19 @@ fn run_ui(cfg: &mut compiletest::Config) { compiletest::run_tests(cfg); } +fn run_ui_test(cfg: &mut compiletest::Config) { + cfg.mode = TestMode::Ui; + cfg.src_base = Path::new("tests").join("ui_test"); + let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap()); + let rustcflags = cfg.target_rustcflags.get_or_insert_with(Default::default); + let len = rustcflags.len(); + rustcflags.push_str(" --test"); + compiletest::run_tests(cfg); + if let Some(ref mut flags) = &mut cfg.target_rustcflags { + flags.truncate(len); + } +} + fn run_internal_tests(cfg: &mut compiletest::Config) { // only run internal tests with the internal-tests feature if !RUN_INTERNAL_TESTS { @@ -312,6 +325,7 @@ fn compile_test() { prepare_env(); let mut config = default_config(); run_ui(&mut config); + run_ui_test(&mut config); run_ui_toml(&mut config); run_ui_cargo(&mut config); run_internal_tests(&mut config); diff --git a/tests/ui_test/eq_op.rs b/tests/ui_test/eq_op.rs new file mode 100644 index 0000000000000..f2f5f1e588ed4 --- /dev/null +++ b/tests/ui_test/eq_op.rs @@ -0,0 +1,15 @@ +#[warn(clippy::eq_op)] +#[test] +fn eq_op_shouldnt_trigger_in_tests() { + let a = 1; + let result = a + 1 == 1 + a; + assert!(result); +} + +#[test] +fn eq_op_macros_shouldnt_trigger_in_tests() { + let a = 1; + let b = 2; + assert_eq!(a, a); + assert_eq!(a + b, b + a); +} From 60da4c9cb683a41eca4707ab7a533c5759b52485 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Tue, 19 Oct 2021 14:23:55 -0700 Subject: [PATCH 092/101] remove resolved note --- tests/ui/trailing_zero_sized_array_without_repr.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/ui/trailing_zero_sized_array_without_repr.rs b/tests/ui/trailing_zero_sized_array_without_repr.rs index dee7f81154000..bfa3af0bbc32c 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr.rs +++ b/tests/ui/trailing_zero_sized_array_without_repr.rs @@ -23,8 +23,6 @@ struct OnlyAnotherAttribute { last: [usize; 0], } -// NOTE: Unfortunately the attribute isn't included in the lint output. I'm not sure how to make it -// show up. #[derive(Debug)] struct OnlyADeriveAttribute { field: i32, From 0f9f591e30074bf5e31e4ca279280425685f33ff Mon Sep 17 00:00:00 2001 From: Nathaniel Hamovitz <18648574+nhamovitz@users.noreply.github.com> Date: Tue, 19 Oct 2021 14:33:43 -0700 Subject: [PATCH 093/101] Rename lint --- CHANGELOG.md | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_nursery.rs | 2 +- clippy_lints/src/lib.rs | 4 ++-- ...ithout_repr.rs => trailing_empty_array.rs} | 8 +++---- ...ithout_repr.rs => trailing_empty_array.rs} | 2 +- ...epr.stderr => trailing_empty_array.stderr} | 24 +++++++++---------- 7 files changed, 22 insertions(+), 22 deletions(-) rename clippy_lints/src/{trailing_zero_sized_array_without_repr.rs => trailing_empty_array.rs} (90%) rename tests/ui/{trailing_zero_sized_array_without_repr.rs => trailing_empty_array.rs} (98%) rename tests/ui/{trailing_zero_sized_array_without_repr.stderr => trailing_empty_array.stderr} (82%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cf19a6f0f169..49499324ea0fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3021,7 +3021,7 @@ Released 2018-09-13 [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines [`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg -[`trailing_zero_sized_array_without_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_zero_sized_array_without_repr +[`trailing_empty_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_empty_array [`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds [`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str [`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index af0629beb36d8..37d8d7f422fdc 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -443,7 +443,7 @@ store.register_lints(&[ temporary_assignment::TEMPORARY_ASSIGNMENT, to_digit_is_some::TO_DIGIT_IS_SOME, to_string_in_display::TO_STRING_IN_DISPLAY, - trailing_zero_sized_array_without_repr::TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, + trailing_empty_array::TRAILING_EMPTY_ARRAY, trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, trait_bounds::TYPE_REPETITION_IN_BOUNDS, transmute::CROSSPOINTER_TRANSMUTE, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index eab0e5b90f11c..1e54482a8dafd 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -25,7 +25,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(regex::TRIVIAL_REGEX), LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), - LintId::of(trailing_zero_sized_array_without_repr::TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR), + LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), ]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index de2ebcab66700..58e4c061892d6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -355,7 +355,7 @@ mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; mod to_string_in_display; -mod trailing_zero_sized_array_without_repr; +mod trailing_empty_array; mod trait_bounds; mod transmute; mod transmuting_null; @@ -778,7 +778,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default())); 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_zero_sized_array_without_repr::TrailingZeroSizedArrayWithoutRepr)); + store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray)); } diff --git a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs b/clippy_lints/src/trailing_empty_array.rs similarity index 90% rename from clippy_lints/src/trailing_zero_sized_array_without_repr.rs rename to clippy_lints/src/trailing_empty_array.rs index 2e579aecab028..c216a1f81ea54 100644 --- a/clippy_lints/src/trailing_zero_sized_array_without_repr.rs +++ b/clippy_lints/src/trailing_empty_array.rs @@ -28,18 +28,18 @@ declare_clippy_lint! { /// last: [u32; 0], /// } /// ``` - pub TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, + pub TRAILING_EMPTY_ARRAY, nursery, "struct with a trailing zero-sized array but without `#[repr(C)]` or another `repr` attribute" } -declare_lint_pass!(TrailingZeroSizedArrayWithoutRepr => [TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR]); +declare_lint_pass!(TrailingEmptyArray => [TRAILING_EMPTY_ARRAY]); -impl<'tcx> LateLintPass<'tcx> for TrailingZeroSizedArrayWithoutRepr { +impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) { span_lint_and_help( cx, - TRAILING_ZERO_SIZED_ARRAY_WITHOUT_REPR, + TRAILING_EMPTY_ARRAY, item.span, "trailing zero-sized array in a struct which is not marked with a `repr` attribute", None, diff --git a/tests/ui/trailing_zero_sized_array_without_repr.rs b/tests/ui/trailing_empty_array.rs similarity index 98% rename from tests/ui/trailing_zero_sized_array_without_repr.rs rename to tests/ui/trailing_empty_array.rs index bfa3af0bbc32c..501c9eb7651f8 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr.rs +++ b/tests/ui/trailing_empty_array.rs @@ -1,4 +1,4 @@ -#![warn(clippy::trailing_zero_sized_array_without_repr)] +#![warn(clippy::trailing_empty_array)] #![feature(const_generics_defaults)] // Do lint: diff --git a/tests/ui/trailing_zero_sized_array_without_repr.stderr b/tests/ui/trailing_empty_array.stderr similarity index 82% rename from tests/ui/trailing_zero_sized_array_without_repr.stderr rename to tests/ui/trailing_empty_array.stderr index 93c607bbf38fd..d88aa0504b537 100644 --- a/tests/ui/trailing_zero_sized_array_without_repr.stderr +++ b/tests/ui/trailing_empty_array.stderr @@ -1,5 +1,5 @@ error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:6:1 + --> $DIR/trailing_empty_array.rs:6:1 | LL | / struct RarelyUseful { LL | | field: i32, @@ -7,11 +7,11 @@ LL | | last: [usize; 0], LL | | } | |_^ | - = note: `-D clippy::trailing-zero-sized-array-without-repr` implied by `-D warnings` + = note: `-D clippy::trailing-empty-array` implied by `-D warnings` = help: consider annotating `RarelyUseful` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:11:1 + --> $DIR/trailing_empty_array.rs:11:1 | LL | / struct OnlyField { LL | | first_and_last: [usize; 0], @@ -21,7 +21,7 @@ LL | | } = help: consider annotating `OnlyField` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:15:1 + --> $DIR/trailing_empty_array.rs:15:1 | LL | / struct GenericArrayType { LL | | field: i32, @@ -32,7 +32,7 @@ LL | | } = help: consider annotating `GenericArrayType` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:21:1 + --> $DIR/trailing_empty_array.rs:21:1 | LL | / struct OnlyAnotherAttribute { LL | | field: i32, @@ -43,7 +43,7 @@ LL | | } = help: consider annotating `OnlyAnotherAttribute` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:29:1 + --> $DIR/trailing_empty_array.rs:27:1 | LL | / struct OnlyADeriveAttribute { LL | | field: i32, @@ -54,7 +54,7 @@ LL | | } = help: consider annotating `OnlyADeriveAttribute` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:35:1 + --> $DIR/trailing_empty_array.rs:33:1 | LL | / struct ZeroSizedWithConst { LL | | field: i32, @@ -65,7 +65,7 @@ LL | | } = help: consider annotating `ZeroSizedWithConst` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:44:1 + --> $DIR/trailing_empty_array.rs:42:1 | LL | / struct ZeroSizedWithConstFunction { LL | | field: i32, @@ -76,7 +76,7 @@ LL | | } = help: consider annotating `ZeroSizedWithConstFunction` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:52:1 + --> $DIR/trailing_empty_array.rs:50:1 | LL | / struct ZeroSizedWithConstFunction2 { LL | | field: i32, @@ -87,7 +87,7 @@ LL | | } = help: consider annotating `ZeroSizedWithConstFunction2` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:57:1 + --> $DIR/trailing_empty_array.rs:55:1 | LL | struct ZeroSizedArrayWrapper([usize; 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -95,7 +95,7 @@ LL | struct ZeroSizedArrayWrapper([usize; 0]); = help: consider annotating `ZeroSizedArrayWrapper` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:59:1 + --> $DIR/trailing_empty_array.rs:57:1 | LL | struct TupleStruct(i32, [usize; 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL | struct TupleStruct(i32, [usize; 0]); = help: consider annotating `TupleStruct` with `#[repr(C)]` or another `repr` attribute error: trailing zero-sized array in a struct which is not marked with a `repr` attribute - --> $DIR/trailing_zero_sized_array_without_repr.rs:61:1 + --> $DIR/trailing_empty_array.rs:59:1 | LL | / struct LotsOfFields { LL | | f1: u32, From 6b22bba902e873a9cceb3f5649d10a195699267d Mon Sep 17 00:00:00 2001 From: F3real Date: Mon, 4 Oct 2021 23:49:53 +0200 Subject: [PATCH 094/101] Lint on underscore variable assignment Fix tests after no_effect update Add a drop testcase Don't lint _ variables in macro expansion Address review comments and update tests Don't shadow unnecessary operation lint if no_effect is allowed Revert shadowing change and remove no_effect allows Update clippy_lints/src/no_effect.rs Co-authored-by: Takayuki Nakata Update clippy_lints/src/no_effect.rs Co-authored-by: Takayuki Nakata Address review comments --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 1 + clippy_lints/src/no_effect.rs | 179 ++++++++++++++-------- tests/ui/cfg_attr_rustfmt.fixed | 2 +- tests/ui/cfg_attr_rustfmt.rs | 2 +- tests/ui/no_effect.rs | 8 +- tests/ui/no_effect.stderr | 28 +++- tests/ui/option_if_let_else.fixed | 3 +- tests/ui/option_if_let_else.rs | 3 +- tests/ui/option_if_let_else.stderr | 28 ++-- 11 files changed, 170 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1e837d363a26..b7a7a421532b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2899,6 +2899,7 @@ Released 2018-09-13 [`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self [`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect +[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 7d81fafe4c93d..e5fe22ad39447 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -362,6 +362,7 @@ store.register_lints(&[ neg_multiply::NEG_MULTIPLY, new_without_default::NEW_WITHOUT_DEFAULT, no_effect::NO_EFFECT, + no_effect::NO_EFFECT_UNDERSCORE_BINDING, no_effect::UNNECESSARY_OPERATION, non_copy_const::BORROW_INTERIOR_MUTABLE_CONST, non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST, diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 6533b94e82bd5..268349d284811 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -72,6 +72,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(needless_continue::NEEDLESS_CONTINUE), LintId::of(needless_for_each::NEEDLESS_FOR_EACH), LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), + LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING), LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(non_expressive_names::SIMILAR_NAMES), LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index c5a5cde4b110f..6dae8f320436f 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::is_lint_allowed; use clippy_utils::source::snippet_opt; use clippy_utils::ty::has_drop; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource}; +use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::ops::Deref; @@ -13,7 +14,7 @@ declare_clippy_lint! { /// Checks for statements which have no effect. /// /// ### Why is this bad? - /// Similar to dead code, these statements are actually + /// Unlike dead code, these statements are actually /// executed. However, as they have no effect, all they do is make the code less /// readable. /// @@ -26,6 +27,28 @@ declare_clippy_lint! { "statements with no effect" } +declare_clippy_lint! { + /// ### What it does + /// Checks for binding to underscore prefixed variable without side-effects. + /// + /// ### Why is this bad? + /// Unlike dead code, these bindings are actually + /// executed. However, as they have no effect and shouldn't be used further on, all they + /// do is make the code less readable. + /// + /// ### Known problems + /// Further usage of this variable is not checked, which can lead to false positives if it is + /// used later in the code. + /// + /// ### Example + /// ```rust,ignore + /// let _i_serve_no_purpose = 1; + /// ``` + pub NO_EFFECT_UNDERSCORE_BINDING, + pedantic, + "binding to `_` prefixed variable with no side-effect" +} + declare_clippy_lint! { /// ### What it does /// Checks for expression statements that can be reduced to a @@ -44,6 +67,46 @@ declare_clippy_lint! { "outer expressions with no effect" } +declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]); + +impl<'tcx> LateLintPass<'tcx> for NoEffect { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if check_no_effect(cx, stmt) { + return; + } + check_unnecessary_operation(cx, stmt); + } +} + +fn check_no_effect(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> bool { + if let StmtKind::Semi(expr) = stmt.kind { + if has_no_effect(cx, expr) { + span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); + return true; + } + } else if let StmtKind::Local(local) = stmt.kind { + if_chain! { + if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id); + if let Some(init) = local.init; + if !local.pat.span.from_expansion(); + if has_no_effect(cx, init); + if let PatKind::Binding(_, _, ident, _) = local.pat.kind; + if ident.name.to_ident_string().starts_with('_'); + then { + span_lint_hir( + cx, + NO_EFFECT_UNDERSCORE_BINDING, + init.hir_id, + stmt.span, + "binding to `_` prefixed variable with no side-effect" + ); + return true; + } + } + } + false +} + fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if expr.span.from_expansion() { return false; @@ -88,71 +151,59 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } -declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION]); - -impl<'tcx> LateLintPass<'tcx> for NoEffect { - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Semi(expr) = stmt.kind { - if has_no_effect(cx, expr) { - span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); - } else if let Some(reduced) = reduce_expression(cx, expr) { - for e in &reduced { - if e.span.from_expansion() { - return; - } - } - if let ExprKind::Index(..) = &expr.kind { - let snippet; - if_chain! { - if let Some(arr) = snippet_opt(cx, reduced[0].span); - if let Some(func) = snippet_opt(cx, reduced[1].span); - then { - snippet = format!("assert!({}.len() > {});", &arr, &func); - } else { - return; - } - } - span_lint_hir_and_then( - cx, - UNNECESSARY_OPERATION, - expr.hir_id, - stmt.span, - "unnecessary operation", - |diag| { - diag.span_suggestion( - stmt.span, - "statement can be written as", - snippet, - Applicability::MaybeIncorrect, - ); - }, - ); +fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if_chain! { + if let StmtKind::Semi(expr) = stmt.kind; + if let Some(reduced) = reduce_expression(cx, expr); + if !&reduced.iter().any(|e| e.span.from_expansion()); + then { + if let ExprKind::Index(..) = &expr.kind { + let snippet; + if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) { + snippet = format!("assert!({}.len() > {});", &arr, &func); } else { - let mut snippet = String::new(); - for e in reduced { - if let Some(snip) = snippet_opt(cx, e.span) { - snippet.push_str(&snip); - snippet.push(';'); - } else { - return; - } + return; + } + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be written as", + snippet, + Applicability::MaybeIncorrect, + ); + }, + ); + } else { + let mut snippet = String::new(); + for e in reduced { + if let Some(snip) = snippet_opt(cx, e.span) { + snippet.push_str(&snip); + snippet.push(';'); + } else { + return; } - span_lint_hir_and_then( - cx, - UNNECESSARY_OPERATION, - expr.hir_id, - stmt.span, - "unnecessary operation", - |diag| { - diag.span_suggestion( - stmt.span, - "statement can be reduced to", - snippet, - Applicability::MachineApplicable, - ); - }, - ); } + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be reduced to", + snippet, + Applicability::MachineApplicable, + ); + }, + ); } } } diff --git a/tests/ui/cfg_attr_rustfmt.fixed b/tests/ui/cfg_attr_rustfmt.fixed index 4e583a25b94c2..061a4ab9b2ef8 100644 --- a/tests/ui/cfg_attr_rustfmt.fixed +++ b/tests/ui/cfg_attr_rustfmt.fixed @@ -1,7 +1,7 @@ // run-rustfix #![feature(stmt_expr_attributes)] -#![allow(unused, clippy::no_effect)] +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] // This doesn't get linted, see known problems diff --git a/tests/ui/cfg_attr_rustfmt.rs b/tests/ui/cfg_attr_rustfmt.rs index 9c0fcf6fb454c..035169fab85be 100644 --- a/tests/ui/cfg_attr_rustfmt.rs +++ b/tests/ui/cfg_attr_rustfmt.rs @@ -1,7 +1,7 @@ // run-rustfix #![feature(stmt_expr_attributes)] -#![allow(unused, clippy::no_effect)] +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] // This doesn't get linted, see known problems diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index 7ec845adfaacf..7bcc4cad0d363 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -1,5 +1,5 @@ #![feature(box_syntax)] -#![warn(clippy::no_effect)] +#![warn(clippy::no_effect_underscore_binding)] #![allow(dead_code)] #![allow(path_statements)] #![allow(clippy::deref_addrof)] @@ -90,6 +90,10 @@ fn main() { || x += 5; let s: String = "foo".into(); FooString { s: s }; + let _unused = 1; + let _penguin = || println!("Some helpful closure"); + let _duck = Struct { field: 0 }; + let _cat = [2, 4, 6, 8][2]; #[allow(clippy::no_effect)] 0; @@ -97,6 +101,8 @@ fn main() { // Do not warn get_number(); unsafe { unsafe_fn() }; + let _used = get_struct(); + let _x = vec![1]; DropUnit; DropStruct { field: 0 }; DropTuple(0); diff --git a/tests/ui/no_effect.stderr b/tests/ui/no_effect.stderr index 6b24675ac2d42..a5dbc9fef455a 100644 --- a/tests/ui/no_effect.stderr +++ b/tests/ui/no_effect.stderr @@ -156,5 +156,31 @@ error: statement with no effect LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 26 previous errors +error: binding to `_` prefixed variable with no side-effect + --> $DIR/no_effect.rs:93:5 + | +LL | let _unused = 1; + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings` + +error: binding to `_` prefixed variable with no side-effect + --> $DIR/no_effect.rs:94:5 + | +LL | let _penguin = || println!("Some helpful closure"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: binding to `_` prefixed variable with no side-effect + --> $DIR/no_effect.rs:95:5 + | +LL | let _duck = Struct { field: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: binding to `_` prefixed variable with no side-effect + --> $DIR/no_effect.rs:96:5 + | +LL | let _cat = [2, 4, 6, 8][2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 30 previous errors diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index a3ebe5d070384..4077f1920a383 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -1,8 +1,7 @@ // edition:2018 // run-rustfix #![warn(clippy::option_if_let_else)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::ref_option_ref, clippy::equatable_if_let)] +#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)] 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 b11df3db60f57..2f414e129d5a7 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -1,8 +1,7 @@ // edition:2018 // run-rustfix #![warn(clippy::option_if_let_else)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::ref_option_ref, clippy::equatable_if_let)] +#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)] 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 ed748ee8b39e4..803d941c36df8 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:8:5 + --> $DIR/option_if_let_else.rs:7: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:26:13 + --> $DIR/option_if_let_else.rs:25: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:27:13 + --> $DIR/option_if_let_else.rs:26: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:28:13 + --> $DIR/option_if_let_else.rs:27: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:34:13 + --> $DIR/option_if_let_else.rs:33: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:35:13 + --> $DIR/option_if_let_else.rs:34: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:41:13 + --> $DIR/option_if_let_else.rs:40: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:50:5 + --> $DIR/option_if_let_else.rs:49: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:63:13 + --> $DIR/option_if_let_else.rs:62: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:72:13 + --> $DIR/option_if_let_else.rs:71:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -143,13 +143,13 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:101:13 + --> $DIR/option_if_let_else.rs:100: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:110:13 + --> $DIR/option_if_let_else.rs:109:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -171,13 +171,13 @@ LL ~ }); | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:138:13 + --> $DIR/option_if_let_else.rs:137:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or_else(|| s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:142:13 + --> $DIR/option_if_let_else.rs:141:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ From 083a4546f6a93be70da2538bf9bd73a0b88c8236 Mon Sep 17 00:00:00 2001 From: dswij Date: Wed, 20 Oct 2021 13:41:44 +0800 Subject: [PATCH 095/101] Small refactor on `question_mark` condition checks --- clippy_lints/src/question_mark.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index c1ee20c370a40..4d616e26bfc1d 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -60,13 +60,8 @@ impl QuestionMark { if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr); if let ExprKind::MethodCall(segment, _, args, _) = &cond.kind; if let Some(subject) = args.get(0); - if (Self::is_option(cx, subject) - && Self::expression_returns_none(cx, then) - && segment.ident.name == sym!(is_none)) - || - (Self::is_result(cx, subject) - && Self::expression_returns_unmodified_err(cx, then, subject) - && segment.ident.name == sym!(is_err)); + if (Self::option_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_none)) || + (Self::result_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_err)); then { let mut applicability = Applicability::MachineApplicable; let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability); @@ -109,13 +104,8 @@ impl QuestionMark { if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr); if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind; - if (Self::is_option(cx, let_expr) - && Self::expression_returns_none(cx, if_else) - && is_lang_ctor(cx, path1, OptionSome)) - || - (Self::is_result(cx, let_expr) - && Self::expression_returns_unmodified_err(cx, if_else, let_expr) - && is_lang_ctor(cx, path1, ResultOk)); + if (Self::option_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, OptionSome)) || + (Self::result_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, ResultOk)); if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind; let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); @@ -141,6 +131,14 @@ impl QuestionMark { } } + fn result_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool { + Self::is_result(cx, expr) && Self::expression_returns_unmodified_err(cx, nested_expr, expr) + } + + fn option_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool { + Self::is_option(cx, expr) && Self::expression_returns_none(cx, nested_expr) + } + fn moves_by_default(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expression); From 9cf68e40fe37106e22859b67fb204145467162d5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 20 Oct 2021 13:46:12 +0200 Subject: [PATCH 096/101] Fix doc heading of TRANSMUTE_NUM_TO_BYTES --- clippy_lints/src/transmute/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 374999473a49a..e6acf1a94c929 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -263,7 +263,7 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// # What it does + /// ### What it does /// Checks for transmutes from a number to an array of `u8` /// /// ### Why this is bad? From 3630afb57f5c98c83210adde07a92f6ebdce7fc7 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 14 Oct 2021 12:16:35 +0200 Subject: [PATCH 097/101] Do not lint if any parent has hidden attribute --- clippy_lints/src/doc.rs | 12 ++++++++++++ tests/ui/doc_unsafe.rs | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 9840affbf6fd8..5511c3ea9b688 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,3 +1,4 @@ +use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note}; use clippy_utils::source::first_line_of_span; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; @@ -297,6 +298,17 @@ fn lint_for_missing_headers<'tcx>( if !cx.access_levels.is_exported(def_id) { return; // Private functions do not require doc comments } + + // do not lint if any parent has `#[doc(hidden)]` attribute (#7347) + if cx + .tcx + .hir() + .parent_iter(cx.tcx.hir().local_def_id_to_hir_id(def_id)) + .any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id))) + { + return; + } + if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe { span_lint( cx, diff --git a/tests/ui/doc_unsafe.rs b/tests/ui/doc_unsafe.rs index 8f823f1672ba2..03bb30f9083ac 100644 --- a/tests/ui/doc_unsafe.rs +++ b/tests/ui/doc_unsafe.rs @@ -115,3 +115,13 @@ fn main() { drive(); } } + +// do not lint if any parent has `#[doc(hidden)]` attribute +// see #7347 +#[doc(hidden)] +pub mod __macro { + pub struct T; + impl T { + pub unsafe fn f() {} + } +} From abb7ae9a79cdeeb6a892f19120a87ed4dba77b17 Mon Sep 17 00:00:00 2001 From: surechen Date: Thu, 21 Oct 2021 14:33:43 +0800 Subject: [PATCH 098/101] Fix typo for INVALID_NULL_PTR_USAGE and MISSING_INLINE_IN_PUBLIC_ITEMS. --- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/ptr.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 667cdd8302528..b593c747498e3 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -8,7 +8,7 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// it lints if an exported function, method, trait method with default impl, + /// It lints if an exported function, method, trait method with default impl, /// or trait method impl is not `#[inline]`. /// /// ### Why is this bad? diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index d180d6f922710..92a4801a8468a 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -139,6 +139,7 @@ declare_clippy_lint! { /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); } /// ``` /// + /// ```ignore /// // Good /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); } /// ``` From 122233091ad36e010796cb9a9b3b0836b0d16f8e Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 21 Oct 2021 12:19:32 +0200 Subject: [PATCH 099/101] Bump Clippy Version -> 0.1.58 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ba3ed3053ac74..ed7fb1440139f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.57" +version = "0.1.58" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 7900dc6d04141..aaf9ac83d4900 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.57" +version = "0.1.58" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index e7fca3ae5d401..d99a3d9359e1f 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.57" +version = "0.1.58" edition = "2021" publish = false From 8e48333bf19218fef59cf1e998ac3704033fc5dd Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 21 Oct 2021 12:19:46 +0200 Subject: [PATCH 100/101] Bump nightly version -> 2021-10-21 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index f98819303e682..67eaf286004f9 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-10-07" +channel = "nightly-2021-10-21" components = ["llvm-tools-preview", "rustc-dev", "rust-src"] From 8d5f69de3bdfe8c19352e086e237d47811e923b6 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 21 Oct 2021 13:13:17 +0200 Subject: [PATCH 101/101] Update Cargo.lock --- Cargo.lock | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8385f1a18e5cd..1a16789d15c45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -555,7 +555,7 @@ dependencies = [ [[package]] name = "clippy" -version = "0.1.57" +version = "0.1.58" dependencies = [ "cargo_metadata 0.12.0", "clippy_lints", @@ -582,6 +582,7 @@ version = "0.0.1" dependencies = [ "bytecount", "clap", + "indoc", "itertools 0.10.1", "opener", "regex", @@ -591,7 +592,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.57" +version = "0.1.58" dependencies = [ "cargo_metadata 0.12.0", "clippy_utils", @@ -612,7 +613,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.57" +version = "0.1.58" dependencies = [ "if_chain", "rustc-semver", @@ -1665,6 +1666,15 @@ dependencies = [ "serde", ] +[[package]] +name = "indoc" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136" +dependencies = [ + "unindent", +] + [[package]] name = "installer" version = "0.0.0" @@ -5593,6 +5603,12 @@ dependencies = [ "diff", ] +[[package]] +name = "unindent" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" + [[package]] name = "unstable-book-gen" version = "0.1.0"